diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..ae6aaa8 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,7 @@ +# Ensure all files are checked into the repo with LF line endings and checked +# out with LFs. +* text=auto eol=lf + +# Except these files will always be checked out with CRLFs (regardless of the +# OS they were checked out on). +**/*.ans text eol=crlf diff --git a/.github/workflows/mdbook.yml b/.github/workflows/mdbook.yml new file mode 100644 index 0000000..37ec17b --- /dev/null +++ b/.github/workflows/mdbook.yml @@ -0,0 +1,61 @@ +## Sample workflow for building and deploying a mdBook site to GitHub Pages +## +## To get started with mdBook see: https://rust-lang.github.io/mdBook/index.html +## +#name: Deploy mdBook site to Pages +# +#on: +# # Runs on pushes targeting the default branch +# push: +# branches: ["main"] +# +# # Allows you to run this workflow manually from the Actions tab +# workflow_dispatch: +# +## Sets permissions of the GITHUB_TOKEN to allow deployment to GitHub Pages +#permissions: +# contents: read +# pages: write +# id-token: write +# +## Allow only one concurrent deployment, skipping runs queued between the run in-progress and latest queued. +## However, do NOT cancel in-progress runs as we want to allow these production deployments to complete. +#concurrency: +# group: "pages" +# cancel-in-progress: false +# +#jobs: +# # Build job +# build: +# runs-on: ubuntu-latest +# env: +# MDBOOK_VERSION: 0.4.21 +# steps: +# - uses: actions/checkout@v3 +# - name: Install mdBook +# run: | +# curl --proto '=https' --tlsv1.2 https://sh.rustup.rs -sSf -y | sh +# rustup update +# cargo install --version ${MDBOOK_VERSION} mdbook +# - name: Setup Pages +# id: pages +# uses: actions/configure-pages@v3 +# - name: Build with mdBook +# run: mdbook build +# - name: Upload artifact +# uses: actions/upload-pages-artifact@v2 +# with: +# path: ./book +# +# # Deployment job +# deploy: +# environment: +# name: github-pages +# url: ${{ steps.deployment.outputs.page_url }} +# runs-on: ubuntu-latest +# needs: build +# steps: +# - name: Deploy to GitHub Pages +# id: deployment +# uses: actions/deploy-pages@v2 +# \ No newline at end of file diff --git a/.gitignore b/.gitignore index 78329d4..93ae194 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,4 @@ .DS_Store -.vscode *.exe *.run *.command diff --git a/.vscode/extensions.json b/.vscode/extensions.json new file mode 100644 index 0000000..0191a0c --- /dev/null +++ b/.vscode/extensions.json @@ -0,0 +1,15 @@ +{ + "recommendations": [ + "brandonfowler.exe-runner", + "bladnman.auto-align", + "file-icons.file-icons", + "github.vscode-pull-request-github", + "ms-vscode.hexeditor", + "ionicabizau.ms-dos-editor-theme", + "ziyasal.vscode-open-in-github", + "qb64-official.qb64", + "benrogerswpg.websearchengine", + "drmerfy.overtype", + "forbeslindesay.forbeslindesay-taskrunner" + ] +} \ No newline at end of file diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 0000000..9e26dfe --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1 @@ +{} \ No newline at end of file diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..9e26dfe --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1 @@ +{} \ No newline at end of file diff --git a/.vscode/tasks.json b/.vscode/tasks.json new file mode 100644 index 0000000..796e35f --- /dev/null +++ b/.vscode/tasks.json @@ -0,0 +1,496 @@ +{ + "version": "2.0.0", + "inputs": [ + { + "id": "QB64PEwiki", + "type": "command", + "command": "simpleBrowser.show", + "args": [ + "https://qb64phoenix.com/qb64wiki" + ] + }, + { + "id": "QB64PEforums", + "type": "command", + "command": "simpleBrowser.show", + "args": [ + "https://qb64phoenix.com" + ] + } + ], + "tasks": [ + { + "label": "HELP: QB64PE Wiki", + "command": "${input:QB64PEwiki}" + }, + { + "label": "HELP: QB64PE Forums", + "command": "${input:QB64PEforums}" + }, + { + "label": "HELP: InForm-PE Wiki", + "command": "OpenWebPage", + "type": "shell", + "linux": { + "command": "setsid xdg-open https://github.com/a740g/InForm-PE/wiki", + }, + "osx": { + "command": "open https://github.com/a740g/InForm-PE/wiki", + }, + "windows": { + "command": "start https://github.com/a740g/InForm-PE/wiki", + }, + "presentation": { + "echo": false, + "reveal": "never", + "focus": false, + "panel": "dedicated", + "showReuseMessage": false, + "clear": false, + "close": true + } + }, + { + "label": "EXECUTE: Run", + "dependsOn": "BUILD: Compile", + "type": "shell", + "windows": { + "command": "${fileDirname}\\${fileBasenameNoExtension}.exe", + }, + "osx": { + "command": "${fileDirname}/${fileBasenameNoExtension}.run", + }, + "linux": { + "command": "${fileDirname}/${fileBasenameNoExtension}.run", + }, + "presentation": { + "echo": false, + "reveal": "always", + "focus": false, + "panel": "shared", + "showReuseMessage": false, + "clear": false + }, + "group": { + "kind": "build", + "isDefault": true + } + }, + { + "label": "QB64PE: Open Selected in InForm", + "type": "shell", + "windows": { + "command": "C:/Users/Admin/git/InForm-PE/UiEditor.exe", + "args": [ + "${file}" + ] + }, + "osx": { + "command": "~/git/InForm-PE/UiEditor", + "args": [ + "${file}" + ] + }, + "linux": { + "command": "setsid --fork /home/grymmjack/git/InForm-PE/UiEditor \"${file}\"" + }, + "presentation": { + "echo": false, + "reveal": "never", + "focus": false, + "panel": "dedicated", + "showReuseMessage": false, + "clear": false, + "close": true + }, + "group": { + "kind": "build" + } + }, + { + "label": "QB64PE: Open Selected in IDE", + "type": "shell", + "windows": { + "command": "C:/Users/Admin/git/QB64pe/qb64pe.exe", + "args": [ + "${file}" + ] + }, + "osx": { + "command": "/home/grymmjack/git/QB64pe/qb64pe", + "args": [ + "${file}" + ] + }, + "linux": { + "command": "setsid --fork /home/grymmjack/git/QB64pe/qb64pe \"${file}\"" + }, + "presentation": { + "echo": false, + "reveal": "never", + "focus": false, + "panel": "dedicated", + "showReuseMessage": false, + "clear": false, + "close": true + }, + "group": { + "kind": "build" + } + }, + { + "label": "EXECUTE: Selected File", + "type": "shell", + "windows": { + "command": "${fileDirname}\\${fileBasenameNoExtension}.exe", + }, + "osx": { + "command": "${fileDirname}/${fileBasenameNoExtension}.run", + }, + "linux": { + "command": "setsid --fork ${fileDirname}/${fileBasenameNoExtension}.run", + }, + "presentation": { + "echo": false, + "reveal": "never", + "focus": false, + "panel": "dedicated", + "showReuseMessage": false, + "clear": false, + "close": true + }, + "group": { + "kind": "build" + } + }, + { + "label": "IMAGE: Open in GIMP", + "type": "shell", + "windows": { + "command": "C:\\Program Files\\GIMP\\gimp.exe", + "args": [ + "${file}" + ] + }, + "osx": { + "command": "/Applications/GIMP.app/MacOS/gimp", + "args": [ + "${file}" + ] + }, + "linux": { + "command": "setsid --fork /usr/bin/gimp-2.10 \"${file}\"" + }, + "presentation": { + "echo": false, + "reveal": "never", + "focus": false, + "panel": "dedicated", + "showReuseMessage": false, + "clear": false, + "close": true + }, + "group": { + "kind": "none" + } + }, + { + "label": "IMAGE: Open in Inkscape", + "type": "shell", + "windows": { + "command": "C:\\Program Files\\Inkscape\\inkscape.exe", + "args": [ + "${file}" + ] + }, + "osx": { + "command": "/Applications/Inkscape.app/MacOS/inkscape", + "args": [ + "${file}" + ] + }, + "linux": { + "command": "setsid --fork /usr/bin/inkscape \"${file}\"" + }, + "presentation": { + "echo": false, + "reveal": "never", + "focus": false, + "panel": "dedicated", + "showReuseMessage": false, + "clear": false, + "close": true + }, + "group": { + "kind": "none" + } + }, + { + "label": "IMAGE: Open in Krita", + "type": "shell", + "windows": { + "command": "C:\\Program Files\\Krita\\krita.exe", + "args": [ + "${file}" + ] + }, + "osx": { + "command": "/Applications/Krita.app/MacOS/krita", + "args": [ + "${file}" + ] + }, + "linux": { + "command": "setsid --fork /var/lib/flatpak/exports/bin/org.kde.krita \"${file}\"" + }, + "presentation": { + "echo": false, + "reveal": "never", + "focus": false, + "panel": "shared", + "showReuseMessage": false, + "clear": false, + "close": true + }, + "group": { + "kind": "none" + } + }, + { + "label": "IMAGE: Open in Aseprite", + "type": "shell", + "windows": { + "command": "C:\\Program Files\\Aseprite\\aseprite.exe", + "args": [ + "${file}" + ] + }, + "osx": { + "command": "/Applications/Aseprite.app/MacOS/aseprite", + "args": [ + "${file}" + ] + }, + "linux": { + "command": "setsid --fork /home/grymmjack/.local/share/Steam/steamapps/common/Aseprite/aseprite \"${file}\"" + }, + "presentation": { + "close": true, + "echo": false, + "reveal": "never", + "focus": false, + "panel": "shared", + "showReuseMessage": false, + "clear": false, + "close": true + }, + "group": { + "kind": "none" + } + }, + { + "label": "TEXTMODE: Open in MoebiusXBIN", + "type": "shell", + "windows": { + "command": "B:\\Backup2020\\Drawing\\ANSI\\Programs\\Moebius x-bin GJ Edition\\MoebiusXBIN.exe", + "args": [ + "--no-sandbox", + "${file}" + ] + }, + "osx": { + "command": "/Applications/MoebisXBIN.app/MacOS/moebius", + "args": [ + "--no-sandbox", + "${file}" + ] + }, + "linux": { + "command": "setsid --fork /opt/MoebiusXBIN/moebius --no-sandbox \"${file}\"" + }, + "presentation": { + "close": true, + "echo": false, + "reveal": "never", + "focus": false, + "panel": "shared", + "showReuseMessage": false, + "clear": false, + "close": true + }, + "group": { + "kind": "none" + } + }, + { + "label": "TEXTMODE: Open in IcyDraw", + "type": "shell", + "windows": { + "command": "B:\\Backup2020\\Drawing\\ANSI\\Programs\\Icy Draw\\icy_draw.exe", + "args": [ + "${file}" + ] + }, + "osx": { + "command": "/Applications/IcyDraw.app/MacOS/IcyDraw", + "args": [ + "${file}" + ] + }, + "linux": { + "command": "setsid --fork /usr/bin/icy_draw \"${file}\"" + }, + "presentation": { + "close": true, + "echo": false, + "reveal": "never", + "focus": false, + "panel": "shared", + "showReuseMessage": false, + "clear": false, + "close": true + }, + "group": { + "kind": "none" + } + }, + { + "label": "TEXTMODE: Open in PabloDraw", + "type": "shell", + "windows": { + "command": "C:\\Program Files (x86)\\PabloDraw\\PabloDraw.exe", + "args": [ + "--edit", + "${file}" + ] + }, + "osx": { + "command": "/Applications/PabloDraw.app/MacOS/PabloDraw", + "args": [ + "--edit", + "${file}" + ] + }, + "linux": { + "command": "setsid --fork /home/grymmjack/git/pablodraw/artifacts/bin/PabloDraw/Debug/net7.0/linux-x64/PabloDraw --edit \"${file}\"" + }, + "presentation": { + "close": true, + "echo": false, + "reveal": "never", + "focus": false, + "panel": "shared", + "showReuseMessage": false, + "clear": false, + "close": true + }, + "group": { + "kind": "none" + } + }, + { + "label": "TEXTMODE: View in PabloDraw", + "type": "shell", + "windows": { + "command": "C:\\Program Files (x86)\\PabloDraw\\PabloDraw.exe", + "args": [ + "--file", + "${file}" + ] + }, + "osx": { + "command": "/Applications/PabloDraw.app/MacOS/PabloDraw", + "args": [ + "--file", + "${file}" + ] + }, + "linux": { + "command": "setsid --fork /home/grymmjack/git/pablodraw/artifacts/bin/PabloDraw/Debug/net7.0/linux-x64/PabloDraw --file \"${file}\"" + }, + "presentation": { + "close": true, + "echo": false, + "reveal": "never", + "focus": false, + "panel": "shared", + "showReuseMessage": false, + "clear": false, + "close": true + }, + "group": { + "kind": "none" + } + }, + { + "label": "BUILD: Compile", + "dependsOn": "BUILD: Remove", + "type": "shell", + "windows": { + "command": "${config:qb64.compilerPath}", + "args": [ + "-w", + "-x", + "${fileDirname}\\${fileBasename}", + "-o", + "${fileDirname}\\${fileBasenameNoExtension}.exe" + ] + }, + "osx": { + "command": "${config:qb64.compilerPath}", + "args": [ + "-w", + "-x", + "${fileDirname}/${fileBasename}", + "-o", + "${fileDirname}/${fileBasenameNoExtension}.run" + ] + }, + "linux": { + "command": "${config:qb64.compilerPath}", + "args": [ + "-w", + "-x", + "${fileDirname}/${fileBasename}", + "-o", + "${fileDirname}/${fileBasenameNoExtension}.run" + ] + }, + "presentation": { + "reveal": "always", + "clear": true, + "panel": "shared", + "focus": false + } + }, + { + "label": "BUILD: Remove", + "type": "shell", + "windows": { + "command": "del", + "args": [ + "${fileDirname}\\${fileBasenameNoExtension}.exe" + ] + }, + "osx": { + "command": "rm", + "args": [ + "-f", + "${fileDirname}/${fileBasenameNoExtension}.run" + ] + }, + "linux": { + "command": "rm", + "args": [ + "-f", + "${fileDirname}/${fileBasenameNoExtension}.run" + ] + }, + "presentation": { + "reveal": "always", + "panel": "shared", + "focus": false + } + } + ] +} \ No newline at end of file diff --git a/ANSI/ANSI.BAS b/ANSI/ANSI.BAS index cd4e9a2..87967bb 100644 --- a/ANSI/ANSI.BAS +++ b/ANSI/ANSI.BAS @@ -14,7 +14,8 @@ $IF GJ_LIB_ANSI_INC_BI = UNDEFINED THEN '$INCLUDE:'ANSI.BI' $END IF - +OPTION _EXPLICIT +OPTION _EXPLICITARRAY _TITLE "QB64_GJ_LIB ANSI LIB TESTS" @@ -38,63 +39,64 @@ $ENDIF _CONTROLCHR OFF LOCATE 1,1,1 ' Initialize the cursor and turn it on -DIM SHARED w, h, cw, ch -w = _WIDTH(0) -h = _HEIGHT(0) -cw = w \ 2 - 1 -ch = h \ 2 - 1 +DIM SHARED nil AS STRING +DIM SHARED AS INTEGER w, h, cw, ch, i +w% = _WIDTH(0) +h% = _HEIGHT(0) +cw% = w% \ 2 - 1 +ch% = h% \ 2 - 1 PRINT "In the following tests, _ = cursor position end." PRINT "Press a key to start tests" -anykey - -test_ansi_hide_cursor -test_ansi_show_cursor -test_ansi_home -test_ansi_locate -test_ansi_move_up -test_ansi_move_down -test_ansi_move_right -test_ansi_move_line_up -test_ansi_move_column -test_ansi_move_lines_up -test_ansi_move_lines_down -test_ansi_move_left -test_ansi_save_restore_pos -test_ansi_erase_to_eos -test_ansi_erase_to_bos -test_ansi_erase_screen -test_ansi_erase_to_eol -test_ansi_erase_from_sol -test_ansi_erase_line -test_ansi_mode_reset_all -test_ansi_mode_bold -test_ansi_mode_bold_reset -test_ansi_mode_dim -test_ansi_mode_dim_reset -test_ansi_mode_italic -test_ansi_mode_italic_reset -test_ansi_mode_underline -test_ansi_mode_underline_reset -test_ansi_mode_blinking -test_ansi_mode_blinking_reset -test_ansi_mode_inverse -test_ansi_mode_inverse_reset -test_ansi_mode_invisible -test_ansi_mode_invisible_reset -test_ansi_mode_strikethrough -test_ansi_mode_strikethrough_reset -test_ansi_fg_reset -test_ansi_bg_reset -test_fg_colors_standard -test_bg_colors_standard -test_fg_256_colors -test_bg_256_colors -test_fg_rgb_colors -test_bg_rgb_colors - - -PRINT "All tests complete, press a key." : anykey +CALL anykey + +CALL test_ansi_hide_cursor +CALL test_ansi_show_cursor +CALL test_ansi_home +CALL test_ansi_locate +CALL test_ansi_move_up +CALL test_ansi_move_down +CALL test_ansi_move_right +CALL test_ansi_move_line_up +CALL test_ansi_move_column +CALL test_ansi_move_lines_up +CALL test_ansi_move_lines_down +CALL test_ansi_move_left +CALL test_ansi_save_restore_pos +CALL test_ansi_erase_to_eos +CALL test_ansi_erase_to_bos +CALL test_ansi_erase_screen +CALL test_ansi_erase_to_eol +CALL test_ansi_erase_from_sol +CALL test_ansi_erase_line +CALL test_ansi_mode_reset_all +CALL test_ansi_mode_bold +CALL test_ansi_mode_bold_reset +CALL test_ansi_mode_dim +CALL test_ansi_mode_dim_reset +CALL test_ansi_mode_italic +CALL test_ansi_mode_italic_reset +CALL test_ansi_mode_underline +CALL test_ansi_mode_underline_reset +CALL test_ansi_mode_blinking +CALL test_ansi_mode_blinking_reset +CALL test_ansi_mode_inverse +CALL test_ansi_mode_inverse_reset +CALL test_ansi_mode_invisible +CALL test_ansi_mode_invisible_reset +CALL test_ansi_mode_strikethrough +CALL test_ansi_mode_strikethrough_reset +CALL test_ansi_fg_reset +CALL test_ansi_bg_reset +CALL test_fg_colors_standard +CALL test_bg_colors_standard +CALL test_fg_256_colors +CALL test_bg_256_colors +CALL test_fg_rgb_colors +CALL test_bg_rgb_colors + +PRINT "All tests complete, press a key." +CALL anykey CLS @@ -104,685 +106,689 @@ SUB anykey() END SUB -SUB test_ansi_hide_cursor () +SUB test_ansi_hide_cursor() CLS - PRINT "Press a key to test ansi_hide_cursor()..." - anykey - nil$ = ansi_hide_cursor + "CURSOR IS HIDDEN?" + PRINT "Press a key to test ANSI.hide_cursor()..." + CALL anykey + nil$ = ANSI.hide_cursor + "CURSOR IS HIDDEN?" nil$ = nil$ + "_" IF GJ_LIB_ANSI_OUTPUT THEN PRINT nil$; - nil$ = ansi_mode_reset_all - anykey : test_complete + nil$ = ANSI.mode_reset_all + CALL anykey + CALL test_complete END SUB -SUB test_ansi_show_cursor () +SUB test_ansi_show_cursor() CLS - PRINT "Press a key to test ansi_show_cursor()..." - anykey - nil$ = ansi_show_cursor + "CURSOR IS VISIBLE?" + PRINT "Press a key to test ANSI.show_cursor()..." + CALL anykey + nil$ = ANSI.show_cursor + "CURSOR IS VISIBLE?" nil$ = nil$ + "_" IF GJ_LIB_ANSI_OUTPUT THEN PRINT nil$; - nil$ = ansi_mode_reset_all - anykey : test_complete + nil$ = ANSI.mode_reset_all + CALL anykey : CALL test_complete END SUB -SUB test_bg_rgb_colors () +SUB test_bg_rgb_colors() CLS - PRINT "Press a key to test ansi_bg_rgb()..." - anykey + PRINT "Press a key to test ANSI.bg_rgb()..." + CALL anykey nil$ = "" FOR i = 0 TO 255 - nil$ = nil$ + ansi_bg_rgb(i, i, i) + STR$(i) + nil$ = nil$ + ANSI.bg_rgb(i, i, i) + STR$(i) NEXT i nil$ = nil$ + "_" IF GJ_LIB_ANSI_OUTPUT THEN PRINT nil$; - nil$ = ansi_mode_reset_all - anykey : test_complete + nil$ = ANSI.mode_reset_all + CALL anykey : CALL test_complete END SUB -SUB test_fg_rgb_colors () +SUB test_fg_rgb_colors() CLS - PRINT "Press a key to test ansi_fg_rgb()..." - anykey + PRINT "Press a key to test ANSI.fg_rgb()..." + CALL anykey nil$ = "" FOR i = 0 TO 255 - nil$ = nil$ + ansi_fg_rgb(i, i, i) + STR$(i) + nil$ = nil$ + ANSI.fg_rgb(i, i, i) + STR$(i) NEXT i nil$ = nil$ + "_" IF GJ_LIB_ANSI_OUTPUT THEN PRINT nil$; - nil$ = ansi_mode_reset_all - anykey : test_complete + nil$ = ANSI.mode_reset_all + CALL anykey : CALL test_complete END SUB -SUB test_bg_256_colors () +SUB test_bg_256_colors() CLS - PRINT "Press a key to test ansi_bg_256()..." - anykey + PRINT "Press a key to test ANSI.bg_256()..." + CALL anykey nil$ = "" FOR i = 0 TO 255 - nil$ = nil$ + ansi_bg_256(i) + STR$(i) + nil$ = nil$ + ANSI.bg_256(i) + STR$(i) NEXT i nil$ = nil$ + "_" IF GJ_LIB_ANSI_OUTPUT THEN PRINT nil$; - nil$ = ansi_mode_reset_all - anykey : test_complete + nil$ = ANSI.mode_reset_all + CALL anykey : CALL test_complete END SUB -SUB test_fg_256_colors () +SUB test_fg_256_colors() CLS - PRINT "Press a key to test ansi_fg_256()..." - anykey + PRINT "Press a key to test ANSI.fg_256()..." + CALL anykey nil$ = "" FOR i = 0 TO 255 - nil$ = nil$ + ansi_fg_256(i) + STR$(i) + nil$ = nil$ + ANSI.fg_256(i) + STR$(i) NEXT i nil$ = nil$ + "_" IF GJ_LIB_ANSI_OUTPUT THEN PRINT nil$; - nil$ = ansi_mode_reset_all - anykey : test_complete + nil$ = ANSI.mode_reset_all + CALL anykey : CALL test_complete END SUB -SUB test_bg_colors_standard () +SUB test_bg_colors_standard() CLS - PRINT "Press a key to test ansi_bg_*()..." - anykey - nil$ = ansi_bg_white + ansi_fg_black + " BLACK " - nil$ = nil$ + ansi_bg_reset + ansi_bg_blue + " BLUE " - nil$ = nil$ + ansi_bg_green + " GREEN " - nil$ = nil$ + ansi_bg_cyan + " CYAN " - nil$ = nil$ + ansi_bg_red + " RED " - nil$ = nil$ + ansi_bg_magenta + " MAGENTA " - nil$ = nil$ + ansi_bg_yellow + " YELLOW " - nil$ = nil$ + ansi_bg_white + " WHITE " - nil$ = nil$ + ansi_bg_bright_black + " BRIGHT BLACK " - nil$ = nil$ + ansi_bg_bright_blue + " BRIGHT BLUE " - nil$ = nil$ + ansi_bg_bright_green + " BRIGHT GREEN " - nil$ = nil$ + ansi_bg_bright_cyan + " BRIGHT CYAN " - nil$ = nil$ + ansi_bg_bright_red + " BRIGHT RED " - nil$ = nil$ + ansi_bg_bright_magenta + " BRIGHT MAGENTA " - nil$ = nil$ + ansi_bg_bright_yellow + " BRIGHT YELLOW " - nil$ = nil$ + ansi_bg_bright_white + " BRIGHT WHITE " + PRINT "Press a key to test ANSI.bg_*()..." + CALL anykey + nil$ = ANSI.bg_white + ANSI.fg_black + " BLACK " + nil$ = nil$ + ANSI.bg_reset + ANSI.bg_blue + " BLUE " + nil$ = nil$ + ANSI.bg_green + " GREEN " + nil$ = nil$ + ANSI.bg_cyan + " CYAN " + nil$ = nil$ + ANSI.bg_red + " RED " + nil$ = nil$ + ANSI.bg_magenta + " MAGENTA " + nil$ = nil$ + ANSI.bg_yellow + " YELLOW " + nil$ = nil$ + ANSI.bg_white + " WHITE " + nil$ = nil$ + ANSI.bg_bright_black + " BRIGHT BLACK " + nil$ = nil$ + ANSI.bg_bright_blue + " BRIGHT BLUE " + nil$ = nil$ + ANSI.bg_bright_green + " BRIGHT GREEN " + nil$ = nil$ + ANSI.bg_bright_cyan + " BRIGHT CYAN " + nil$ = nil$ + ANSI.bg_bright_red + " BRIGHT RED " + nil$ = nil$ + ANSI.bg_bright_magenta + " BRIGHT MAGENTA " + nil$ = nil$ + ANSI.bg_bright_yellow + " BRIGHT YELLOW " + nil$ = nil$ + ANSI.bg_bright_white + " BRIGHT WHITE " nil$ = nil$ + "_" IF GJ_LIB_ANSI_OUTPUT THEN PRINT nil$; - nil$ = ansi_mode_reset_all - anykey : test_complete + nil$ = ANSI.mode_reset_all + CALL anykey : CALL test_complete END SUB -SUB test_fg_colors_standard () +SUB test_fg_colors_standard() CLS - PRINT "Press a key to test ansi_fg_*()..." - anykey - nil$ = ansi_bg_white + ansi_fg_black + " BLACK " - nil$ = nil$ + ansi_bg_reset + ansi_fg_blue + " BLUE " - nil$ = nil$ + ansi_fg_green + " GREEN " - nil$ = nil$ + ansi_fg_cyan + " CYAN " - nil$ = nil$ + ansi_fg_red + " RED " - nil$ = nil$ + ansi_fg_magenta + " MAGENTA " - nil$ = nil$ + ansi_fg_yellow + " YELLOW " - nil$ = nil$ + ansi_fg_white + " WHITE " - nil$ = nil$ + ansi_fg_bright_black + " BRIGHT BLACK " - nil$ = nil$ + ansi_fg_bright_blue + " BRIGHT BLUE " - nil$ = nil$ + ansi_fg_bright_green + " BRIGHT GREEN " - nil$ = nil$ + ansi_fg_bright_cyan + " BRIGHT CYAN " - nil$ = nil$ + ansi_fg_bright_red + " BRIGHT RED " - nil$ = nil$ + ansi_fg_bright_magenta + " BRIGHT MAGENTA " - nil$ = nil$ + ansi_fg_bright_yellow + " BRIGHT YELLOW " - nil$ = nil$ + ansi_fg_bright_white + " BRIGHT WHITE " + PRINT "Press a key to test ANSI.fg_*()..." + CALL anykey + nil$ = ANSI.bg_white + ANSI.fg_black + " BLACK " + nil$ = nil$ + ANSI.bg_reset + ANSI.fg_blue + " BLUE " + nil$ = nil$ + ANSI.fg_green + " GREEN " + nil$ = nil$ + ANSI.fg_cyan + " CYAN " + nil$ = nil$ + ANSI.fg_red + " RED " + nil$ = nil$ + ANSI.fg_magenta + " MAGENTA " + nil$ = nil$ + ANSI.fg_yellow + " YELLOW " + nil$ = nil$ + ANSI.fg_white + " WHITE " + nil$ = nil$ + ANSI.fg_bright_black + " BRIGHT BLACK " + nil$ = nil$ + ANSI.fg_bright_blue + " BRIGHT BLUE " + nil$ = nil$ + ANSI.fg_bright_green + " BRIGHT GREEN " + nil$ = nil$ + ANSI.fg_bright_cyan + " BRIGHT CYAN " + nil$ = nil$ + ANSI.fg_bright_red + " BRIGHT RED " + nil$ = nil$ + ANSI.fg_bright_magenta + " BRIGHT MAGENTA " + nil$ = nil$ + ANSI.fg_bright_yellow + " BRIGHT YELLOW " + nil$ = nil$ + ANSI.fg_bright_white + " BRIGHT WHITE " nil$ = nil$ + "_" IF GJ_LIB_ANSI_OUTPUT THEN PRINT nil$; - nil$ = ansi_mode_reset_all - anykey : test_complete + nil$ = ANSI.mode_reset_all + CALL anykey : CALL test_complete END SUB -SUB test_complete () +SUB test_complete() CLS PRINT "Test complete, press a key." - nil$ = ansi_mode_reset_all + nil$ = ANSI.mode_reset_all IF GJ_LIB_ANSI_OUTPUT THEN PRINT nil$; - anykey + CALL anykey END SUB -SUB test_ansi_bg_reset () +SUB test_ansi_bg_reset() CLS - PRINT "Press a key to test ansi_bg_reset()..." + PRINT "Press a key to test ANSI.bg_reset()..." anykey - nil$ = ansi_bg_bright_yellow + "This is bright yellow" + nil$ = ANSI.bg_bright_yellow + "This is bright yellow" IF GJ_LIB_ANSI_OUTPUT THEN PRINT nil$; - anykey - nil$ = ansi_bg_reset + "bg is reset" + CALL anykey + nil$ = ANSI.bg_reset + "bg is reset" nil$ = nil$ + "_" IF GJ_LIB_ANSI_OUTPUT THEN PRINT nil$; - anykey : test_complete + CALL anykey : CALL test_complete END SUB -SUB test_ansi_fg_reset () +SUB test_ansi_fg_reset() CLS - PRINT "Press a key to test ansi_fg_reset()..." + PRINT "Press a key to test ANSI.fg_reset()..." anykey - nil$ = ansi_fg_bright_yellow + "This is bright yellow" + nil$ = ANSI.fg_bright_yellow + "This is bright yellow" IF GJ_LIB_ANSI_OUTPUT THEN PRINT nil$; - anykey - nil$ = ansi_fg_reset + "fg is reset" + CALL anykey + nil$ = ANSI.fg_reset + "fg is reset" nil$ = nil$ + "_" IF GJ_LIB_ANSI_OUTPUT THEN PRINT nil$; - anykey : test_complete + CALL anykey : CALL test_complete END SUB -SUB test_ansi_mode_strikethrough_reset () +SUB test_ansi_mode_strikethrough_reset() CLS - PRINT "Press a key to test ansi_mode_strikethrough()..." + PRINT "Press a key to test ANSI.mode_strikethrough()..." anykey - nil$ = ansi_mode_strikethrough + "This is strikethrough" + nil$ = ANSI.mode_strikethrough + "This is strikethrough" IF GJ_LIB_ANSI_OUTPUT THEN PRINT nil$; - anykey - nil$ = ansi_mode_strikethrough_reset + " - and this is NOT strikethrough" + CALL anykey + nil$ = ANSI.mode_strikethrough_reset + " - and this is NOT strikethrough" nil$ = nil$ + "_" IF GJ_LIB_ANSI_OUTPUT THEN PRINT nil$; - anykey : test_complete + CALL anykey : CALL test_complete END SUB -SUB test_ansi_mode_strikethrough () +SUB test_ansi_mode_strikethrough() CLS - PRINT "Press a key to test ansi_mode_strikethrough()..." + PRINT "Press a key to test ANSI.mode_strikethrough()..." anykey nil$ = "This is not strikethrough" IF GJ_LIB_ANSI_OUTPUT THEN PRINT nil$; - anykey - nil$ = ansi_mode_strikethrough + " - and this is strikethrough" + CALL anykey + nil$ = ANSI.mode_strikethrough + " - and this is strikethrough" nil$ = nil$ + "_" IF GJ_LIB_ANSI_OUTPUT THEN PRINT nil$; - anykey : test_complete + CALL anykey : CALL test_complete END SUB -SUB test_ansi_mode_invisible_reset () +SUB test_ansi_mode_invisible_reset() CLS - PRINT "Press a key to test ansi_mode_invisible()..." + PRINT "Press a key to test ANSI.mode_invisible()..." anykey - nil$ = ansi_mode_invisible + "This is invisible" + nil$ = ANSI.mode_invisible + "This is invisible" IF GJ_LIB_ANSI_OUTPUT THEN PRINT nil$; - anykey - nil$ = ansi_mode_invisible_reset + " - and this is NOT invisible" + CALL anykey + nil$ = ANSI.mode_invisible_reset + " - and this is NOT invisible" nil$ = nil$ + "_" IF GJ_LIB_ANSI_OUTPUT THEN PRINT nil$; - anykey : test_complete + CALL anykey : CALL test_complete END SUB -SUB test_ansi_mode_invisible () +SUB test_ansi_mode_invisible() CLS - PRINT "Press a key to test ansi_mode_invisible()..." + PRINT "Press a key to test ANSI.mode_invisible()..." anykey nil$ = "This is not invisible" IF GJ_LIB_ANSI_OUTPUT THEN PRINT nil$; - anykey - nil$ = ansi_mode_invisible + " - and this is invisible" + CALL anykey + nil$ = ANSI.mode_invisible + " - and this is invisible" nil$ = nil$ + "_" IF GJ_LIB_ANSI_OUTPUT THEN PRINT nil$; - anykey : test_complete + CALL anykey : CALL test_complete END SUB -SUB test_ansi_mode_inverse_reset () +SUB test_ansi_mode_inverse_reset() CLS - PRINT "Press a key to test ansi_mode_inverse()..." + PRINT "Press a key to test ANSI.mode_inverse()..." anykey - nil$ = ansi_mode_inverse + "This is inverse" + nil$ = ANSI.mode_inverse + "This is inverse" IF GJ_LIB_ANSI_OUTPUT THEN PRINT nil$; - anykey - nil$ = ansi_mode_inverse_reset + " - and this is NOT inverse" + CALL anykey + nil$ = ANSI.mode_inverse_reset + " - and this is NOT inverse" nil$ = nil$ + "_" IF GJ_LIB_ANSI_OUTPUT THEN PRINT nil$; - anykey : test_complete + CALL anykey : CALL test_complete END SUB -SUB test_ansi_mode_inverse () +SUB test_ansi_mode_inverse() CLS - PRINT "Press a key to test ansi_mode_inverse()..." + PRINT "Press a key to test ANSI.mode_inverse()..." anykey nil$ = "This is not inverse" IF GJ_LIB_ANSI_OUTPUT THEN PRINT nil$; - anykey - nil$ = ansi_mode_inverse + " - and this is inverse" + CALL anykey + nil$ = ANSI.mode_inverse + " - and this is inverse" nil$ = nil$ + "_" IF GJ_LIB_ANSI_OUTPUT THEN PRINT nil$; - anykey : test_complete + CALL anykey : CALL test_complete END SUB -SUB test_ansi_mode_blinking_reset () +SUB test_ansi_mode_blinking_reset() CLS - PRINT "Press a key to test ansi_mode_blinking()..." + PRINT "Press a key to test ANSI.mode_blinking()..." anykey - nil$ = ansi_mode_blinking + "This is blinking" + nil$ = ANSI.mode_blinking + "This is blinking" IF GJ_LIB_ANSI_OUTPUT THEN PRINT nil$; - anykey - nil$ = ansi_mode_blinking_reset + " - and this is NOT blinking" + CALL anykey + nil$ = ANSI.mode_blinking_reset + " - and this is NOT blinking" nil$ = nil$ + "_" IF GJ_LIB_ANSI_OUTPUT THEN PRINT nil$; - anykey : test_complete + CALL anykey : CALL test_complete END SUB -SUB test_ansi_mode_blinking () +SUB test_ansi_mode_blinking() CLS - PRINT "Press a key to test ansi_mode_blinking()..." + PRINT "Press a key to test ANSI.mode_blinking()..." anykey nil$ = "This is not blinking" IF GJ_LIB_ANSI_OUTPUT THEN PRINT nil$; - anykey - nil$ = ansi_mode_blinking + " - and this is blinking" + CALL anykey + nil$ = ANSI.mode_blinking + " - and this is blinking" nil$ = nil$ + "_" IF GJ_LIB_ANSI_OUTPUT THEN PRINT nil$; - anykey : test_complete + CALL anykey : CALL test_complete END SUB -SUB test_ansi_mode_underline_reset () +SUB test_ansi_mode_underline_reset() CLS - PRINT "Press a key to test ansi_mode_underline()..." + PRINT "Press a key to test ANSI.mode_underline()..." anykey - nil$ = ansi_mode_underline + "This is underline" + nil$ = ANSI.mode_underline + "This is underline" IF GJ_LIB_ANSI_OUTPUT THEN PRINT nil$; - anykey - nil$ = ansi_mode_underline_reset + " - and this is NOT underline" + CALL anykey + nil$ = ANSI.mode_underline_reset + " - and this is NOT underline" nil$ = nil$ + "_" IF GJ_LIB_ANSI_OUTPUT THEN PRINT nil$; - anykey : test_complete + CALL anykey : CALL test_complete END SUB -SUB test_ansi_mode_underline () +SUB test_ansi_mode_underline() CLS - PRINT "Press a key to test ansi_mode_underline()..." + PRINT "Press a key to test ANSI.mode_underline()..." anykey nil$ = "This is not underline" IF GJ_LIB_ANSI_OUTPUT THEN PRINT nil$; - anykey - nil$ = ansi_mode_underline + " - and this is underline" + CALL anykey + nil$ = ANSI.mode_underline + " - and this is underline" nil$ = nil$ + "_" IF GJ_LIB_ANSI_OUTPUT THEN PRINT nil$; - anykey : test_complete + CALL anykey : CALL test_complete END SUB -SUB test_ansi_mode_italic_reset () +SUB test_ansi_mode_italic_reset() CLS - PRINT "Press a key to test ansi_mode_italic()..." + PRINT "Press a key to test ANSI.mode_italic()..." anykey - nil$ = ansi_mode_italic + "This is italic" + nil$ = ANSI.mode_italic + "This is italic" IF GJ_LIB_ANSI_OUTPUT THEN PRINT nil$; - anykey - nil$ = ansi_mode_italic_reset + " - and this is NOT italic" + CALL anykey + nil$ = ANSI.mode_italic_reset + " - and this is NOT italic" nil$ = nil$ + "_" IF GJ_LIB_ANSI_OUTPUT THEN PRINT nil$; - anykey : test_complete + CALL anykey : CALL test_complete END SUB -SUB test_ansi_mode_italic () +SUB test_ansi_mode_italic() CLS - PRINT "Press a key to test ansi_mode_italic()..." + PRINT "Press a key to test ANSI.mode_italic()..." anykey nil$ = "This is not italic" IF GJ_LIB_ANSI_OUTPUT THEN PRINT nil$; - anykey - nil$ = ansi_mode_italic + " - and this is italic" + CALL anykey + nil$ = ANSI.mode_italic + " - and this is italic" nil$ = nil$ + "_" IF GJ_LIB_ANSI_OUTPUT THEN PRINT nil$; - anykey : test_complete + CALL anykey : CALL test_complete END SUB -SUB test_ansi_mode_dim_reset () +SUB test_ansi_mode_dim_reset() CLS - PRINT "Press a key to test ansi_mode_dim()..." + PRINT "Press a key to test ANSI.mode_dim()..." anykey - nil$ = ansi_mode_dim + "This is dim" + nil$ = ANSI.mode_dim + "This is dim" IF GJ_LIB_ANSI_OUTPUT THEN PRINT nil$; - anykey - nil$ = ansi_mode_dim_reset + " - and this is NOT dim" + CALL anykey + nil$ = ANSI.mode_dim_reset + " - and this is NOT dim" nil$ = nil$ + "_" IF GJ_LIB_ANSI_OUTPUT THEN PRINT nil$; - anykey : test_complete + CALL anykey : CALL test_complete END SUB -SUB test_ansi_mode_dim () +SUB test_ansi_mode_dim() CLS - PRINT "Press a key to test ansi_mode_dim()..." + PRINT "Press a key to test ANSI.mode_dim()..." anykey nil$ = "This is not dim" IF GJ_LIB_ANSI_OUTPUT THEN PRINT nil$; - anykey - nil$ = ansi_mode_dim + " - and this is dim" + CALL anykey + nil$ = ANSI.mode_dim + " - and this is dim" nil$ = nil$ + "_" IF GJ_LIB_ANSI_OUTPUT THEN PRINT nil$; - anykey : test_complete + CALL anykey : CALL test_complete END SUB -SUB test_ansi_mode_bold_reset () +SUB test_ansi_mode_bold_reset() CLS - PRINT "Press a key to test ansi_mode_bold()..." + PRINT "Press a key to test ANSI.mode_bold()..." anykey - nil$ = ansi_mode_bold + "This is bold" + nil$ = ANSI.mode_bold + "This is bold" IF GJ_LIB_ANSI_OUTPUT THEN PRINT nil$; - anykey - nil$ = ansi_mode_bold_reset + " - and this is NOT bold" + CALL anykey + nil$ = ANSI.mode_bold_reset + " - and this is NOT bold" nil$ = nil$ + "_" IF GJ_LIB_ANSI_OUTPUT THEN PRINT nil$; - anykey : test_complete + CALL anykey : CALL test_complete END SUB -SUB test_ansi_mode_bold () +SUB test_ansi_mode_bold() CLS - PRINT "Press a key to test ansi_mode_bold()..." + PRINT "Press a key to test ANSI.mode_bold()..." anykey nil$ = "This is not bold" IF GJ_LIB_ANSI_OUTPUT THEN PRINT nil$; - anykey - nil$ = ansi_mode_bold + " - and this is bold" + CALL anykey + nil$ = ANSI.mode_bold + " - and this is bold" nil$ = nil$ + "_" IF GJ_LIB_ANSI_OUTPUT THEN PRINT nil$; - anykey : test_complete + CALL anykey : CALL test_complete END SUB -SUB test_ansi_mode_reset_all () +SUB test_ansi_mode_reset_all() CLS - PRINT "Press a key to test ansi_mode_reset_all()..." + PRINT "Press a key to test ANSI.mode_reset_all()..." anykey fill_screen_with_text "*" - nil$ = "MODE IS NOT RESET" + "---stuff---" + ansi_mode_reset_all + nil$ = "MODE IS NOT RESET" + "---stuff---" + ANSI.mode_reset_all nil$ = nil$ + "MODE IS RESET" nil$ = nil$ + "_" IF GJ_LIB_ANSI_OUTPUT THEN PRINT nil$; - anykey : test_complete + CALL anykey : CALL test_complete END SUB -SUB test_ansi_erase_line () +SUB test_ansi_erase_line() CLS - PRINT "Press a key to test erase line with ansi_erase_line()..." + PRINT "Press a key to test erase line with ANSI.erase_line()..." anykey fill_screen_with_text "*" - anykey - nil$ = ansi_locate(ch, cw) + CALL anykey + nil$ = ANSI.locate(ch, cw) nil$ = nil$ + "Line is here in the middle." - nil$ = nil$ + ansi_erase_line + nil$ = nil$ + ANSI.erase_line nil$ = nil$ + "_" IF GJ_LIB_ANSI_OUTPUT THEN PRINT nil$; - anykey : test_complete + CALL anykey : CALL test_complete END SUB -SUB test_ansi_erase_from_sol () +SUB test_ansi_erase_from_sol() CLS - PRINT "Press a key to test erase from start of line with ansi_erase_from_sol()..." + PRINT "Press a key to test erase from start of line with ANSI.erase_from_sol()..." anykey fill_screen_with_text "*" - anykey - nil$ = ansi_locate(ch, cw) + CALL anykey + nil$ = ANSI.locate(ch, cw) nil$ = nil$ + "Line is here in the middle." - nil$ = nil$ + ansi_erase_from_sol + nil$ = nil$ + ANSI.erase_from_sol nil$ = nil$ + "_" IF GJ_LIB_ANSI_OUTPUT THEN PRINT nil$; - anykey : test_complete + CALL anykey : CALL test_complete END SUB -SUB test_ansi_erase_to_eol () +SUB test_ansi_erase_to_eol() CLS - PRINT "Press a key to test erase to end of line with ansi_erase_to_eol()..." + PRINT "Press a key to test erase to end of line with ANSI.erase_to_eol()..." anykey fill_screen_with_text "*" - anykey - nil$ = ansi_locate(ch, cw) + CALL anykey + nil$ = ANSI.locate(ch, cw) nil$ = nil$ + "Line is here in the middle." - nil$ = nil$ + ansi_erase_to_eol + nil$ = nil$ + ANSI.erase_to_eol nil$ = nil$ + "_" IF GJ_LIB_ANSI_OUTPUT THEN PRINT nil$; - anykey : test_complete + CALL anykey : CALL test_complete END SUB -SUB test_ansi_erase_screen () +SUB test_ansi_erase_screen() CLS - PRINT "Press a key to test erase screen with ansi_erase_screen()..." + PRINT "Press a key to test erase screen with ANSI.erase_screen()..." anykey fill_screen_with_text "*" - anykey - nil$ = ansi_erase_screen + CALL anykey + nil$ = ANSI.erase_screen nil$ = nil$ + "_" IF GJ_LIB_ANSI_OUTPUT THEN PRINT nil$; - anykey : test_complete + CALL anykey : CALL test_complete END SUB -SUB test_ansi_erase_to_bos () +SUB test_ansi_erase_to_bos() CLS - PRINT "Press a key to test erase to beginning of screen with ansi_erase_to_bos()..." + PRINT "Press a key to test erase to beginning of screen with ANSI.erase_to_bos()..." anykey fill_screen_with_text "*" - anykey - nil$ = ansi_locate(ch, cw) + CALL anykey + nil$ = ANSI.locate(ch, cw) nil$ = nil$ + "Line is here in the middle." - nil$ = nil$ + ansi_erase_to_bos + nil$ = nil$ + ANSI.erase_to_bos nil$ = nil$ + "_" IF GJ_LIB_ANSI_OUTPUT THEN PRINT nil$; - anykey : test_complete + CALL anykey : CALL test_complete END SUB -SUB test_ansi_erase_to_eos () +SUB test_ansi_erase_to_eos() CLS - PRINT "Press a key to test erase to end of screen with ansi_erase_to_eos()..." + PRINT "Press a key to test erase to end of screen with ANSI.erase_to_eos()..." anykey fill_screen_with_text "*" - anykey - nil$ = ansi_locate(ch, cw) + CALL anykey + nil$ = ANSI.locate(ch, cw) nil$ = nil$ + "Line is here in the middle." - nil$ = nil$ + ansi_erase_to_eos + nil$ = nil$ + ANSI.erase_to_eos nil$ = nil$ + "_" IF GJ_LIB_ANSI_OUTPUT THEN PRINT nil$; - anykey : test_complete + CALL anykey : CALL test_complete END SUB -SUB test_ansi_save_restore_pos () +SUB test_ansi_save_restore_pos() CLS PRINT "Press a key to test ansi save and restore cursor..." - anykey - nil$ = ansi_locate(ch, cw) + CALL anykey + nil$ = ANSI.locate(ch, cw) nil$ = nil$ + "Line is here in the middle." - nil$ = nil$ + ansi_save_pos + nil$ = nil$ + ANSI.save_pos nil$ = nil$ + "X <- original X" IF GJ_LIB_ANSI_OUTPUT THEN PRINT nil$; - anykey + CALL anykey nil$ = STRING$(50, " ") nil$ = nil$ + "If Y overwrites original X, it works." nil$ = STRING$(50, " ") + "more stuff" - nil$ = ansi_restore_pos + nil$ = ANSI.restore_pos nil$ = nil$ + "Y" nil$ = nil$ + "_" IF GJ_LIB_ANSI_OUTPUT THEN PRINT nil$; - anykey : test_complete + CALL anykey : CALL test_complete END SUB -SUB test_ansi_move_line_up () +SUB test_ansi_move_line_up() CLS - PRINT "Press a key to test ansi cursor move up with ansi_move_line_up()..." - anykey - nil$ = ansi_locate(ch, cw) + "Line is here in the middle." + PRINT "Press a key to test ansi cursor move up with ANSI.move_line_up()..." + CALL anykey + nil$ = ANSI.locate(ch, cw) + "Line is here in the middle." IF GJ_LIB_ANSI_OUTPUT THEN PRINT nil$; - nil$ = nil$ + ansi_locate(ch, cw) - nil$ = nil$ + ansi_move_line_up + "Line is one line up." - nil$ = nil$ + ansi_move_line_up + "Line is two lines up." + nil$ = nil$ + ANSI.locate(ch, cw) + nil$ = nil$ + ANSI.move_line_up + "Line is one line up." + nil$ = nil$ + ANSI.move_line_up + "Line is two lines up." nil$ = nil$ + "_" IF GJ_LIB_ANSI_OUTPUT THEN PRINT nil$; - anykey : test_complete + CALL anykey : CALL test_complete END SUB -SUB test_ansi_move_column () - PRINT "Press a key to move across screen by 5's with ansi_move_column()..." - anykey +SUB test_ansi_move_column() + DIM AS STRING w, h + PRINT "Press a key to move across screen by 5's with ANSI.move_column()..." + CALL anykey CLS - w$ = _TRIM$(STR$(w-1)) - h$ = _TRIM$(STR$(h)) + w$ = _TRIM$(STR$(w%-1)) + h$ = _TRIM$(STR$(h%)) nil$ = "" - FOR i = 5 TO (w - 5) STEP 5 - nil$ = nil$ + ansi_move_column(i) + STR$(i) - NEXT i + FOR i% = 5 TO (w% - 5) STEP 5 + nil$ = nil$ + ANSI.move_column(i%) + STR$(i%) + NEXT i% nil$ = nil$ + "_" IF GJ_LIB_ANSI_OUTPUT THEN PRINT nil$; - anykey : test_complete + CALL anykey : CALL test_complete END SUB -SUB test_ansi_move_lines_up () +SUB test_ansi_move_lines_up() CLS - PRINT "Press a key to test ansi cursor move up with ansi_move_lines_up()..." - anykey - nil$ = ansi_locate(ch, cw) + "Line is here in the middle." + PRINT "Press a key to test ansi cursor move up with ANSI.move_lines_up()..." + CALL anykey + nil$ = ANSI.locate(ch, cw) + "Line is here in the middle." IF GJ_LIB_ANSI_OUTPUT THEN PRINT nil$; - nil$ = nil$ + ansi_locate(ch, cw) - nil$ = nil$ + ansi_move_lines_up(1) + "Line is one line up." - nil$ = nil$ + ansi_move_lines_up(5) + "Line is six lines up." + nil$ = nil$ + ANSI.locate(ch, cw) + nil$ = nil$ + ANSI.move_lines_up(1) + "Line is one line up." + nil$ = nil$ + ANSI.move_lines_up(5) + "Line is six lines up." nil$ = nil$ + "_" IF GJ_LIB_ANSI_OUTPUT THEN PRINT nil$; - anykey : test_complete + CALL anykey : CALL test_complete END SUB -SUB test_ansi_move_lines_down () +SUB test_ansi_move_lines_down() CLS - PRINT "Press a key to test ansi cursor move down with ansi_move_lines_down()..." - anykey - nil$ = ansi_locate(ch, cw) + "Line is here in the middle." + PRINT "Press a key to test ansi cursor move down with ANSI.move_lines_down()..." + CALL anykey + nil$ = ANSI.locate(ch, cw) + "Line is here in the middle." IF GJ_LIB_ANSI_OUTPUT THEN PRINT nil$; - nil$ = nil$ + ansi_locate(ch, cw) - nil$ = nil$ + ansi_move_lines_down(1) + "Line is one line down." - nil$ = nil$ + ansi_move_lines_down(5) + "Line is six lines down." + nil$ = nil$ + ANSI.locate(ch, cw) + nil$ = nil$ + ANSI.move_lines_down(1) + "Line is one line down." + nil$ = nil$ + ANSI.move_lines_down(5) + "Line is six lines down." nil$ = nil$ + "_" IF GJ_LIB_ANSI_OUTPUT THEN PRINT nil$; - anykey : test_complete + CALL anykey : CALL test_complete END SUB -SUB test_ansi_move_left () +SUB test_ansi_move_left() CLS - PRINT "Press a key to test ansi cursor move left with ansi_move_left()..." - anykey - nil$ = ansi_locate(ch, cw) + "Line is here in the middle." + PRINT "Press a key to test ansi cursor move left with ANSI.move_left()..." + CALL anykey + nil$ = ANSI.locate(ch, cw) + "Line is here in the middle." IF GJ_LIB_ANSI_OUTPUT THEN PRINT nil$; - nil$ = nil$ + ansi_locate(ch, cw) - nil$ = nil$ + ansi_move_left(1) + "Moved left by one char." - nil$ = nil$ + ansi_move_left(5) + "Moved left by 5 chars." + nil$ = nil$ + ANSI.locate(ch, cw) + nil$ = nil$ + ANSI.move_left(1) + "Moved left by one char." + nil$ = nil$ + ANSI.move_left(5) + "Moved left by 5 chars." nil$ = nil$ + "_" IF GJ_LIB_ANSI_OUTPUT THEN PRINT nil$; - anykey : test_complete + CALL anykey : CALL test_complete END SUB -SUB test_ansi_move_right () +SUB test_ansi_move_right() CLS - PRINT "Press a key to test ansi cursor move right with ansi_move_right()..." - anykey - nil$ = ansi_locate(ch, cw) + "Line is here in the middle." + PRINT "Press a key to test ansi cursor move right with ANSI.move_right()..." + CALL anykey + nil$ = ANSI.locate(ch, cw) + "Line is here in the middle." IF GJ_LIB_ANSI_OUTPUT THEN PRINT nil$; - nil$ = nil$ + ansi_locate(ch, cw) - nil$ = nil$ + ansi_move_right(1) + "Moved right by one char." - nil$ = nil$ + ansi_move_right(5) + "Moved right by 5 chars." + nil$ = nil$ + ANSI.locate(ch, cw) + nil$ = nil$ + ANSI.move_right(1) + "Moved right by one char." + nil$ = nil$ + ANSI.move_right(5) + "Moved right by 5 chars." nil$ = nil$ + "_" IF GJ_LIB_ANSI_OUTPUT THEN PRINT nil$; - anykey : test_complete + CALL anykey : CALL test_complete END SUB -SUB test_ansi_move_down () +SUB test_ansi_move_down() CLS - PRINT "Press a key to test ansi cursor move down with ansi_move_down()..." - anykey - nil$ = ansi_locate(ch, cw) + "Line is here in the middle." + PRINT "Press a key to test ansi cursor move down with ANSI.move_down()..." + CALL anykey + nil$ = ANSI.locate(ch, cw) + "Line is here in the middle." IF GJ_LIB_ANSI_OUTPUT THEN PRINT nil$; - nil$ = nil$ + ansi_locate(ch, cw) - nil$ = nil$ + ansi_move_down(1) + "Line is one line down." - nil$ = nil$ + ansi_move_down(5) + "Line is six lines down." + nil$ = nil$ + ANSI.locate(ch, cw) + nil$ = nil$ + ANSI.move_down(1) + "Line is one line down." + nil$ = nil$ + ANSI.move_down(5) + "Line is six lines down." nil$ = nil$ + "_" IF GJ_LIB_ANSI_OUTPUT THEN PRINT nil$; - anykey : test_complete + CALL anykey : CALL test_complete END SUB -SUB test_ansi_move_up () +SUB test_ansi_move_up() CLS - PRINT "Press a key to test ansi cursor move up with ansi_move_up()..." - anykey - nil$ = ansi_locate(ch, cw) + "Line is here in the middle." - nil$ = nil$ + ansi_locate(ch, cw) - nil$ = nil$ + ansi_move_up(1) + "Line is one line up." - nil$ = nil$ + ansi_move_up(5) + "Line is six lines up." + PRINT "Press a key to test ansi cursor move up with ANSI.move_up()..." + CALL anykey + nil$ = ANSI.locate(ch, cw) + "Line is here in the middle." + nil$ = nil$ + ANSI.locate(ch, cw) + nil$ = nil$ + ANSI.move_up(1) + "Line is one line up." + nil$ = nil$ + ANSI.move_up(5) + "Line is six lines up." nil$ = nil$ + "_" IF GJ_LIB_ANSI_OUTPUT THEN PRINT nil$; - anykey : test_complete + CALL anykey : CALL test_complete END SUB -SUB test_ansi_locate () - PRINT "Press a key to locate to corners of the screen with ansi_locate()..."; - anykey +SUB test_ansi_locate() + DIM AS STRING w, h + PRINT "Press a key to locate to corners of the screen with ANSI.locate()..."; + CALL anykey CLS - w$ = _TRIM$(STR$(w-1)) - h$ = _TRIM$(STR$(h)) - nil$ = ansi_locate(ch, cw) + "A = 1,1" - nil$ = nil$ + ansi_locate(ch + 1, cw) + "B = 1," + w$ - nil$ = nil$ + ansi_locate(ch + 2, cw) + "C = " + h$ + ",1" - nil$ = nil$ + ansi_locate(ch + 3, cw) + "D = " + w$ + "," + h$ - nil$ = nil$ + ansi_locate(1,1) + "A" - nil$ = nil$ + ansi_locate(1,w-1) + "B" - nil$ = nil$ + ansi_locate(h,1) + "C" - nil$ = nil$ + ansi_locate(h,w-1) + "D" + w$ = _TRIM$(STR$(w%-1)) + h$ = _TRIM$(STR$(h%)) + nil$ = ANSI.locate(ch%, cw%) + "A = 1,1" + nil$ = nil$ + ANSI.locate(ch% + 1, cw%) + "B = 1," + w$ + nil$ = nil$ + ANSI.locate(ch% + 2, cw%) + "C = " + h$ + ",1" + nil$ = nil$ + ANSI.locate(ch% + 3, cw%) + "D = " + w$ + "," + h$ + nil$ = nil$ + ANSI.locate(1, 1) + "A" + nil$ = nil$ + ANSI.locate(1, w% - 1) + "B" + nil$ = nil$ + ANSI.locate(h%, 1) + "C" + nil$ = nil$ + ANSI.locate(h%, w% - 1) + "D" nil$ = nil$ + "_" IF GJ_LIB_ANSI_OUTPUT THEN PRINT nil$; - anykey : test_complete + CALL anykey : CALL test_complete END SUB -SUB test_ansi_home () - fill_screen_with_text "*" - PRINT "Press any key to home the cursor with ansi_home()..."; - anykey - nil$ = ansi_home +SUB test_ansi_home() + CALL fill_screen_with_text("*") + PRINT "Press any key to home the cursor with ANSI.home()..."; + CALL anykey + nil$ = ANSI.home nil$ = nil$ + "_" IF GJ_LIB_ANSI_OUTPUT THEN PRINT nil$; - anykey : test_complete + CALL anykey : CALL test_complete END SUB -SUB fill_screen_with_text (char$) +SUB fill_screen_with_text(char$) + DIM AS INTEGER y CLS - LOCATE 1,1 - FOR y = 0 TO _HEIGHT(0) + LOCATE 1, 1 + FOR y% = 0 TO _HEIGHT(0) PRINT STRING$(_WIDTH(0) - 1, char$) - NEXT y + NEXT y% END SUB diff --git a/ANSI/ANSI.BI b/ANSI/ANSI.BI index d4c2ca3..ff2ce38 100644 --- a/ANSI/ANSI.BI +++ b/ANSI/ANSI.BI @@ -19,9 +19,9 @@ $END IF ' Emulate ANSI functionality using QB internals while outputing ANSI codes? ' Default = FALSE $IF ANSI_DEBUGGING = UNDEFINED THEN - CONST ANSI_ESC=27 + CONST ANSI.ESC=27 $ELSE - CONST ANSI_ESC=32 + CONST ANSI.ESC=32 $END IF DIM SHARED GJ_LIB_ANSI_EMU AS INTEGER @@ -30,21 +30,21 @@ GJ_LIB_ANSI_EMU = FALSE DIM SHARED GJ_LIB_ANSI_OUTPUT AS INTEGER GJ_LIB_ANSI_OUTPUT = TRUE -DIM SHARED ansi_x% ' Cursor x position -DIM SHARED ansi_y% ' Cursor y position -ansi_x% = POS(0) -ansi_y% = CSRLIN -DIM SHARED save_x% ' Cursor x position -DIM SHARED save_y% ' Cursor y position -save_x% = POS(0) -save_y% = CSRLIN +DIM SHARED ANSI.x% ' Cursor x position +DIM SHARED ANSI.y% ' Cursor y position +ANSI.x% = POS(0) +ANSI.y% = CSRLIN +DIM SHARED ANSI.save_x% ' Cursor x position +DIM SHARED ANSI.save_y% ' Cursor y position +ANSI.save_x% = POS(0) +ANSI.save_y% = CSRLIN -DIM SHARED fg_color& ' Foreground color -DIM SHARED bg_color& ' Background color -DIM SHARED old_fg_color& ' Old Foreground color -DIM SHARED old_bg_color& ' Old Background color +DIM SHARED ANSI.fg_color& ' Foreground color +DIM SHARED ANSI.bg_color& ' Background color +DIM SHARED ANSI.old_fg_color& ' Old Foreground color +DIM SHARED ANSI.old_bg_color& ' Old Background color -fg_color& = 7 -bg_color& = 0 -old_fg_color& = fg_color& -old_bg_color& = bg_color& +ANSI.fg_color& = 7 +ANSI.bg_color& = 0 +ANSI.old_fg_color& = ANSI.fg_color& +ANSI.old_bg_color& = ANSI.bg_color& diff --git a/ANSI/ANSI.BM b/ANSI/ANSI.BM index 0f690b5..f36ed1c 100644 --- a/ANSI/ANSI.BM +++ b/ANSI/ANSI.BM @@ -13,97 +13,6 @@ ' There is a QB64 bug on MacOS/Linux where $CONSOLE:ONLY does not read input ' in the same way as on Windows. This bug is described here: ' https://github.com/QB64Official/qb64/issues/33 -' -' CURSOR VISIBILITY AND MOVEMENT: -' FUNCTION NOTES -' ansi_delay$ Creates a delay of n seconds -' ansi_hide_cursor$ Hides cursor -' ansi_show_cursor$ Shows cursor -' ansi_home$ Moves cursor to home position (0,0) -' ansi_locate$ Moves cursor to desired row and column -' ansi_move_up$ Moves cursor up n lines -' ansi_move_down$ Moves cursor down n lines -' ansi_move_right$ Moves cursor right n lines -' ansi_move_left$ Moves cursor left n lines -' ansi_move_lines_down$ Moves cursor to beginning of next line, n lines down -' ansi_move_lines_up$ Moves cursor to beginning of next line, n lines up -' ansi_move_column$ Moves cursor to column position n -' ansi_move_line_up$ Moves cursor one one line up, scrolling if needed -' ansi_save_pos$ Save cursor position -' ansi_restore_pos$ Restore cursor position -' -' ERASE: -' FUNCTION NOTES -' ansi_erase_to_eos$ Erase from cursor to end of screen -' ansi_erase_to_bos$ Erase from cursor to beginning of screen -' ansi_erase_screen$ Erase entire screen -' ansi_erase_to_eol$ Erase from cursor to end of line -' ansi_erase_from_sol$ Erase from start of line to cursor -' ansi_erase_line$ Erase line -' -' MODES: -' FUNCTION NOTES -' ansi_mode_reset_all$ Reset all modes -' ansi_mode_bold$ Set bold mode -' ansi_mode_bold_reset$ Reset bold mode -' ansi_mode_dim$ Set dim mode -' ansi_mode_dim_reset$ Reset dim mode -' ansi_mode_italic$ Set italic mode -' ansi_mode_italic_reset$ Reset italic mode -' ansi_mode_underline$ Set underline mode -' ansi_mode_underline_reset$ Reset underline mode -' ansi_mode_blinking$ Set blinking mode -' ansi_mode_blinking_reset$ Reset blinking mode -' ansi_mode_inverse$ Set inverse mode -' ansi_mode_inverse_reset$ Reset inverse mode -' ansi_mode_invisible$ Set invisible mode -' ansi_mode_invisible_reset$ Reset invisible mode -' ansi_mode_strikethrough$ Set strikethrough mode -' ansi_mode_strikethrough_reset$ Reset strikethrough mode -' -' STANDARD COLORS: -' FUNCTION NOTES -' ansi_fg_reset$ Reset foreground color -' ansi_bg_reset$ Reset background color -' ansi_fg_black$ Set foreground color to black -' ansi_fg_blue$ Set foreground color to blue -' ansi_fg_green$ Set foreground color to green -' ansi_fg_cyan$ Set foreground color to cyan -' ansi_fg_red$ Set foreground color to red -' ansi_fg_magenta$ Set foreground color to magenta -' ansi_fg_yellow$ Set foreground color to yellow -' ansi_fg_white$ Set foreground color to white -' ansi_fg_bright_black$ Set foreground color to black -' ansi_fg_bright_blue$ Set foreground color to blue -' ansi_fg_bright_green$ Set foreground color to green -' ansi_fg_bright_cyan$ Set foreground color to cyan -' ansi_fg_bright_red$ Set foreground color to red -' ansi_fg_bright_magenta$ Set foreground color to magenta -' ansi_fg_bright_yellow$ Set foreground color to yellow -' ansi_fg_bright_white$ Set foreground color to white -' ansi_bg_black$ Set background color to black -' ansi_bg_blue$ Set background color to blue -' ansi_bg_green$ Set background color to green -' ansi_bg_cyan$ Set background color to cyan -' ansi_bg_red$ Set background color to red -' ansi_bg_magenta$ Set background color to magenta -' ansi_bg_yellow$ Set background color to yellow -' ansi_bg_white$ Set background color to white -' ansi_bg_bright_black$ Set background color to black -' ansi_bg_bright_blue$ Set background color to blue -' ansi_bg_bright_green$ Set background color to green -' ansi_bg_bright_cyan$ Set background color to cyan -' ansi_bg_bright_red$ Set background color to red -' ansi_bg_bright_magenta$ Set background color to magenta -' ansi_bg_bright_yellow$ Set background color to yellow -' ansi_bg_bright_white$ Set background color to white -' -' 8-BIT (256) COLORS: -' FUNCTION NOTES -' ansi_fg_256$ Sets text foreground color using 256 color mode -' ansi_bg_256$ Sets text background color using 256 color mode -' ansi_fg_rgb$ Sets text color foreground using RGB 8-bit mode -' ansi_bg_rgb$ Sets text color background using RGB 8-bit mode ' ' @author Rick Christy ' @uses ANSI.BI @@ -116,13 +25,14 @@ $LET GJ_LIB_ANSI_INC_BM = 1 '' ' Clamps a value from going below 0 ' -' @return void +' @param INTEGER var% to clamp to zero +' @return INTEGER var clamped to 0 or more ' -FUNCTION clamp_zero% (var%) +FUNCTION ANSI.clamp_zero%(var%) IF var% < 0 THEN - clamp_zero% = 0 + ANSI.clamp_zero% = 0 ELSE - clamp_zero% = var% + ANSI.clamp_zero% = var% END IF END FUNCTION @@ -130,10 +40,10 @@ END FUNCTION '' ' Safely locates within ranges 1+ on row and col ' -' @param row% row for locate -' @param col% col for locate +' @param INTEGER row% for locate +' @param INTEGER col% for locate ' -SUB safe_locate (row%, col%) +SUB ANSI.safe_locate(row%, col%) IF row% <= 0 THEN row% = 1 IF col% <= 0 THEN col% = 1 LOCATE row%, col% @@ -143,9 +53,9 @@ END SUB '' ' Safely locates within ranges 1+ on col ' -' @param col% col for locate +' @param INTEGER col% for locate ' -SUB safe_locate_x (col%) +SUB ANSI.safe_locate_x(col%) IF col% <= 0 THEN col% = 1 LOCATE , col% END SUB @@ -154,9 +64,9 @@ END SUB '' ' Safely locates within ranges 1+ on row ' -' @param row% row for locate +' @param INTEGER row% for locate ' -SUB safe_locate_y (row%) +SUB ANSI.safe_locate_y(row%) IF row% <= 0 THEN row% = 1 LOCATE row% END SUB @@ -165,1088 +75,1088 @@ END SUB '' ' Hides cursor ' -' @return string with ANSI escape codes to hide cursor +' @return STRING with ANSI escape codes to hide cursor ' -FUNCTION ansi_hide_cursor$ () +FUNCTION ANSI.hide_cursor$() DIM AS STRING sout - sout$ = CHR$(ANSI_ESC) + "[?25l" + sout$ = CHR$(ANSI.ESC) + "[?25l" IF GJ_LIB_ANSI_EMU THEN LOCATE ,,0 - IF GJ_LIB_ANSI_OUTPUT THEN ansi_hide_cursor$ = sout$ + IF GJ_LIB_ANSI_OUTPUT THEN ANSI.hide_cursor$ = sout$ END FUNCTION '' ' Shows cursor ' -' @return string with ANSI escape codes to show cursor +' @return STRING with ANSI escape codes to show cursor ' -FUNCTION ansi_show_cursor$ () +FUNCTION ANSI.show_cursor$() DIM AS STRING sout - sout$ = CHR$(ANSI_ESC) + "[?25h" + sout$ = CHR$(ANSI.ESC) + "[?25h" IF GJ_LIB_ANSI_EMU THEN LOCATE ,,1 - IF GJ_LIB_ANSI_OUTPUT THEN ansi_show_cursor$ = sout$ + IF GJ_LIB_ANSI_OUTPUT THEN ANSI.show_cursor$ = sout$ END FUNCTION '' ' Moves cursor to home position (0,0) ' -' @return string with ANSI escape codes to move cursor +' @return STRING with ANSI escape codes to move cursor ' -FUNCTION ansi_home$ () +FUNCTION ANSI.home$() DIM AS STRING sout - sout$ = CHR$(ANSI_ESC) + "[H" - ansi_x% = 0 : ansi_y% = 0 - IF GJ_LIB_ANSI_EMU THEN safe_locate ansi_y%, ansi_x% - IF GJ_LIB_ANSI_OUTPUT THEN ansi_home$ = sout$ + sout$ = CHR$(ANSI.ESC) + "[H" + ANSI.x% = 0 : ANSI.y% = 0 + IF GJ_LIB_ANSI_EMU THEN CALL ANSI.safe_locate(ANSI.y%, ANSI.x%) + IF GJ_LIB_ANSI_OUTPUT THEN ANSI.home$ = sout$ END FUNCTION '' ' Moves cursor to desired row and column ' -' @param row% Row to move cursor to -' @param col% Column to move cursor to -' @return string with ANSI escape codes to move cursor +' @param INTEGER row% to move cursor to +' @param INTEGER col% to move cursor to +' @return STRING with ANSI escape codes to move cursor ' -FUNCTION ansi_locate$ (row%, col%) +FUNCTION ANSI.locate$(row%, col%) DIM AS STRING sout - sout$ = CHR$(ANSI_ESC) + "[" + sout$ = CHR$(ANSI.ESC) + "[" sout$ = sout$ + _TRIM$(STR$(row%)) + ";" sout$ = sout$ + _TRIM$(STR$(col%)) + "H" - ansi_x% = col% : ansi_y% = row% - IF GJ_LIB_ANSI_EMU THEN safe_locate ansi_y%, ansi_x% - IF GJ_LIB_ANSI_OUTPUT THEN ansi_locate$ = sout$ + ANSI.x% = col% : ANSI.y% = row% + IF GJ_LIB_ANSI_EMU THEN CALL ANSI.safe_locate(ANSI.y%, ANSI.x%) + IF GJ_LIB_ANSI_OUTPUT THEN ANSI.locate$ = sout$ END FUNCTION '' ' Moves cursor up n lines ' -' @param n% Number of lines to move cursor up -' @return string with ANSI escape codes to move cursor +' @param INTEGER n% Number of lines to move cursor up +' @return STRING with ANSI escape codes to move cursor ' -FUNCTION ansi_move_up$ (n%) +FUNCTION ANSI.move_up$(n%) DIM AS STRING sout - sout$ = CHR$(ANSI_ESC) + "[" + sout$ = CHR$(ANSI.ESC) + "[" sout$ = sout$ + _TRIM$(STR$(n%)) + "A" - ansi_y% = clamp_zero(ansi_y% - n%) - IF GJ_LIB_ANSI_EMU THEN safe_locate_y ansi_y% - IF GJ_LIB_ANSI_OUTPUT THEN ansi_move_up$ = sout$ + ANSI.y% = ANSI.clamp_zero(ANSI.y% - n%) + IF GJ_LIB_ANSI_EMU THEN CALL ANSI.safe_locate_y(ANSI.y%) + IF GJ_LIB_ANSI_OUTPUT THEN ANSI.move_up$ = sout$ END FUNCTION '' ' Moves cursor down n lines ' -' @param n% Number of lines to move cursor down -' @return string with ANSI escape codes to move cursor +' @param INTEGER n% Number of lines to move cursor down +' @return STRING with ANSI escape codes to move cursor ' -FUNCTION ansi_move_down$ (n%) +FUNCTION ANSI.move_down$(n%) DIM AS STRING sout - sout$ = CHR$(ANSI_ESC) + "[" + sout$ = CHR$(ANSI.ESC) + "[" sout$ = sout$ + _TRIM$(STR$(n%)) + "B" - ansi_y% = ansi_y% + n% - IF GJ_LIB_ANSI_EMU THEN safe_locate_y ansi_y% - IF GJ_LIB_ANSI_OUTPUT THEN ansi_move_down$ = sout$ + ANSI.y% = ANSI.y% + n% + IF GJ_LIB_ANSI_EMU THEN CALL ANSI.safe_locate_y(ANSI.y%) + IF GJ_LIB_ANSI_OUTPUT THEN ANSI.move_down$ = sout$ END FUNCTION '' ' Moves cursor right n lines ' -' @param n% Number of lines to move cursor right -' @return string with ANSI escape codes to move cursor +' @param INTEGER n% Number of lines to move cursor right +' @return STRING with ANSI escape codes to move cursor ' -FUNCTION ansi_move_right$ (n%) +FUNCTION ANSI.move_right$(n%) DIM AS STRING sout - sout$ = CHR$(ANSI_ESC) + "[" + sout$ = CHR$(ANSI.ESC) + "[" sout$ = sout$ + _TRIM$(STR$(n%)) + "C" - ansi_x% = ansi_x% + n% - IF GJ_LIB_ANSI_EMU THEN safe_locate_x ansi_y% - IF GJ_LIB_ANSI_OUTPUT THEN ansi_move_right$ = sout$ + ANSI.x% = ANSI.x% + n% + IF GJ_LIB_ANSI_EMU THEN CALL ANSI.safe_locate_x(ANSI.x%) + IF GJ_LIB_ANSI_OUTPUT THEN ANSI.move_right$ = sout$ END FUNCTION '' ' Moves cursor left n lines ' -' @param n% Number of lines to move cursor left -' @return string with ANSI escape codes to move cursor +' @param INTEGER n% Number of lines to move cursor left +' @return STRING with ANSI escape codes to move cursor ' -FUNCTION ansi_move_left$ (n%) +FUNCTION ANSI.move_left$(n%) DIM AS STRING sout - sout$ = CHR$(ANSI_ESC) + "[" + sout$ = CHR$(ANSI.ESC) + "[" sout$ = sout$ + _TRIM$(STR$(n%)) + "D" - ansi_x% = clamp_zero(ansi_x% - n%) - IF GJ_LIB_ANSI_EMU THEN safe_locate_x ansi_x% - IF GJ_LIB_ANSI_OUTPUT THEN ansi_move_left$ = sout$ + ANSI.x% = ANSI.clamp_zero(ANSI.x% - n%) + IF GJ_LIB_ANSI_EMU THEN CALL ANSI.safe_locate_x(ANSI.x%) + IF GJ_LIB_ANSI_OUTPUT THEN ANSI.move_left$ = sout$ END FUNCTION '' ' Moves cursor to beginning of next line, n lines down ' -' @param n% Number of lines to move cursor down -' @return string with ANSI escape codes to move cursor +' @param INTEGER n% Number of lines to move cursor down +' @return STRING with ANSI escape codes to move cursor ' -FUNCTION ansi_move_lines_down$ (n%) +FUNCTION ANSI.move_lines_down$(n%) DIM AS STRING sout - sout$ = CHR$(ANSI_ESC) + "[" + sout$ = CHR$(ANSI.ESC) + "[" sout$ = sout$ + _TRIM$(STR$(n%)) + "E" - ansi_y% = ansi_y% + n% - IF GJ_LIB_ANSI_EMU THEN safe_locate_y ansi_y% - IF GJ_LIB_ANSI_OUTPUT THEN ansi_move_lines_down$ = sout$ + ANSI.y% = ANSI.y% + n% + IF GJ_LIB_ANSI_EMU THEN CALL ANSI.safe_locate_y(ANSI.y%) + IF GJ_LIB_ANSI_OUTPUT THEN ANSI.move_lines_down$ = sout$ END FUNCTION '' ' Moves cursor to beginning of previous line, n lines up ' -' @param n% Number of lines to move cursor up -' @return string with ANSI escape codes to move cursor +' @param INTEGER n% Number of lines to move cursor up +' @return STRING with ANSI escape codes to move cursor ' -FUNCTION ansi_move_lines_up$ (n%) +FUNCTION ANSI.move_lines_up$(n%) DIM AS STRING sout - sout$ = CHR$(ANSI_ESC) + "[" + sout$ = CHR$(ANSI.ESC) + "[" sout$ = sout$ + _TRIM$(STR$(n%)) + "F" - ansi_y% = clamp_zero(ansi_y% - n%) - IF GJ_LIB_ANSI_EMU THEN safe_locate_y ansi_y% - IF GJ_LIB_ANSI_OUTPUT THEN ansi_move_lines_up$ = sout$ + ANSI.y% = ANSI.clamp_zero(ANSI.y% - n%) + IF GJ_LIB_ANSI_EMU THEN CALL ANSI.safe_locate_y(ANSI.y%) + IF GJ_LIB_ANSI_OUTPUT THEN ANSI.move_lines_up$ = sout$ END FUNCTION '' ' Moves cursor to column position n ' -' @param n% Column to move cursor to -' @return string with ANSI escape codes to move cursor +' @param INTEGER n% Column to move cursor to +' @return STRING with ANSI escape codes to move cursor ' -FUNCTION ansi_move_column$ (n%) +FUNCTION ANSI.move_column$(n%) DIM AS STRING sout - sout$ = CHR$(ANSI_ESC) + "[" + sout$ = CHR$(ANSI.ESC) + "[" sout$ = sout$ + _TRIM$(STR$(n%)) + "G" - ansi_x% = n% - IF GJ_LIB_ANSI_EMU THEN safe_locate_x ansi_x% - IF GJ_LIB_ANSI_OUTPUT THEN ansi_move_column$ = sout$ + ANSI.x% = n% + IF GJ_LIB_ANSI_EMU THEN CALL ANSI.safe_locate_x(ANSI.x%) + IF GJ_LIB_ANSI_OUTPUT THEN ANSI.move_column$ = sout$ END FUNCTION '' ' Moves cursor one line up, scrolling if needed ' -' @return string with ANSI escape codes to move cursor +' @return STRING with ANSI escape codes to move cursor ' -FUNCTION ansi_move_line_up$ () +FUNCTION ANSI.move_line_up$() DIM AS STRING sout - sout$ = CHR$(ANSI_ESC) + "M" - ansi_y% = clamp_zero(ansi_y% - 1) - IF GJ_LIB_ANSI_EMU THEN safe_locate_y ansi_y% - IF GJ_LIB_ANSI_OUTPUT THEN ansi_move_line_up$ = sout$ + sout$ = CHR$(ANSI.ESC) + "M" + ANSI.y% = ANSI.clamp_zero(ANSI.y% - 1) + IF GJ_LIB_ANSI_EMU THEN CALL ANSI.safe_locate_y(ANSI.y%) + IF GJ_LIB_ANSI_OUTPUT THEN ANSI.move_line_up$ = sout$ END FUNCTION '' ' Save cursor position ' -' @return string with ANSI escape codes +' @return STRING with ANSI escape codes ' -FUNCTION ansi_save_pos$ () +FUNCTION ANSI.save_pos$() DIM AS STRING sout - sout$ = CHR$(ANSI_ESC) + "[s" - save_x% = ansi_x% : save_y% = ansi_y% - IF GJ_LIB_ANSI_OUTPUT THEN ansi_save_pos$ = sout$ + sout$ = CHR$(ANSI.ESC) + "[s" + ANSI.save_x% = ANSI.x% : ANSI.save_y% = ANSI.y% + IF GJ_LIB_ANSI_OUTPUT THEN ANSI.save_pos$ = sout$ END FUNCTION '' ' Restore cursor position ' -' @return string with ANSI escape codes +' @return STRING with ANSI escape codes ' -FUNCTION ansi_restore_pos$ () +FUNCTION ANSI.restore_pos$() DIM AS STRING sout, nil - sout$ = CHR$(ANSI_ESC) + "[u" - nil$ = ansi_locate(save_y%, save_x%) - IF GJ_LIB_ANSI_EMU THEN safe_locate save_y%, save_x% - IF GJ_LIB_ANSI_OUTPUT THEN ansi_restore_pos$ = sout$ + sout$ = CHR$(ANSI.ESC) + "[u" + nil$ = ANSI.locate(ANSI.save_y%, ANSI.save_x%) + IF GJ_LIB_ANSI_EMU THEN CALL ANSI.safe_locate(ANSI.save_y%, ANSI.save_x%) + IF GJ_LIB_ANSI_OUTPUT THEN ANSI.restore_pos$ = sout$ END FUNCTION '' ' Erase from cursor to end of screen ' -' @return string with ANSI escape codes +' @return STRING with ANSI escape codes ' -FUNCTION ansi_erase_to_eos$ () +FUNCTION ANSI.erase_to_eos$() DIM AS STRING sout DIM AS INTEGER w, h, x, y, row - sout$ = CHR$(ANSI_ESC) + "[0J" + sout$ = CHR$(ANSI.ESC) + "[0J" IF GJ_LIB_ANSI_EMU THEN w = _WIDTH h = _HEIGHT - x = ansi_x% - y = ansi_y% + x = ANSI.x% + y = ANSI.y% PRINT SPC(w-x) FOR row = y TO h LOCATE row, 1 PRINT SPC(w) NEXT row - safe_locate ansi_y%, ansi_x% + CALL ANSI.safe_locate(ANSI.y%, ANSI.x%) END IF - IF GJ_LIB_ANSI_OUTPUT THEN ansi_erase_to_eos$ = sout$ + IF GJ_LIB_ANSI_OUTPUT THEN ANSI.erase_to_eos$ = sout$ END FUNCTION '' ' Erase from cursor to beginning of screen ' -' @return string with ANSI escape codes +' @return STRING with ANSI escape codes ' -FUNCTION ansi_erase_to_bos$ () +FUNCTION ANSI.erase_to_bos$() DIM AS STRING sout DIM AS INTEGER w, h, row - sout$ = CHR$(ANSI_ESC) + "[1J" + sout$ = CHR$(ANSI.ESC) + "[1J" IF GJ_LIB_ANSI_EMU THEN w = _WIDTH h = _HEIGHT LOCATE ,1 - PRINT SPC(ansi_x%-1) + PRINT SPC(ANSI.x%-1) FOR row = h TO 1 STEP - 1 LOCATE row, 1 PRINT SPC(w) NEXT row - safe_locate ansi_y%, ansi_x% + CALL ANSI.safe_locate(ANSI.y%, ANSI.x%) END IF - IF GJ_LIB_ANSI_OUTPUT THEN ansi_erase_to_bos$ = sout$ + IF GJ_LIB_ANSI_OUTPUT THEN ANSI.erase_to_bos$ = sout$ END FUNCTION '' ' Erase entire screen ' -' @return string with ANSI escape codes +' @return STRING with ANSI escape codes ' -FUNCTION ansi_erase_screen$ () +FUNCTION ANSI.erase_screen$() DIM AS STRING sout - sout$ = CHR$(ANSI_ESC) + "[2J" - sout$ = sout$ + ansi_locate(1,1) - ansi_x% = 0 : ansi_y% = 0 + sout$ = CHR$(ANSI.ESC) + "[2J" + sout$ = sout$ + ANSI.locate(1,1) + ANSI.x% = 0 : ANSI.y% = 0 IF GJ_LIB_ANSI_EMU THEN - safe_locate ansi_y%, ansi_x% + CALL ANSI.safe_locate(ANSI.y%, ANSI.x%) CLS END IF - IF GJ_LIB_ANSI_OUTPUT THEN ansi_erase_screen$ = sout$ + IF GJ_LIB_ANSI_OUTPUT THEN ANSI.erase_screen$ = sout$ END FUNCTION '' ' Erase from cursor to end of line ' -' @return string with ANSI escape codes +' @return STRING with ANSI escape codes ' -FUNCTION ansi_erase_to_eol$ () +FUNCTION ANSI.erase_to_eol$() DIM AS STRING sout DIM AS INTEGER w - sout$ = CHR$(ANSI_ESC) + "[0K" + sout$ = CHR$(ANSI.ESC) + "[0K" IF GJ_LIB_ANSI_EMU THEN w = _WIDTH - PRINT SPC(w-ansi_x%) - safe_locate ansi_y%, ansi_x% + PRINT SPC(w-ANSI.x%) + CALL ANSI.safe_locate(ANSI.y%, ANSI.x%) END IF - IF GJ_LIB_ANSI_OUTPUT THEN ansi_erase_to_eol$ = sout$ + IF GJ_LIB_ANSI_OUTPUT THEN ANSI.erase_to_eol$ = sout$ END FUNCTION '' ' Erase from start of line to cursor ' -' @return string with ANSI escape codes +' @return STRING with ANSI escape codes ' -FUNCTION ansi_erase_from_sol$ () +FUNCTION ANSI.erase_from_sol$() DIM AS STRING sout DIM AS INTEGER w - sout$ = CHR$(ANSI_ESC) + "[1K" + sout$ = CHR$(ANSI.ESC) + "[1K" IF GJ_LIB_ANSI_EMU THEN w = _WIDTH - safe_locate ansi_y%, ansi_x% - PRINT SPC(w-ansi_x%) + CALL ANSI.safe_locate(ANSI.y%, ANSI.x%) + PRINT SPC(w-ANSI.x%) END IF - IF GJ_LIB_ANSI_OUTPUT THEN ansi_erase_from_sol$ = sout$ + IF GJ_LIB_ANSI_OUTPUT THEN ANSI.erase_from_sol$ = sout$ END FUNCTION '' ' Erase line ' -' @return string with ANSI escape codes +' @return STRING with ANSI escape codes ' -FUNCTION ansi_erase_line$ () +FUNCTION ANSI.erase_line$() DIM AS STRING sout DIM AS INTEGER w, x, y - sout$ = CHR$(ANSI_ESC) + "[2K" + sout$ = CHR$(ANSI.ESC) + "[2K" IF GJ_LIB_ANSI_EMU THEN w = _WIDTH - x = ansi_x% - y = ansi_y% + x = ANSI.x% + y = ANSI.y% LOCATE ,1 PRINT SPC(w) - safe_locate ansi_y%, ansi_x% + CALL ANSI.safe_locate(ANSI.y%, ANSI.x%) END IF - IF GJ_LIB_ANSI_OUTPUT THEN ansi_erase_line$ = sout$ + IF GJ_LIB_ANSI_OUTPUT THEN ANSI.erase_line$ = sout$ END FUNCTION '' ' Reset modes ' -' @return string with ANSI escape codes for resetting 1modes +' @return STRING with ANSI escape codes for resetting 1modes ' -FUNCTION ansi_mode_reset_all$ () +FUNCTION ANSI.mode_reset_all$() DIM AS STRING sout - sout$ = CHR$(ANSI_ESC) + "[0m" + sout$ = CHR$(ANSI.ESC) + "[0m" IF GJ_LIB_ANSI_EMU THEN - fg_color& = 7 : bg_color& = 0 - COLOR fg_color&, bg_color& + ANSI.fg_color& = 7 : ANSI.bg_color& = 0 + COLOR ANSI.fg_color&, ANSI.bg_color& _BLINK ON END IF - IF GJ_LIB_ANSI_OUTPUT THEN ansi_mode_reset_all$ = sout$ + IF GJ_LIB_ANSI_OUTPUT THEN ANSI.mode_reset_all$ = sout$ END FUNCTION '' ' Set bold mode ' -' @return string with ANSI escape codes for setting mode +' @return STRING with ANSI escape codes for setting mode ' -FUNCTION ansi_mode_bold$ () +FUNCTION ANSI.mode_bold$() DIM AS STRING sout - sout$ = CHR$(ANSI_ESC) + "[1m" + sout$ = CHR$(ANSI.ESC) + "[1m" IF GJ_LIB_ANSI_EMU THEN - IF fg_color& <= 7 THEN fg_color& = fg_color& + 8 - COLOR fg_color& + IF ANSI.fg_color& <= 7 THEN ANSI.fg_color& = ANSI.fg_color& + 8 + COLOR ANSI.fg_color& END IF - IF GJ_LIB_ANSI_OUTPUT THEN ansi_mode_bold$ = sout$ + IF GJ_LIB_ANSI_OUTPUT THEN ANSI.mode_bold$ = sout$ END FUNCTION '' ' Reset bold mode ' -' @return string with ANSI escape codes for resetting mode +' @return STRING with ANSI escape codes for resetting mode ' -FUNCTION ansi_mode_bold_reset$ () +FUNCTION ANSI.mode_bold_reset$() DIM AS STRING sout - sout$ = CHR$(ANSI_ESC) + "[22m" + sout$ = CHR$(ANSI.ESC) + "[22m" IF GJ_LIB_ANSI_EMU THEN - IF fg_color& >= 8 THEN fg_color& = fg_color& - 8 - COLOR fg_color& + IF ANSI.fg_color& >= 8 THEN ANSI.fg_color& = ANSI.fg_color& - 8 + COLOR ANSI.fg_color& END IF - IF GJ_LIB_ANSI_OUTPUT THEN ansi_mode_bold_reset$ = sout$ + IF GJ_LIB_ANSI_OUTPUT THEN ANSI.mode_bold_reset$ = sout$ END FUNCTION '' ' Set dim mode ' -' @return string with ANSI escape codes for setting mode +' @return STRING with ANSI escape codes for setting mode ' -FUNCTION ansi_mode_dim$ () +FUNCTION ANSI.mode_dim$() DIM AS STRING sout, nil - sout$ = CHR$(ANSI_ESC) + "[2m" - IF GJ_LIB_ANSI_EMU THEN nil$ = ansi_mode_bold_reset - IF GJ_LIB_ANSI_OUTPUT THEN ansi_mode_dim$ = sout$ + sout$ = CHR$(ANSI.ESC) + "[2m" + IF GJ_LIB_ANSI_EMU THEN nil$ = ANSI.mode_bold_reset + IF GJ_LIB_ANSI_OUTPUT THEN ANSI.mode_dim$ = sout$ END FUNCTION '' ' Reset dim mode ' -' @return string with ANSI escape codes for resetting mode +' @return STRING with ANSI escape codes for resetting mode ' -FUNCTION ansi_mode_dim_reset$ () +FUNCTION ANSI.mode_dim_reset$() DIM AS STRING sout, nil - sout$ = CHR$(ANSI_ESC) + "[22m" - IF GJ_LIB_ANSI_EMU THEN nil$ = ansi_mode_bold_reset - IF GJ_LIB_ANSI_OUTPUT THEN ansi_mode_dim_reset$ = sout$ + sout$ = CHR$(ANSI.ESC) + "[22m" + IF GJ_LIB_ANSI_EMU THEN nil$ = ANSI.mode_bold_reset + IF GJ_LIB_ANSI_OUTPUT THEN ANSI.mode_dim_reset$ = sout$ END FUNCTION '' ' Set italic mode ' -' @return string with ANSI escape codes for setting mode +' @return STRING with ANSI escape codes for setting mode ' -FUNCTION ansi_mode_italic$ () +FUNCTION ANSI.mode_italic$() DIM AS STRING sout, nil - sout$ = CHR$(ANSI_ESC) + "[3m" - IF GJ_LIB_ANSI_EMU THEN nil$ = ansi_mode_bold - IF GJ_LIB_ANSI_OUTPUT THEN ansi_mode_italic$ = sout$ + sout$ = CHR$(ANSI.ESC) + "[3m" + IF GJ_LIB_ANSI_EMU THEN nil$ = ANSI.mode_bold + IF GJ_LIB_ANSI_OUTPUT THEN ANSI.mode_italic$ = sout$ END FUNCTION '' ' Reset italic mode ' -' @return string with ANSI escape codes for resetting mode +' @return STRING with ANSI escape codes for resetting mode ' -FUNCTION ansi_mode_italic_reset$ () +FUNCTION ANSI.mode_italic_reset$() DIM AS STRING sout, nil - sout$ = CHR$(ANSI_ESC) + "[23m" - IF GJ_LIB_ANSI_EMU THEN nil$ = ansi_mode_bold_reset - IF GJ_LIB_ANSI_OUTPUT THEN ansi_mode_italic_reset$ = sout$ + sout$ = CHR$(ANSI.ESC) + "[23m" + IF GJ_LIB_ANSI_EMU THEN nil$ = ANSI.mode_bold_reset + IF GJ_LIB_ANSI_OUTPUT THEN ANSI.mode_italic_reset$ = sout$ END FUNCTION '' ' Set underline mode ' -' @return string with ANSI escape codes for setting mode +' @return STRING with ANSI escape codes for setting mode ' -FUNCTION ansi_mode_underline$ () +FUNCTION ANSI.mode_underline$() DIM AS STRING sout, nil - sout$ = CHR$(ANSI_ESC) + "[4m" - IF GJ_LIB_ANSI_EMU THEN nil$ = ansi_mode_bold - IF GJ_LIB_ANSI_OUTPUT THEN ansi_mode_underline$ = sout$ + sout$ = CHR$(ANSI.ESC) + "[4m" + IF GJ_LIB_ANSI_EMU THEN nil$ = ANSI.mode_bold + IF GJ_LIB_ANSI_OUTPUT THEN ANSI.mode_underline$ = sout$ END FUNCTION '' ' Reset underline mode ' -' @return string with ANSI escape codes for resetting mode +' @return STRING with ANSI escape codes for resetting mode ' -FUNCTION ansi_mode_underline_reset$ () +FUNCTION ANSI.mode_underline_reset$() DIM AS STRING sout, nil - sout$ = CHR$(ANSI_ESC) + "[24m" - IF GJ_LIB_ANSI_EMU THEN nil$ = ansi_mode_bold_reset - IF GJ_LIB_ANSI_OUTPUT THEN ansi_mode_underline_reset$ = sout$ + sout$ = CHR$(ANSI.ESC) + "[24m" + IF GJ_LIB_ANSI_EMU THEN nil$ = ANSI.mode_bold_reset + IF GJ_LIB_ANSI_OUTPUT THEN ANSI.mode_underline_reset$ = sout$ END FUNCTION '' ' Set blinking mode ' -' @return string with ANSI escape codes for setting mode +' @return STRING with ANSI escape codes for setting mode ' -FUNCTION ansi_mode_blinking$ () +FUNCTION ANSI.mode_blinking$() DIM AS STRING sout - sout$ = CHR$(ANSI_ESC) + "[5m" + sout$ = CHR$(ANSI.ESC) + "[5m" IF GJ_LIB_ANSI_EMU THEN _BLINK ON - IF GJ_LIB_ANSI_OUTPUT THEN ansi_mode_blinking$ = sout$ + IF GJ_LIB_ANSI_OUTPUT THEN ANSI.mode_blinking$ = sout$ END FUNCTION '' ' Reset blinking mode ' -' @return string with ANSI escape codes for resetting mode +' @return STRING with ANSI escape codes for resetting mode ' -FUNCTION ansi_mode_blinking_reset$ () +FUNCTION ANSI.mode_blinking_reset$() DIM AS STRING sout - sout$ = CHR$(ANSI_ESC) + "[25m" + sout$ = CHR$(ANSI.ESC) + "[25m" IF GJ_LIB_ANSI_EMU THEN _BLINK OFF - IF GJ_LIB_ANSI_OUTPUT THEN ansi_mode_blinking_reset$ = sout$ + IF GJ_LIB_ANSI_OUTPUT THEN ANSI.mode_blinking_reset$ = sout$ END FUNCTION '' ' Set inverse mode ' -' @return string with ANSI escape codes for setting mode +' @return STRING with ANSI escape codes for setting mode ' -FUNCTION ansi_mode_inverse$ () +FUNCTION ANSI.mode_inverse$() DIM AS STRING sout - sout$ = CHR$(ANSI_ESC) + "[?5h" - sout$ = sout$ + CHR$(ANSI_ESC) + "[7m" + sout$ = CHR$(ANSI.ESC) + "[?5h" + sout$ = sout$ + CHR$(ANSI.ESC) + "[7m" IF GJ_LIB_ANSI_EMU THEN - old_fg_color& = fg_color& - old_bg_color& = bg_color& - fg_color& = bg_color& - bg_color& = old_fg_color& - COLOR fg_color&, bg_color& + ANSI.old_fg_color& = ANSI.fg_color& + ANSI.old_bg_color& = ANSI.bg_color& + ANSI.fg_color& = ANSI.bg_color& + ANSI.bg_color& = ANSI.old_fg_color& + COLOR ANSI.fg_color&, ANSI.bg_color& END IF - IF GJ_LIB_ANSI_OUTPUT THEN ansi_mode_inverse$ = sout$ + IF GJ_LIB_ANSI_OUTPUT THEN ANSI.mode_inverse$ = sout$ END FUNCTION '' ' Reset inverse mode ' -' @return string with ANSI escape codes for resetting mode +' @return STRING with ANSI escape codes for resetting mode ' -FUNCTION ansi_mode_inverse_reset$ () +FUNCTION ANSI.mode_inverse_reset$() DIM AS STRING sout - sout$ = CHR$(ANSI_ESC) + "[?5l" - sout$ = sout$ + CHR$(ANSI_ESC) + "[27m" - IF GJ_LIB_ANSI_EMU THEN COLOR old_fg_color&, old_bg_color& - IF GJ_LIB_ANSI_OUTPUT THEN ansi_mode_inverse_reset$ = sout$ + sout$ = CHR$(ANSI.ESC) + "[?5l" + sout$ = sout$ + CHR$(ANSI.ESC) + "[27m" + IF GJ_LIB_ANSI_EMU THEN COLOR ANSI.old_fg_color&, ANSI.old_bg_color& + IF GJ_LIB_ANSI_OUTPUT THEN ANSI.mode_inverse_reset$ = sout$ END FUNCTION '' ' Set invisible mode ' -' @return string with ANSI escape codes for setting mode +' @return STRING with ANSI escape codes for setting mode ' -FUNCTION ansi_mode_invisible$ () +FUNCTION ANSI.mode_invisible$() DIM AS STRING sout - sout$ = CHR$(ANSI_ESC) + "[8m" + sout$ = CHR$(ANSI.ESC) + "[8m" IF GJ_LIB_ANSI_EMU THEN - old_fg_color& = fg_color& - old_bg_color& = bg_color& - fg_color& = bg_color& - COLOR fg_color&, bg_color& + ANSI.old_fg_color& = ANSI.fg_color& + ANSI.old_bg_color& = ANSI.bg_color& + ANSI.fg_color& = ANSI.bg_color& + COLOR ANSI.fg_color&, ANSI.bg_color& END IF - IF GJ_LIB_ANSI_OUTPUT THEN ansi_mode_invisible$ = sout$ + IF GJ_LIB_ANSI_OUTPUT THEN ANSI.mode_invisible$ = sout$ END FUNCTION '' ' Reset invisible mode ' -' @return string with ANSI escape codes for resetting mode +' @return STRING with ANSI escape codes for resetting mode ' -FUNCTION ansi_mode_invisible_reset$ () +FUNCTION ANSI.mode_invisible_reset$() DIM AS STRING sout - sout$ = CHR$(ANSI_ESC) + "[28m" - IF GJ_LIB_ANSI_EMU THEN COLOR old_fg_color&, old_bg_color& - IF GJ_LIB_ANSI_OUTPUT THEN ansi_mode_invisible_reset$ = sout$ + sout$ = CHR$(ANSI.ESC) + "[28m" + IF GJ_LIB_ANSI_EMU THEN COLOR ANSI.old_fg_color&, ANSI.old_bg_color& + IF GJ_LIB_ANSI_OUTPUT THEN ANSI.mode_invisible_reset$ = sout$ END FUNCTION '' ' Set strikethrough mode ' -' @return string with ANSI escape codes for setting mode +' @return STRING with ANSI escape codes for setting mode ' -FUNCTION ansi_mode_strikethrough$ () +FUNCTION ANSI.mode_strikethrough$() DIM AS STRING sout - sout$ = CHR$(ANSI_ESC) + "[9m" - IF GJ_LIB_ANSI_OUTPUT THEN ansi_mode_strikethrough$ = sout$ + sout$ = CHR$(ANSI.ESC) + "[9m" + IF GJ_LIB_ANSI_OUTPUT THEN ANSI.mode_strikethrough$ = sout$ END FUNCTION '' ' Reset strikethrough mode ' -' @return string with ANSI escape codes for resetting mode +' @return STRING with ANSI escape codes for resetting mode ' -FUNCTION ansi_mode_strikethrough_reset$ () +FUNCTION ANSI.mode_strikethrough_reset$() DIM AS STRING sout - sout$ = CHR$(ANSI_ESC) + "[29m" - IF GJ_LIB_ANSI_OUTPUT THEN ansi_mode_strikethrough_reset$ = sout$ + sout$ = CHR$(ANSI.ESC) + "[29m" + IF GJ_LIB_ANSI_OUTPUT THEN ANSI.mode_strikethrough_reset$ = sout$ END FUNCTION '' ' Reset foreground color ' -' @return string with ANSI escape codes for resetting foreground color +' @return STRING with ANSI escape codes for resetting foreground color ' -FUNCTION ansi_fg_reset$ () +FUNCTION ANSI.fg_reset$() DIM AS STRING sout - sout$ = CHR$(ANSI_ESC) + "[0m" + sout$ = CHR$(ANSI.ESC) + "[0m" IF GJ_LIB_ANSI_EMU THEN COLOR 7 - IF GJ_LIB_ANSI_OUTPUT THEN ansi_fg_reset$ = sout$ + IF GJ_LIB_ANSI_OUTPUT THEN ANSI.fg_reset$ = sout$ END FUNCTION '' ' Reset background color ' -' @return string with ANSI escape codes for resetting background color +' @return STRING with ANSI escape codes for resetting background color ' -FUNCTION ansi_bg_reset$ () +FUNCTION ANSI.bg_reset$() DIM AS STRING sout - sout$ = CHR$(ANSI_ESC) + "[0m" + sout$ = CHR$(ANSI.ESC) + "[0m" IF GJ_LIB_ANSI_EMU THEN COLOR ,0 - IF GJ_LIB_ANSI_OUTPUT THEN ansi_bg_reset$ = sout$ + IF GJ_LIB_ANSI_OUTPUT THEN ANSI.bg_reset$ = sout$ END FUNCTION '' ' Set foreground color to black ' -' @return string with ANSI escape codes for setting color +' @return STRING with ANSI escape codes for setting color ' -FUNCTION ansi_fg_black$ () +FUNCTION ANSI.fg_black$() DIM AS STRING sout - sout$ = CHR$(ANSI_ESC) + "[30m" + sout$ = CHR$(ANSI.ESC) + "[30m" IF GJ_LIB_ANSI_EMU THEN COLOR 0 - IF GJ_LIB_ANSI_OUTPUT THEN ansi_fg_black$ = sout$ + IF GJ_LIB_ANSI_OUTPUT THEN ANSI.fg_black$ = sout$ END FUNCTION '' ' Set foreground color to bright black ' -' @return string with ANSI escape codes for setting color +' @return STRING with ANSI escape codes for setting color ' -FUNCTION ansi_fg_bright_black$ () +FUNCTION ANSI.fg_bright_black$() DIM AS STRING sout - sout$ = CHR$(ANSI_ESC) + "[30;1m" + sout$ = CHR$(ANSI.ESC) + "[30;1m" IF GJ_LIB_ANSI_EMU THEN COLOR 8 - IF GJ_LIB_ANSI_OUTPUT THEN ansi_fg_bright_black$ = sout$ + IF GJ_LIB_ANSI_OUTPUT THEN ANSI.fg_bright_black$ = sout$ END FUNCTION '' ' Set background color to black ' -' @return string with ANSI escape codes for setting color +' @return STRING with ANSI escape codes for setting color ' -FUNCTION ansi_bg_black$ () +FUNCTION ANSI.bg_black$() DIM AS STRING sout - sout$ = CHR$(ANSI_ESC) + "[0;40m" + sout$ = CHR$(ANSI.ESC) + "[0;40m" IF GJ_LIB_ANSI_EMU THEN COLOR ,0 - IF GJ_LIB_ANSI_OUTPUT THEN ansi_bg_black$ = sout$ + IF GJ_LIB_ANSI_OUTPUT THEN ANSI.bg_black$ = sout$ END FUNCTION '' ' Set background color to bright black ' -' @return string with ANSI escape codes for setting color +' @return STRING with ANSI escape codes for setting color ' -FUNCTION ansi_bg_bright_black$ () +FUNCTION ANSI.bg_bright_black$() DIM AS STRING sout - sout$ = CHR$(ANSI_ESC) + "[100;1m" + sout$ = CHR$(ANSI.ESC) + "[100;1m" IF GJ_LIB_ANSI_EMU THEN COLOR ,8 - IF GJ_LIB_ANSI_OUTPUT THEN ansi_bg_bright_black$ = sout$ + IF GJ_LIB_ANSI_OUTPUT THEN ANSI.bg_bright_black$ = sout$ END FUNCTION '' ' Set foreground color to blue ' -' @return string with ANSI escape codes for setting color +' @return STRING with ANSI escape codes for setting color ' -FUNCTION ansi_fg_blue$ () +FUNCTION ANSI.fg_blue$() DIM AS STRING sout - sout$ = CHR$(ANSI_ESC) + "[0;34m" + sout$ = CHR$(ANSI.ESC) + "[0;34m" IF GJ_LIB_ANSI_EMU THEN COLOR 1 - IF GJ_LIB_ANSI_OUTPUT THEN ansi_fg_blue$ = sout$ + IF GJ_LIB_ANSI_OUTPUT THEN ANSI.fg_blue$ = sout$ END FUNCTION '' ' Set foreground color to bright blue ' -' @return string with ANSI escape codes for setting color +' @return STRING with ANSI escape codes for setting color ' -FUNCTION ansi_fg_bright_blue$ () +FUNCTION ANSI.fg_bright_blue$() DIM AS STRING sout - sout$ = CHR$(ANSI_ESC) + "[34;1m" + sout$ = CHR$(ANSI.ESC) + "[34;1m" IF GJ_LIB_ANSI_EMU THEN COLOR 9 - IF GJ_LIB_ANSI_OUTPUT THEN ansi_fg_bright_blue$ = sout$ + IF GJ_LIB_ANSI_OUTPUT THEN ANSI.fg_bright_blue$ = sout$ END FUNCTION '' ' Set background color to blue ' -' @return string with ANSI escape codes for setting color +' @return STRING with ANSI escape codes for setting color ' -FUNCTION ansi_bg_blue$ () +FUNCTION ANSI.bg_blue$() DIM AS STRING sout - sout$ = CHR$(ANSI_ESC) + "[44m" + sout$ = CHR$(ANSI.ESC) + "[44m" IF GJ_LIB_ANSI_EMU THEN COLOR ,1 - IF GJ_LIB_ANSI_OUTPUT THEN ansi_bg_blue$ = sout$ + IF GJ_LIB_ANSI_OUTPUT THEN ANSI.bg_blue$ = sout$ END FUNCTION '' ' Set background color to bright blue ' -' @return string with ANSI escape codes for setting color +' @return STRING with ANSI escape codes for setting color ' -FUNCTION ansi_bg_bright_blue$ () +FUNCTION ANSI.bg_bright_blue$() DIM AS STRING sout - sout$ = CHR$(ANSI_ESC) + "[104;1m" + sout$ = CHR$(ANSI.ESC) + "[104;1m" IF GJ_LIB_ANSI_EMU THEN COLOR ,9 - IF GJ_LIB_ANSI_OUTPUT THEN ansi_bg_bright_blue$ = sout$ + IF GJ_LIB_ANSI_OUTPUT THEN ANSI.bg_bright_blue$ = sout$ END FUNCTION '' ' Set foreground color to green ' -' @return string with ANSI escape codes for setting color +' @return STRING with ANSI escape codes for setting color ' -FUNCTION ansi_fg_green$ () +FUNCTION ANSI.fg_green$() DIM AS STRING sout - sout$ = CHR$(ANSI_ESC) + "[0;32m" + sout$ = CHR$(ANSI.ESC) + "[0;32m" IF GJ_LIB_ANSI_EMU THEN COLOR 2 - IF GJ_LIB_ANSI_OUTPUT THEN ansi_fg_green$ = sout$ + IF GJ_LIB_ANSI_OUTPUT THEN ANSI.fg_green$ = sout$ END FUNCTION '' ' Set foreground color to bright green ' -' @return string with ANSI escape codes for setting color +' @return STRING with ANSI escape codes for setting color ' -FUNCTION ansi_fg_bright_green$ () +FUNCTION ANSI.fg_bright_green$() DIM AS STRING sout - sout$ = CHR$(ANSI_ESC) + "[32;1m" + sout$ = CHR$(ANSI.ESC) + "[32;1m" IF GJ_LIB_ANSI_EMU THEN COLOR 10 - IF GJ_LIB_ANSI_OUTPUT THEN ansi_fg_bright_green$ = sout$ + IF GJ_LIB_ANSI_OUTPUT THEN ANSI.fg_bright_green$ = sout$ END FUNCTION '' ' Set background color to green ' -' @return string with ANSI escape codes for setting color +' @return STRING with ANSI escape codes for setting color ' -FUNCTION ansi_bg_green$ () +FUNCTION ANSI.bg_green$() DIM AS STRING sout - sout$ = CHR$(ANSI_ESC) + "[42m" + sout$ = CHR$(ANSI.ESC) + "[42m" IF GJ_LIB_ANSI_EMU THEN COLOR ,2 - IF GJ_LIB_ANSI_OUTPUT THEN ansi_bg_green$ = sout$ + IF GJ_LIB_ANSI_OUTPUT THEN ANSI.bg_green$ = sout$ END FUNCTION '' ' Set background color to bright green ' -' @return string with ANSI escape codes for setting color +' @return STRING with ANSI escape codes for setting color ' -FUNCTION ansi_bg_bright_green$ () +FUNCTION ANSI.bg_bright_green$() DIM AS STRING sout - sout$ = CHR$(ANSI_ESC) + "[102;1m" + sout$ = CHR$(ANSI.ESC) + "[102;1m" IF GJ_LIB_ANSI_EMU THEN COLOR ,10 - IF GJ_LIB_ANSI_OUTPUT THEN ansi_bg_bright_green$ = sout$ + IF GJ_LIB_ANSI_OUTPUT THEN ANSI.bg_bright_green$ = sout$ END FUNCTION '' ' Set foreground color to cyan ' -' @return string with ANSI escape codes for setting color +' @return STRING with ANSI escape codes for setting color ' -FUNCTION ansi_fg_cyan$ () +FUNCTION ANSI.fg_cyan$() DIM AS STRING sout - sout$ = CHR$(ANSI_ESC) + "[0;36m" + sout$ = CHR$(ANSI.ESC) + "[0;36m" IF GJ_LIB_ANSI_EMU THEN COLOR 3 - IF GJ_LIB_ANSI_OUTPUT THEN ansi_fg_cyan$ = sout$ + IF GJ_LIB_ANSI_OUTPUT THEN ANSI.fg_cyan$ = sout$ END FUNCTION '' ' Set foreground color to bright_cyan ' -' @return string with ANSI escape codes for setting color +' @return STRING with ANSI escape codes for setting color ' -FUNCTION ansi_fg_bright_cyan$ () +FUNCTION ANSI.fg_bright_cyan$() DIM AS STRING sout - sout$ = CHR$(ANSI_ESC) + "[36;1m" + sout$ = CHR$(ANSI.ESC) + "[36;1m" IF GJ_LIB_ANSI_EMU THEN COLOR 11 - IF GJ_LIB_ANSI_OUTPUT THEN ansi_fg_bright_cyan$ = sout$ + IF GJ_LIB_ANSI_OUTPUT THEN ANSI.fg_bright_cyan$ = sout$ END FUNCTION '' ' Set background color to cyan ' -' @return string with ANSI escape codes for setting color +' @return STRING with ANSI escape codes for setting color ' -FUNCTION ansi_bg_cyan$ () +FUNCTION ANSI.bg_cyan$() DIM AS STRING sout - sout$ = CHR$(ANSI_ESC) + "[46m" + sout$ = CHR$(ANSI.ESC) + "[46m" IF GJ_LIB_ANSI_EMU THEN COLOR ,3 - IF GJ_LIB_ANSI_OUTPUT THEN ansi_bg_cyan$ = sout$ + IF GJ_LIB_ANSI_OUTPUT THEN ANSI.bg_cyan$ = sout$ END FUNCTION '' ' Set background color to bright cyan ' -' @return string with ANSI escape codes for setting color +' @return STRING with ANSI escape codes for setting color ' -FUNCTION ansi_bg_bright_cyan$ () +FUNCTION ANSI.bg_bright_cyan$() DIM AS STRING sout - sout$ = CHR$(ANSI_ESC) + "[106;1m" + sout$ = CHR$(ANSI.ESC) + "[106;1m" IF GJ_LIB_ANSI_EMU THEN COLOR ,11 - IF GJ_LIB_ANSI_OUTPUT THEN ansi_bg_bright_cyan$ = sout$ + IF GJ_LIB_ANSI_OUTPUT THEN ANSI.bg_bright_cyan$ = sout$ END FUNCTION '' ' Set foreground color to red ' -' @return string with ANSI escape codes for setting color +' @return STRING with ANSI escape codes for setting color ' -FUNCTION ansi_fg_red$ () +FUNCTION ANSI.fg_red$() DIM AS STRING sout - sout$ = CHR$(ANSI_ESC) + "[0;31m" + sout$ = CHR$(ANSI.ESC) + "[0;31m" IF GJ_LIB_ANSI_EMU THEN COLOR 4 - IF GJ_LIB_ANSI_OUTPUT THEN ansi_fg_red$ = sout$ + IF GJ_LIB_ANSI_OUTPUT THEN ANSI.fg_red$ = sout$ END FUNCTION '' ' Set foreground color to bright red ' -' @return string with ANSI escape codes for setting color +' @return STRING with ANSI escape codes for setting color ' -FUNCTION ansi_fg_bright_red$ () +FUNCTION ANSI.fg_bright_red$() DIM AS STRING sout - sout$ = CHR$(ANSI_ESC) + "[31;1m" + sout$ = CHR$(ANSI.ESC) + "[31;1m" IF GJ_LIB_ANSI_EMU THEN COLOR 12 - IF GJ_LIB_ANSI_OUTPUT THEN ansi_fg_bright_red$ = sout$ + IF GJ_LIB_ANSI_OUTPUT THEN ANSI.fg_bright_red$ = sout$ END FUNCTION '' ' Set background color to red ' -' @return string with ANSI escape codes for setting color +' @return STRING with ANSI escape codes for setting color ' -FUNCTION ansi_bg_red$ () +FUNCTION ANSI.bg_red$() DIM AS STRING sout - sout$ = CHR$(ANSI_ESC) + "[41m" + sout$ = CHR$(ANSI.ESC) + "[41m" IF GJ_LIB_ANSI_EMU THEN COLOR ,4 - IF GJ_LIB_ANSI_OUTPUT THEN ansi_bg_red$ = sout$ + IF GJ_LIB_ANSI_OUTPUT THEN ANSI.bg_red$ = sout$ END FUNCTION '' ' Set background color to bright red ' -' @return string with ANSI escape codes for setting color +' @return STRING with ANSI escape codes for setting color ' -FUNCTION ansi_bg_bright_red$ () +FUNCTION ANSI.bg_bright_red$() DIM AS STRING sout - sout$ = CHR$(ANSI_ESC) + "[101;1m" + sout$ = CHR$(ANSI.ESC) + "[101;1m" IF GJ_LIB_ANSI_EMU THEN COLOR ,12 - IF GJ_LIB_ANSI_OUTPUT THEN ansi_bg_bright_red$ = sout$ + IF GJ_LIB_ANSI_OUTPUT THEN ANSI.bg_bright_red$ = sout$ END FUNCTION '' ' Set foreground color to magenta ' -' @return string with ANSI escape codes for setting color +' @return STRING with ANSI escape codes for setting color ' -FUNCTION ansi_fg_magenta$ () +FUNCTION ANSI.fg_magenta$() DIM AS STRING sout - sout$ = CHR$(ANSI_ESC) + "[0;35m" + sout$ = CHR$(ANSI.ESC) + "[0;35m" IF GJ_LIB_ANSI_EMU THEN COLOR 5 - IF GJ_LIB_ANSI_OUTPUT THEN ansi_fg_magenta$ = sout$ + IF GJ_LIB_ANSI_OUTPUT THEN ANSI.fg_magenta$ = sout$ END FUNCTION '' ' Set foreground color to bright magenta ' -' @return string with ANSI escape codes for setting color +' @return STRING with ANSI escape codes for setting color ' -FUNCTION ansi_fg_bright_magenta$ () +FUNCTION ANSI.fg_bright_magenta$() DIM AS STRING sout - sout$ = CHR$(ANSI_ESC) + "[35;1m" + sout$ = CHR$(ANSI.ESC) + "[35;1m" IF GJ_LIB_ANSI_EMU THEN COLOR 13 - IF GJ_LIB_ANSI_OUTPUT THEN ansi_fg_bright_magenta$ = sout$ + IF GJ_LIB_ANSI_OUTPUT THEN ANSI.fg_bright_magenta$ = sout$ END FUNCTION '' ' Set background color to magenta ' -' @return string with ANSI escape codes for setting color +' @return STRING with ANSI escape codes for setting color ' -FUNCTION ansi_bg_magenta$ () +FUNCTION ANSI.bg_magenta$() DIM AS STRING sout - sout$ = CHR$(ANSI_ESC) + "[45m" + sout$ = CHR$(ANSI.ESC) + "[45m" IF GJ_LIB_ANSI_EMU THEN COLOR ,5 - IF GJ_LIB_ANSI_OUTPUT THEN ansi_bg_magenta$ = sout$ + IF GJ_LIB_ANSI_OUTPUT THEN ANSI.bg_magenta$ = sout$ END FUNCTION '' ' Set background color to bright magenta ' -' @return string with ANSI escape codes for setting color +' @return STRING with ANSI escape codes for setting color ' -FUNCTION ansi_bg_bright_magenta$ () +FUNCTION ANSI.bg_bright_magenta$() DIM AS STRING sout - sout$ = CHR$(ANSI_ESC) + "[105;1m" + sout$ = CHR$(ANSI.ESC) + "[105;1m" IF GJ_LIB_ANSI_EMU THEN COLOR ,13 - IF GJ_LIB_ANSI_OUTPUT THEN ansi_bg_bright_magenta$ = sout$ + IF GJ_LIB_ANSI_OUTPUT THEN ANSI.bg_bright_magenta$ = sout$ END FUNCTION '' ' Set foreground color to yellow ' -' @return string with ANSI escape codes for setting color +' @return STRING with ANSI escape codes for setting color ' -FUNCTION ansi_fg_yellow$ () +FUNCTION ANSI.fg_yellow$() DIM AS STRING sout - sout$ = CHR$(ANSI_ESC) + "[0;33m" + sout$ = CHR$(ANSI.ESC) + "[0;33m" IF GJ_LIB_ANSI_EMU THEN COLOR 6 - IF GJ_LIB_ANSI_OUTPUT THEN ansi_fg_yellow$ = sout$ + IF GJ_LIB_ANSI_OUTPUT THEN ANSI.fg_yellow$ = sout$ END FUNCTION '' ' Set foreground color to bright yellow ' -' @return string with ANSI escape codes for setting color +' @return STRING with ANSI escape codes for setting color ' -FUNCTION ansi_fg_bright_yellow$ () +FUNCTION ANSI.fg_bright_yellow$() DIM AS STRING sout - sout$ = CHR$(ANSI_ESC) + "[33;1m" + sout$ = CHR$(ANSI.ESC) + "[33;1m" IF GJ_LIB_ANSI_EMU THEN COLOR 14 - IF GJ_LIB_ANSI_OUTPUT THEN ansi_fg_bright_yellow$ = sout$ + IF GJ_LIB_ANSI_OUTPUT THEN ANSI.fg_bright_yellow$ = sout$ END FUNCTION '' ' Set background color to yellow ' -' @return string with ANSI escape codes for setting color +' @return STRING with ANSI escape codes for setting color ' -FUNCTION ansi_bg_yellow$ () +FUNCTION ANSI.bg_yellow$() DIM AS STRING sout - sout$ = CHR$(ANSI_ESC) + "[43m" + sout$ = CHR$(ANSI.ESC) + "[43m" IF GJ_LIB_ANSI_EMU THEN COLOR ,6 - IF GJ_LIB_ANSI_OUTPUT THEN ansi_bg_yellow$ = sout$ + IF GJ_LIB_ANSI_OUTPUT THEN ANSI.bg_yellow$ = sout$ END FUNCTION '' ' Set background color to bright yellow ' -' @return string with ANSI escape codes for setting color +' @return STRING with ANSI escape codes for setting color ' -FUNCTION ansi_bg_bright_yellow$ () +FUNCTION ANSI.bg_bright_yellow$() DIM AS STRING sout - sout$ = CHR$(ANSI_ESC) + "[103;1m" + sout$ = CHR$(ANSI.ESC) + "[103;1m" IF GJ_LIB_ANSI_EMU THEN COLOR ,14 - IF GJ_LIB_ANSI_OUTPUT THEN ansi_bg_bright_yellow$ = sout$ + IF GJ_LIB_ANSI_OUTPUT THEN ANSI.bg_bright_yellow$ = sout$ END FUNCTION '' ' Set foreground color to white ' -' @return string with ANSI escape codes for setting color +' @return STRING with ANSI escape codes for setting color ' -FUNCTION ansi_fg_white$ () +FUNCTION ANSI.fg_white$() DIM AS STRING sout - sout$ = CHR$(ANSI_ESC) + "[0;37m" + sout$ = CHR$(ANSI.ESC) + "[0;37m" IF GJ_LIB_ANSI_EMU THEN COLOR 7 - IF GJ_LIB_ANSI_OUTPUT THEN ansi_fg_white$ = sout$ + IF GJ_LIB_ANSI_OUTPUT THEN ANSI.fg_white$ = sout$ END FUNCTION '' ' Set foreground color to bright white ' -' @return string with ANSI escape codes for setting color +' @return STRING with ANSI escape codes for setting color ' -FUNCTION ansi_fg_bright_white$ () +FUNCTION ANSI.fg_bright_white$() DIM AS STRING sout - sout$ = CHR$(ANSI_ESC) + "[37;1m" + sout$ = CHR$(ANSI.ESC) + "[37;1m" IF GJ_LIB_ANSI_EMU THEN COLOR 15 - IF GJ_LIB_ANSI_OUTPUT THEN ansi_fg_bright_white$ = sout$ + IF GJ_LIB_ANSI_OUTPUT THEN ANSI.fg_bright_white$ = sout$ END FUNCTION '' ' Set background color to white ' -' @return string with ANSI escape codes for setting color +' @return STRING with ANSI escape codes for setting color ' -FUNCTION ansi_bg_white$ () +FUNCTION ANSI.bg_white$() DIM AS STRING sout - sout$ = CHR$(ANSI_ESC) + "[47m" + sout$ = CHR$(ANSI.ESC) + "[47m" IF GJ_LIB_ANSI_EMU THEN COLOR ,7 - IF GJ_LIB_ANSI_OUTPUT THEN ansi_bg_white$ = sout$ + IF GJ_LIB_ANSI_OUTPUT THEN ANSI.bg_white$ = sout$ END FUNCTION '' ' Set background color to bright white ' -' @return string with ANSI escape codes for setting color +' @return STRING with ANSI escape codes for setting color ' -FUNCTION ansi_bg_bright_white$ () +FUNCTION ANSI.bg_bright_white$() DIM AS STRING sout - sout$ = CHR$(ANSI_ESC) + "[107;1m" + sout$ = CHR$(ANSI.ESC) + "[107;1m" IF GJ_LIB_ANSI_EMU THEN COLOR ,15 - IF GJ_LIB_ANSI_OUTPUT THEN ansi_bg_bright_white$ = sout$ + IF GJ_LIB_ANSI_OUTPUT THEN ANSI.bg_bright_white$ = sout$ END FUNCTION '' -' Sets text color foreground using 256 color mode +' Sets text color foreground using 256 color mode ' -' @param c% Color number (see link for color table) -' @return string with ANSI escape codes for setting color +' @param INTEGER c% Color number (see link for color table) +' @return STRING with ANSI escape codes for setting color ' -FUNCTION ansi_fg_256$ (c%) +FUNCTION ANSI.fg_256$(c%) DIM AS STRING sout - sout$ = CHR$(ANSI_ESC) + "[38;5" + sout$ = CHR$(ANSI.ESC) + "[38;5" sout$ = sout$ + ";" + _TRIM$(STR$(c%)) sout$ = sout$ + "m" - IF GJ_LIB_ANSI_EMU THEN COLOR c%, bg_color& - IF GJ_LIB_ANSI_OUTPUT THEN ansi_fg_256$ = sout$ + IF GJ_LIB_ANSI_EMU THEN COLOR c%, ANSI.bg_color& + IF GJ_LIB_ANSI_OUTPUT THEN ANSI.fg_256$ = sout$ END FUNCTION '' -' Sets text color background using 256 color mode +' Sets text color background using 256 color mode ' -' @param c% Color number (see link for color table) -' @return string with ANSI escape codes for setting color +' @param INTEGER c% Color number (see link for color table) +' @return STRING with ANSI escape codes for setting color ' -FUNCTION ansi_bg_256$ (c%) +FUNCTION ANSI.bg_256$(c%) DIM AS STRING sout - sout$ = CHR$(ANSI_ESC) + "[48;5" + sout$ = CHR$(ANSI.ESC) + "[48;5" sout$ = sout$ + ";" + _TRIM$(STR$(c%)) sout$ = sout$ + "m" - IF GJ_LIB_ANSI_EMU THEN COLOR fg_color& ,c% - IF GJ_LIB_ANSI_OUTPUT THEN ansi_bg_256$ = sout$ + IF GJ_LIB_ANSI_EMU THEN COLOR ANSI.fg_color& ,c% + IF GJ_LIB_ANSI_OUTPUT THEN ANSI.bg_256$ = sout$ END FUNCTION '' -' Sets text color foreground using RGB 8-bit mode +' Sets text color foreground using RGB 8-bit mode ' -' @param r% Red value 0-255 -' @param g% Green value 0-255 -' @param b% Blue value 0-255 -' @return string with ANSI escape codes for setting color +' @param INTEGER r% Red value 0-255 +' @param INTEGER g% Green value 0-255 +' @param INTEGER b% Blue value 0-255 +' @return STRING with ANSI escape codes for setting color ' -FUNCTION ansi_fg_rgb$ (r%, g%, b%) +FUNCTION ANSI.fg_rgb$(r%, g%, b%) DIM AS STRING sout - sout$ = CHR$(ANSI_ESC) + "[38;2" + sout$ = CHR$(ANSI.ESC) + "[38;2" sout$ = sout$ + ";" + _TRIM$(STR$(r%)) sout$ = sout$ + ";" + _TRIM$(STR$(g%)) sout$ = sout$ + ";" + _TRIM$(STR$(b%)) sout$ = sout$ + "m" - IF GJ_LIB_ANSI_EMU THEN COLOR _RGB(r%, g%, b%), bg_color& - IF GJ_LIB_ANSI_OUTPUT THEN ansi_fg_rgb$ = sout$ + IF GJ_LIB_ANSI_EMU THEN COLOR _RGB(r%, g%, b%), ANSI.bg_color& + IF GJ_LIB_ANSI_OUTPUT THEN ANSI.fg_rgb$ = sout$ END FUNCTION '' -' Sets text color background using RGB 8-bit mode +' Sets text color background using RGB 8-bit mode ' -' @param r% Red value 0-255 -' @param g% Green value 0-255 -' @param b% Blue value 0-255 -' @return string with ANSI escape codes for setting color +' @param INTEGER r% Red value 0-255 +' @param INTEGER g% Green value 0-255 +' @param INTEGER b% Blue value 0-255 +' @return STRING with ANSI escape codes for setting color ' -FUNCTION ansi_bg_rgb$ (r%, g%, b%) +FUNCTION ANSI.bg_rgb$(r%, g%, b%) DIM AS STRING sout - sout$ = CHR$(ANSI_ESC) + "[48;2" + sout$ = CHR$(ANSI.ESC) + "[48;2" sout$ = sout$ + ";" + _TRIM$(STR$(r%)) sout$ = sout$ + ";" + _TRIM$(STR$(g%)) sout$ = sout$ + ";" + _TRIM$(STR$(b%)) sout$ = sout$ + "m" - IF GJ_LIB_ANSI_EMU THEN COLOR fg_color& ,_RGB(r%, g%, b%) - IF GJ_LIB_ANSI_OUTPUT THEN ansi_bg_rgb$ = sout$ + IF GJ_LIB_ANSI_EMU THEN COLOR ANSI.fg_color& ,_RGB(r%, g%, b%) + IF GJ_LIB_ANSI_OUTPUT THEN ANSI.bg_rgb$ = sout$ END FUNCTION diff --git a/ANSI/README.md b/ANSI/README.md index ea88d99..3636339 100644 --- a/ANSI/README.md +++ b/ANSI/README.md @@ -47,100 +47,100 @@ More information here: https://github.com/QB64Official/qb64/issues/33 | FUNCTION | NOTES | |----------|-------| -| ansi_hide_cursor$ | Hides cursor | -| ansi_home$ | Moves cursor to home position (0,0) | -| ansi_locate$ | Moves cursor to desired row and column | -| ansi_move_up$ | Moves cursor up n lines | -| ansi_move_down$ | Moves cursor down n lines | -| ansi_move_right$ | Moves cursor right n lines | -| ansi_move_left$ | Moves cursor left n lines | -| ansi_move_lines_down$ | Moves cursor to beginning of next line, n lines down | -| ansi_move_lines_up$ | Moves cursor to beginning of next line, n lines up | -| ansi_move_column$ | Moves cursor to column position n | -| ansi_move_line_up$ | Moves cursor one one line up, scrolling if needed | -| ansi_save_pos$ | Save cursor position | -| ansi_restore_pos$ | Restore cursor position | +| ANSI.hide_cursor$ | Hides cursor | +| ANSI.home$ | Moves cursor to home position (0,0) | +| ANSI.locate$ | Moves cursor to desired row and column | +| ANSI.move_up$ | Moves cursor up n lines | +| ANSI.move_down$ | Moves cursor down n lines | +| ANSI.move_right$ | Moves cursor right n lines | +| ANSI.move_left$ | Moves cursor left n lines | +| ANSI.move_lines_down$ | Moves cursor to beginning of next line, n lines down | +| ANSI.move_lines_up$ | Moves cursor to beginning of next line, n lines up | +| ANSI.move_column$ | Moves cursor to column position n | +| ANSI.move_line_up$ | Moves cursor one one line up, scrolling if needed | +| ANSI.save_pos$ | Save cursor position | +| ANSI.restore_pos$ | Restore cursor position | ### ERASE | FUNCTION | NOTES | |----------|-------| -| ansi_erase_to_eos$ | Erase from cursor to end of screen | -| ansi_erase_to_bos$ | Erase from cursor to beginning of screen | -| ansi_erase_screen$ | Erase entire screen | -| ansi_erase_to_eol$ | Erase from cursor to end of line | -| ansi_erase_from_sol$ | Erase from start of line to cursor | -| ansi_erase_line$ | Erase line | +| ANSI.erase_to_eos$ | Erase from cursor to end of screen | +| ANSI.erase_to_bos$ | Erase from cursor to beginning of screen | +| ANSI.erase_screen$ | Erase entire screen | +| ANSI.erase_to_eol$ | Erase from cursor to end of line | +| ANSI.erase_from_sol$ | Erase from start of line to cursor | +| ANSI.erase_line$ | Erase line | ### MODES | FUNCTION | NOTES | |----------|-------| -| ansi_mode_reset_all$ | Reset all modes | -| ansi_mode_bold$ | Set bold mode | -| ansi_mode_bold_reset$ | Reset bold mode | -| ansi_mode_dim$ | Set dim mode | -| ansi_mode_dim_reset$ | Reset dim mode | -| ansi_mode_italic$ | Set italic mode | -| ansi_mode_italic_reset$ | Reset italic mode | -| ansi_mode_underline$ | Set underline mode | -| ansi_mode_underline_reset$ | Reset underline mode | -| ansi_mode_blinking$ | Set blinking mode | -| ansi_mode_blinking_reset$ | Reset blinking mode | -| ansi_mode_inverse$ | Set inverse mode | -| ansi_mode_inverse_reset$ | Reset inverse mode | -| ansi_mode_invisible$ | Set invisible mode | -| ansi_mode_invisible_reset$ | Reset invisible mode | -| ansi_mode_strikethrough$ | Set strikethrough mode | -| ansi_mode_strikethrough_reset$ | Reset strikethrough mode | +| ANSI.mode_reset_all$ | Reset all modes | +| ANSI.mode_bold$ | Set bold mode | +| ANSI.mode_bold_reset$ | Reset bold mode | +| ANSI.mode_dim$ | Set dim mode | +| ANSI.mode_dim_reset$ | Reset dim mode | +| ANSI.mode_italic$ | Set italic mode | +| ANSI.mode_italic_reset$ | Reset italic mode | +| ANSI.mode_underline$ | Set underline mode | +| ANSI.mode_underline_reset$ | Reset underline mode | +| ANSI.mode_blinking$ | Set blinking mode | +| ANSI.mode_blinking_reset$ | Reset blinking mode | +| ANSI.mode_inverse$ | Set inverse mode | +| ANSI.mode_inverse_reset$ | Reset inverse mode | +| ANSI.mode_invisible$ | Set invisible mode | +| ANSI.mode_invisible_reset$ | Reset invisible mode | +| ANSI.mode_strikethrough$ | Set strikethrough mode | +| ANSI.mode_strikethrough_reset$ | Reset strikethrough mode | ### STANDARD COLORS | FUNCTION | NOTES | |----------|-------| -| ansi_fg_reset$ | Reset foreground color | -| ansi_bg_reset$ | Reset background color | -| ansi_fg_black$ | Set foreground color to black | -| ansi_fg_blue$ | Set foreground color to blue | -| ansi_fg_green$ | Set foreground color to green | -| ansi_fg_cyan$ | Set foreground color to cyan | -| ansi_fg_red$ | Set foreground color to red | -| ansi_fg_magenta$ | Set foreground color to magenta | -| ansi_fg_yellow$ | Set foreground color to yellow | -| ansi_fg_white$ | Set foreground color to white | -| ansi_fg_bright_black$ | Set foreground color to black | -| ansi_fg_bright_blue$ | Set foreground color to blue | -| ansi_fg_bright_green$ | Set foreground color to green | -| ansi_fg_bright_cyan$ | Set foreground color to cyan | -| ansi_fg_bright_red$ | Set foreground color to red | -| ansi_fg_bright_magenta$ | Set foreground color to magenta | -| ansi_fg_bright_yellow$ | Set foreground color to yellow | -| ansi_fg_bright_white$ | Set foreground color to white | -| ansi_bg_black$ | Set background color to black | -| ansi_bg_blue$ | Set background color to blue | -| ansi_bg_green$ | Set background color to green | -| ansi_bg_cyan$ | Set background color to cyan | -| ansi_bg_red$ | Set background color to red | -| ansi_bg_magenta$ | Set background color to magenta | -| ansi_bg_yellow$ | Set background color to yellow | -| ansi_bg_white$ | Set background color to white | -| ansi_bg_bright_black$ | Set background color to black | -| ansi_bg_bright_blue$ | Set background color to blue | -| ansi_bg_bright_green$ | Set background color to green | -| ansi_bg_bright_cyan$ | Set background color to cyan | -| ansi_bg_bright_red$ | Set background color to red | -| ansi_bg_bright_magenta$ | Set background color to magenta | -| ansi_bg_bright_yellow$ | Set background color to yellow | -| ansi_bg_bright_white$ | Set background color to white | +| ANSI.fg_reset$ | Reset foreground color | +| ANSI.bg_reset$ | Reset background color | +| ANSI.fg_black$ | Set foreground color to black | +| ANSI.fg_blue$ | Set foreground color to blue | +| ANSI.fg_green$ | Set foreground color to green | +| ANSI.fg_cyan$ | Set foreground color to cyan | +| ANSI.fg_red$ | Set foreground color to red | +| ANSI.fg_magenta$ | Set foreground color to magenta | +| ANSI.fg_yellow$ | Set foreground color to yellow | +| ANSI.fg_white$ | Set foreground color to white | +| ANSI.fg_bright_black$ | Set foreground color to black | +| ANSI.fg_bright_blue$ | Set foreground color to blue | +| ANSI.fg_bright_green$ | Set foreground color to green | +| ANSI.fg_bright_cyan$ | Set foreground color to cyan | +| ANSI.fg_bright_red$ | Set foreground color to red | +| ANSI.fg_bright_magenta$ | Set foreground color to magenta | +| ANSI.fg_bright_yellow$ | Set foreground color to yellow | +| ANSI.fg_bright_white$ | Set foreground color to white | +| ANSI.bg_black$ | Set background color to black | +| ANSI.bg_blue$ | Set background color to blue | +| ANSI.bg_green$ | Set background color to green | +| ANSI.bg_cyan$ | Set background color to cyan | +| ANSI.bg_red$ | Set background color to red | +| ANSI.bg_magenta$ | Set background color to magenta | +| ANSI.bg_yellow$ | Set background color to yellow | +| ANSI.bg_white$ | Set background color to white | +| ANSI.bg_bright_black$ | Set background color to black | +| ANSI.bg_bright_blue$ | Set background color to blue | +| ANSI.bg_bright_green$ | Set background color to green | +| ANSI.bg_bright_cyan$ | Set background color to cyan | +| ANSI.bg_bright_red$ | Set background color to red | +| ANSI.bg_bright_magenta$ | Set background color to magenta | +| ANSI.bg_bright_yellow$ | Set background color to yellow | +| ANSI.bg_bright_white$ | Set background color to white | ### 256 COLORS: | FUNCTION | NOTES | |----------|-------| -| ansi_fg_256$ | Sets text foreground color using 256 color mode | -| ansi_bg_256$ | Sets text background color using 256 color mode | +| ANSI.fg_256$ | Sets text foreground color using 256 color mode | +| ANSI.bg_256$ | Sets text background color using 256 color mode | ### 24-BIT COLORS: | FUNCTION | NOTES | |----------|-------| -| ansi_fg_rgb$ | Sets text color foreground using RGB 8-bit mode | -| ansi_bg_rgb$ | Sets text color background using RGB 8-bit mode | +| ANSI.fg_rgb$ | Sets text color foreground using RGB 8-bit mode | +| ANSI.bg_rgb$ | Sets text color background using RGB 8-bit mode | diff --git a/ARR/ARR_BYTE.BAS b/ARR/ARR_BYTE.BAS new file mode 100644 index 0000000..4106219 --- /dev/null +++ b/ARR/ARR_BYTE.BAS @@ -0,0 +1,820 @@ +'$DYNAMIC +$LET GJ_LIB_ARR_BYTE_BAS = 1 + +'' +' Slice an array from source to destination starting at index and count slices +' +' @param _BYTE() source_arr%% to slice from +' @param _BYTE() dest_arr%% to put slices into +' @param INTEGER start_idx% starting index to use as slice range +' @param INTEGER count% number of slices - if negative, backwards from index +' +SUB ARR_BYTE.slice(source_arr%%(), dest_arr%%(), start_idx%, count%) + DIM AS LONG ub, lb, i, n + lb& = LBOUND(source_arr%%) : ub& = UBOUND(source_arr%%) + IF start_idx% < lb& OR start_idx% + count% > ub& THEN EXIT SUB ' out of range + IF ub& - lb& < count% THEN EXIT SUB ' too many and not enough + REDIM dest_arr(0 TO ABS(count%)) AS _BYTE + IF SGN(count%) = -1 THEN + IF ((start_idx% - 1) - ABS(count%)) < 0 THEN EXIT SUB ' out of range + n& = 0 + FOR i& = (start_idx% - 1) TO ((start_idx% - 1) - ABS(count%)) STEP -1 + dest_arr%%(n&) = source_arr%%(i&) + n& = n& + 1 + NEXT i& + ELSE + IF ((start_idx% + 1) + ABS(count%)) > (ub& - lb&) THEN EXIT SUB ' out of range + n& = 0 + FOR i& = start_idx% + 1 TO ((start_idx% + 1) + count%) STEP 1 + dest_arr%%(n&) = source_arr%%(i&) + n& = n& + 1 + NEXT i& + END IF +END SUB + + +'' +' Push a byte onto the end of a _BYTE array +' +' @param _BYTE arr%%() array to push into +' @param _BYTE value%% of byte to push +' +SUB ARR_BYTE.push(arr%%(), value%%) + DIM AS LONG ub, lb + lb& = LBOUND(arr%%) : ub& = UBOUND(arr%%) + REDIM _PRESERVE arr(lb& TO (ub& + 1)) AS _BYTE + arr%%(ub& + 1) = value%% +END SUB + + +'' +' Pop a byte from the end of a _BYTE array +' +' @param _BYTE arr%%() array to pop from +' @param _BYTE var%% of byte to store popped byte +' +SUB ARR_BYTE.pop(arr%%(), var%%) + DIM AS LONG ub, lb + lb& = LBOUND(arr%%) : ub& = UBOUND(arr%%) + var%% = arr%%(ub&) + REDIM _PRESERVE arr(lb& TO (ub& - 1)) AS _BYTE +END SUB + + +'' +' Pop a byte from the beginning of a _BYTE array +' +' @param _BYTE arr%%() array to pop from +' @param _BYTE var%% of byte to store popped byte +' +SUB ARR_BYTE.shift(arr%%(), var%%) + DIM AS LONG ub, lb, i + lb& = LBOUND(arr%%) : ub& = UBOUND(arr%%) + var%% = arr%%(lb&) + FOR i& = lb& TO ub& - 1 + arr%%(i&) = arr%%(i& + 1) + NEXT i& + REDIM _PRESERVE arr(lb& + 1 TO ub&) AS _BYTE +END SUB + + +'' +' Copy an array of BYTEs to another _BYTE array +' +' @param _BYTE ARRAY source_arr%%() source array to copy +' @param _BYTE ARRAY dest_arr%%() dest array to copy into +' +SUB ARR_BYTE.copy(source_arr%%(), dest_arr%%()) + DIM AS LONG ub, lb, i + lb& = LBOUND(source_arr%%) : ub& = UBOUND(source_arr%%) + REDIM dest_arr(lb& TO ub&) AS _BYTE + FOR i& = lb& TO ub& + dest_arr%%(i&) = source_arr%%(i&) + NEXT i& +END SUB + + +'' +' Push a byte into the beginning of a _BYTE array +' +' @param _BYTE arr%%() array to push into +' @param _BYTE value%% of byte to push +' +SUB ARR_BYTE.unshift(arr%%(), value%%) + DIM AS LONG ub, lb, i + lb& = LBOUND(arr%%) : ub& = UBOUND(arr%%) + DIM work(lb& TO ub&) AS _BYTE + CALL ARR_BYTE.copy(arr%%(), work%%()) + REDIM _PRESERVE arr(lb& TO (ub& + 1)) AS _BYTE + FOR i& = lb& + 1 TO ub& + 1 + arr%%(i&) = work%%(i& - 1) + NEXT i& + arr%%(lb&) = value%% +END SUB + + +'' +' Joins an array of BYTEs as a string +' +' @param _BYTE ARRAY arr%%() to get as a string +' @param STRING s$ to store stringified array in +' +SUB ARR_BYTE.join(arr%%(), s$) + DIM AS LONG ub, lb, i + s$ = "" + lb& = LBOUND(arr%%) : ub& = UBOUND(arr%%) + FOR i& = lb& TO ub& + s$ = s$ + _TRIM$(STR$(arr%%(i&))) + ", " + NEXT i& + ' remove trailing comma + s$ = MID$(s$, 1, (LEN(s$)-LEN(", "))) +END SUB + + +'' +' Create a new _BYTE array using string of bytes seperated by commas +' +' @param _BYTE ARRAY arr%%() to store the bytes in +' @param STRING s$ string of comma separated bytes +' +SUB ARR_BYTE.new(arr%%(), s$) + DIM AS LONG i, count + DIM t AS STRING + count& = 0 + FOR i& = 1 TO LEN(s$) + IF ASC(s$, i&) = ASC(",") THEN count& = count& + 1 + NEXT i& + REDIM arr(0 TO count&) AS _BYTE + IF count& = 0 THEN + arr%%(0) = VAL(s$) + EXIT SUB + END IF + count& = 0 + FOR i& = 1 TO LEN(s$) + t$ = t$ + CHR$(ASC(s$, i&)) + IF ASC(s$, i&) = ASC(",") OR i& = LEN(s$) THEN + arr%%(count&) = VAL(t$) + count& = count& + 1 + t$ = "" + END IF + NEXT i& +END SUB + + +'' +' Return the visually longest element of a _BYTE array +' +' @param _BYTE ARRAY arr%%() to check in +' @return _BYTE value of visually longest element +' +FUNCTION ARR_BYTE.longest%%(arr%%()) + DIM AS LONG lb, ub, i, res, lw + lb& = LBOUND(arr%%) : ub& = UBOUND(arr%%) + res& = 0 : lw& = 0 + FOR i& = lb& TO ub& + IF LEN(_TRIM$(STR$(arr%%(i&)))) > lw& THEN + lw& = LEN(_TRIM$(STR$(arr%%(i&)))) + res& = i& + END IF + NEXT i& + ARR_BYTE.longest%% = arr%%(res&) +END FUNCTION + + +'' +' Perform some math on every element of a _BYTE array +' +' @param _BYTE ARRAY source_arr%%() to do math on +' @param _BYTE ARRAY dest_arr%%() to store results in +' @param STRING op$ one of: +' and or xor shl shr +' "+", "-", "*", "\", "&&", "||", "!!", "<<", ">>" +' @param _BYTE value%% to use for operand +' +SUB ARR_BYTE.math(source_arr%%(), dest_arr%%(), op$, value%%) + DIM AS LONG lb, ub, i + lb& = LBOUND(source_arr%%) : ub& = UBOUND(source_arr%%) + REDIM dest_arr(lb& TO ub&) AS _BYTE + FOR i& = lb& TO ub& + SELECT CASE op$ + CASE "+": + dest_arr%%(i&) = source_arr%%(i&) + value%% + CASE "-": + dest_arr%%(i&) = source_arr%%(i&) - value%% + CASE "*": + dest_arr%%(i&) = source_arr%%(i&) * value%% + CASE "\": + IF value%% > 0 THEN + dest_arr%%(i&) = source_arr%%(i&) \ value%% + END IF + CASE "&&": + dest_arr%%(i&) = source_arr%%(i&) AND value%% + CASE "||": + dest_arr%%(i&) = source_arr%%(i&) OR value%% + CASE "!!": + dest_arr%%(i&) = source_arr%%(i&) XOR value%% + CASE "<<": + dest_arr%%(i&) = _SHL(source_arr%%(i&), value%%) + CASE ">>": + dest_arr%%(i&) = _SHR(source_arr%%(i&), value%%) + END SELECT + NEXT i& +END SUB + + +'' +' Return the minimum element value in _BYTE array +' +' @param _BYTE ARRAY arr%%() to check in +' @return _BYTE minimum value found +' +FUNCTION ARR_BYTE.min%%(arr%%()) + DIM AS LONG lb, ub, i + DIM AS _BYTE s + lb& = LBOUND(arr%%) : ub& = UBOUND(arr%%) + s%% = 127 + FOR i& = lb& TO ub& + IF arr%%(i&) < s%% THEN + s%% = arr%%(i&) + END IF + NEXT i& + ARR_BYTE.min%% = s%% +END FUNCTION + + +'' +' Return the maximum element value in _BYTE array +' +' @param _BYTE ARRAY arr%%() to check in +' @return _BYTE maximum value found +' +FUNCTION ARR_BYTE.max%%(arr%%()) + DIM AS LONG lb, ub, i + DIM AS _BYTE s + lb& = LBOUND(arr%%) : ub& = UBOUND(arr%%) + s%% = 0 + FOR i& = lb& TO ub& + IF arr%%(i&) > s%% THEN + s%% = arr%%(i&) + END IF + NEXT i& + ARR_BYTE.max%% = s%% +END FUNCTION + + +'' +' Return the visually shortest element of a _BYTE array +' +' @param _BYTE ARRAY arr%%() to check in +' @return _BYTE value of visually shortest element +' +FUNCTION ARR_BYTE.shortest%%(arr%%()) + DIM AS LONG lb, ub, i, res, sw + lb& = LBOUND(arr%%) : ub& = UBOUND(arr%%) + res& = 0 : sw& = 0 + FOR i& = lb& TO ub& + IF LEN(_TRIM$(STR$(arr%%(i&)))) < sw& THEN + sw& = LEN(_TRIM$(STR$(arr%%(i&)))) + res& = i& + END IF + NEXT i& + ARR_BYTE.shortest%% = arr%%(res&) +END FUNCTION + + +'' +' Return the first element of a _BYTE array +' +' @param _BYTE ARRAY arr%%() to check in +' @return _BYTE value of first element +' +FUNCTION ARR_BYTE.first%%(arr%%()) + ARR_BYTE.first%% = arr%%(LBOUND(arr%%)) +END FUNCTION + + +'' +' Return the last element of a _BYTE array +' +' @param _BYTE ARRAY arr%%() to check in +' @return _BYTE value of last element +' +FUNCTION ARR_BYTE.last%%(arr%%()) + ARR_BYTE.last%% = arr%%(UBOUND(arr%%)) +END FUNCTION + + +'' +' Return every nth array element of a _BYTE array +' +' @param _BYTE ARRAY source_arr%%() to get from +' @param _BYTE ARRAY dest_arr%%() to store in +' @param INTEGER nth% element +' +SUB ARR_BYTE.nth(source_arr%%(), dest_arr%%(), nth%) + DIM AS LONG lb, ub, i, n + lb& = LBOUND(source_arr%%) : ub& = UBOUND(source_arr%%) + n& = (ub& - lb&) \ nth% + REDIM dest_arr(n&) AS _BYTE + n& = 0 + FOR i& = lb& to ub& + IF i& MOD nth% = 0 THEN + dest_arr%%(n&) = source_arr%%(i&) + n& = n& + 1 + END IF + NEXT i& +END SUB + + +'' +' Checks if value exists in _BYTE array +' +' @param _BYTE ARRAY arr%%() to check in +' @param _BYTE value%% value to check for +' @return INTEGER -1 if found or 0 if not found +' +FUNCTION ARR_BYTE.in%(arr%%(), value%%) + DIM AS LONG lb, ub, i + lb& = LBOUND(arr%%) : ub& = UBOUND(arr%%) + FOR i& = lb& TO ub& + IF arr%%(i&) = value%% THEN + ARR_BYTE.in% = -1 + EXIT FUNCTION + END IF + NEXT i& + ARR_BYTE.in% = 0 +END FUNCTION + + +'' +' Checks if value exists in _BYTE array and returns index if found +' +' @param _BYTE ARRAY arr%%() to check in +' @param _BYTE value%% value to check for +' @return INTEGER index of element if found or -1 if not found +' +FUNCTION ARR_BYTE.find%(arr%%(), value%%) + DIM AS LONG lb, ub, i + lb& = LBOUND(arr%%) : ub& = UBOUND(arr%%) + FOR i& = lb& TO ub& + IF arr%%(i&) = value%% THEN + ARR_BYTE.find% = i& + EXIT FUNCTION + END IF + NEXT i& + ARR_BYTE.find% = -1 +END FUNCTION + + +'' +' Return the number of elements in a _BYTE array +' +' @param _BYTE ARRAY arr%%() to count +' @return INTEGER number of elements +' +FUNCTION ARR_BYTE.count&(arr%%()) + ARR_BYTE.count& = UBOUND(arr%%) - LBOUND(arr%%) +END FUNCTION + + +'' +' Return the size of a _BYTE array +' +' @param _BYTE ARRAY arr%%() to get size of +' @return LONG size in bytes +' +FUNCTION ARR_BYTE.size&(arr%%()) + ARR_BYTE.size& = LEN(arr%%()) +END FUNCTION + + +'' +' Reverses the elements of a _BYTE array +' +' @param _BYTE ARRAY source_arr%%() to reverse +' @param _BYTE ARRAY dest_arr%%() to store reversed array in +' +SUB ARR_BYTE.reverse(source_arr%%(), dest_arr%%()) + DIM AS LONG lb, ub, i, n + lb& = LBOUND(source_arr%%) : ub& = UBOUND(source_arr%%) + REDIM dest_arr(0 TO (ub& - lb&)) AS _BYTE + n& = 0 + FOR i& = ub& TO lb& STEP -1 + dest_arr%%(n&) = source_arr%%(i&) + n& = n& + 1 + NEXT i& +END SUB + + +'' +' Returns a random byte from a _BYTE array +' +' @param _BYTE ARRAY arr%%() array to get random element from +' @return _BYTE random element +' +FUNCTION ARR_BYTE.random%%(arr%%()) + DIM AS LONG lb, ub + lb& = LBOUND(arr%%) : ub& = UBOUND(arr%%) + RANDOMIZE TIMER + ARR_BYTE.random%% = arr%%(INT(RND * (ub& - lb&)) + 1) +END FUNCTION + + +'' +' Returns the sum of all elements in a _BYTE array +' +' @param _BYTE ARRAY arr%%() array to get some for +' @return LONG sum of all elements +' +FUNCTION ARR_BYTE.sum&(arr%%()) + DIM AS LONG lb, ub, i + DIM sum AS LONG + lb& = LBOUND(arr%%) : ub& = UBOUND(arr%%) + FOR i& = lb& TO ub& + sum& = sum& + arr%%(i&) + NEXT i& + ARR_BYTE.sum& = sum& +END FUNCTION + + +'' +' Returns the average value of elements in a _BYTE array +' +' @param _BYTE ARRAY arr%%() array to get average for +' @return LONG average of elements +' +FUNCTION ARR_BYTE.avg&(arr%%()) + DIM AS LONG lb, ub, i + DIM sum AS LONG + lb& = LBOUND(arr%%) : ub& = UBOUND(arr%%) + FOR i& = lb& TO ub& + sum& = sum& + arr%%(i&) + NEXT i& + ARR_BYTE.avg& = sum& / (ub& - lb&) +END FUNCTION + + +'' +' Shuffle the elements of a _BYTE array +' +' @param _BYTE ARRAY source_arr%%() to shuffle +' @param _BYTE ARRAY dest_arr%%() to store shuffled array in +' +SUB ARR_BYTE.shuffle(source_arr%%(), dest_arr%%()) + DIM AS LONG lb, ub, i, count + lb& = LBOUND(source_arr%%) : ub& = UBOUND(source_arr%%) + count& = ub& - lb& + REDIM dest_arr(0 TO count&) AS _BYTE + CALL ARR_BYTE.copy(source_arr%%(), dest_arr%%()) + RANDOMIZE TIMER + FOR i& = 0 TO count& + SWAP dest_arr%%(i&), dest_arr%%(lb& + RND * (ub& - lb&)) + NEXT i& +END SUB + + +'' +' Makes a _BYTE array contain only unique values +' +' @param _BYTE ARRAY source_arr%%() array to get uniques for +' @param _BYTE ARRAY dest_arr%%() array to store uniques in +' +SUB ARR_BYTE.unique(source_arr%%(), dest_arr%%()) + DIM AS LONG lb, ub, i + DIM tmp AS _BYTE + lb& = LBOUND(source_arr%%) : ub& = UBOUND(source_arr%%) + DIM work_arr(0) AS _BYTE + FOR i& = lb& TO ub& + IF NOT ARR_BYTE.in%(work_arr%%(), source_arr%%(i&)) THEN + CALL ARR_BYTE.push(work_arr%%(), source_arr%%(i&)) + END IF + NEXT i& + CALL ARR_BYTE.shift(work_arr%%(), tmp%%) + CALL ARR_BYTE.copy(work_arr%%(), dest_arr%%()) +END SUB + + +'' +' Filters a _BYTE array to only elements greater than value +' +' @param _BYTE ARRAY source_arr%%() array to work on +' @param _BYTE ARRAY dest_arr%%() array to store in +' @param _BYTE value%% to be greater than to be returned +' +SUB ARR_BYTE.gt(source_arr%%(), dest_arr%%(), value%%) + DIM AS LONG lb, ub, i + DIM tmp AS _BYTE + lb& = LBOUND(source_arr%%) : ub& = UBOUND(source_arr%%) + DIM work_arr(0) AS _BYTE + FOR i& = lb& TO ub& + IF source_arr%%(i&) > value%% THEN + CALL ARR_BYTE.push(work_arr%%(), source_arr%%(i&)) + END IF + NEXT i& + CALL ARR_BYTE.shift(work_arr%%(), tmp%%) + CALL ARR_BYTE.copy(work_arr%%(), dest_arr%%()) +END SUB + + +'' +' Filters a _BYTE array to only elements greater than or equal to value +' +' @param _BYTE ARRAY source_arr%%() array to work on +' @param _BYTE ARRAY dest_arr%%() array to store in +' @param _BYTE value%% to be greater than or equal to be returned +' +SUB ARR_BYTE.gte(source_arr%%(), dest_arr%%(), value%%) + DIM AS LONG lb, ub, i + DIM tmp AS _BYTE + lb& = LBOUND(source_arr%%) : ub& = UBOUND(source_arr%%) + DIM work_arr(0) AS _BYTE + FOR i& = lb& TO ub& + IF source_arr%%(i&) >= value%% THEN + CALL ARR_BYTE.push(work_arr%%(), source_arr%%(i&)) + END IF + NEXT i& + CALL ARR_BYTE.shift(work_arr%%(), tmp%%) + CALL ARR_BYTE.copy(work_arr%%(), dest_arr%%()) +END SUB + + +'' +' Filters a _BYTE array to only elements less than value +' +' @param _BYTE ARRAY source_arr%%() array to work on +' @param _BYTE ARRAY dest_arr%%() array to store in +' @param _BYTE value%% to be less than to be returned +' +SUB ARR_BYTE.lt(source_arr%%(), dest_arr%%(), value%%) + DIM AS LONG lb, ub, i + DIM tmp AS _BYTE + lb& = LBOUND(source_arr%%) : ub& = UBOUND(source_arr%%) + DIM work_arr(0) AS _BYTE + FOR i& = lb& TO ub& + IF source_arr%%(i&) < value%% THEN + CALL ARR_BYTE.push(work_arr%%(), source_arr%%(i&)) + END IF + NEXT i& + CALL ARR_BYTE.shift(work_arr%%(), tmp%%) + CALL ARR_BYTE.copy(work_arr%%(), dest_arr%%()) +END SUB + + +'' +' Filters a _BYTE array to only elements less than or equal to value +' +' @param _BYTE ARRAY source_arr%%() array to work on +' @param _BYTE ARRAY dest_arr%%() array to store in +' @param _BYTE value%% to be less than or equal to be returned +' +SUB ARR_BYTE.lte(source_arr%%(), dest_arr%%(), value%%) + DIM AS LONG lb, ub, i + DIM tmp AS _BYTE + lb& = LBOUND(source_arr%%) : ub& = UBOUND(source_arr%%) + DIM work_arr(0) AS _BYTE + FOR i& = lb& TO ub& + IF source_arr%%(i&) <= value%% THEN + CALL ARR_BYTE.push(work_arr%%(), source_arr%%(i&)) + END IF + NEXT i& + CALL ARR_BYTE.shift(work_arr%%(), tmp%%) + CALL ARR_BYTE.copy(work_arr%%(), dest_arr%%()) +END SUB + + +'' +' Finds and replaces values across all elements in a _BYTE ARRAY +' +' @param _BYTE ARRAY arr%%() to check in +' @param _BYTE find%% value to find +' @param _BYTE replace%% value to replace with if found +' +SUB ARR_BYTE.replace(arr%%(), find%%, replace%%) + DIM AS LONG lb, ub, i + lb& = LBOUND(arr%%) : ub& = UBOUND(arr%%) + FOR i& = lb& TO ub& + IF arr%%(i&) = find%% THEN + arr%%(i&) = replace%% + END IF + NEXT i& +END SUB + + +'' +' Inserts a new element into _BYTE array after index +' +' @param _BYTE ARRAY arr%%() array to work on +' @param _BYTE value%% to insert +' @param INTEGER index% of element to insert at +' +SUB ARR_BYTE.insert(arr%%(), value%%, index%) + DIM AS LONG lb, ub, i + DIM tmp AS _BYTE + DIM work_arr(0) AS _BYTE + lb& = LBOUND(arr%%) : ub& = UBOUND(arr%%) + IF index% >= lb& AND index% <= ub& THEN + ' build new array up to index + FOR i& = lb& TO index% + CALL ARR_BYTE.push(work_arr%%(), arr%%(i&)) + NEXT i& + ' insert new element + CALL ARR_BYTE.push(work_arr%%(), value%%) + ' finish building array from index + 1 + FOR i& = index% + 1 TO ub& + CALL ARR_BYTE.push(work_arr%%(), arr%%(i&)) + NEXT i& + CALL ARR_BYTE.shift(work_arr%%(), tmp%%) + CALL ARR_BYTE.copy(work_arr%%(), arr%%()) + END IF +END SUB + + +'' +' Removes element from a _BYTE array by element index +' +' @param _BYTE ARRAY arr%%() array to work on +' @param INTEGER index% of element to remove +' +SUB ARR_BYTE.remove(arr%%(), index%) + DIM AS LONG lb, ub, i + DIM tmp AS _BYTE + DIM work_arr(0) AS _BYTE + lb& = LBOUND(arr%%) : ub& = UBOUND(arr%%) + IF index% >= lb& AND index% <= ub& THEN + ' build new array up to index + FOR i& = lb& TO index% - 1 + CALL ARR_BYTE.push(work_arr%%(), arr%%(i&)) + NEXT i& + ' skip elements + FOR i& = index% + 1 TO ub& + CALL ARR_BYTE.push(work_arr%%(), arr%%(i&)) + NEXT i& + CALL ARR_BYTE.shift(work_arr%%(), tmp%%) + CALL ARR_BYTE.copy(work_arr%%(), arr%%()) + END IF +END SUB + + +'' +' Filters a _BYTE array to only elements that have odd values +' +' @param _BYTE ARRAY source_arr%%() array to work on +' @param _BYTE ARRAY dest_arr%%() array to store in +' +SUB ARR_BYTE.odd(source_arr%%(), dest_arr%%()) + DIM AS LONG lb, ub, i + DIM tmp AS _BYTE + lb& = LBOUND(source_arr%%) : ub& = UBOUND(source_arr%%) + DIM work_arr(0) AS _BYTE + FOR i& = lb& TO ub& + IF source_arr%%(i&) MOD 2 <> 0 THEN + CALL ARR_BYTE.push(work_arr%%(), source_arr%%(i&)) + END IF + NEXT i& + CALL ARR_BYTE.shift(work_arr%%(), tmp%%) + CALL ARR_BYTE.copy(work_arr%%(), dest_arr%%()) +END SUB + + +'' +' Filters a _BYTE array to only elements that have even values +' +' @param _BYTE ARRAY source_arr%%() array to work on +' @param _BYTE ARRAY dest_arr%%() array to store in +' +SUB ARR_BYTE.even(source_arr%%(), dest_arr%%()) + DIM AS LONG lb, ub, i + DIM tmp AS _BYTE + lb& = LBOUND(source_arr%%) : ub& = UBOUND(source_arr%%) + DIM work_arr(0) AS _BYTE + FOR i& = lb& TO ub& + IF source_arr%%(i&) MOD 2 = 0 THEN + CALL ARR_BYTE.push(work_arr%%(), source_arr%%(i&)) + END IF + NEXT i& + CALL ARR_BYTE.shift(work_arr%%(), tmp%%) + CALL ARR_BYTE.copy(work_arr%%(), dest_arr%%()) +END SUB + + +'' +' Filters a _BYTE array to only elements that have values evenly divisible by divisor +' +' @param _BYTE ARRAY source_arr%%() array to work on +' @param _BYTE ARRAY dest_arr%%() array to store in +' @param _BYTE divisor%% for modulo +' +SUB ARR_BYTE.mod(source_arr%%(), dest_arr%%(), divisor%%) + DIM AS LONG lb, ub, i + DIM tmp AS _BYTE + lb& = LBOUND(source_arr%%) : ub& = UBOUND(source_arr%%) + DIM work_arr(0) AS _BYTE + FOR i& = lb& TO ub& + IF source_arr%%(i&) MOD divisor%% = 0 THEN + CALL ARR_BYTE.push(work_arr%%(), source_arr%%(i&)) + END IF + NEXT i& + CALL ARR_BYTE.shift(work_arr%%(), tmp%%) + CALL ARR_BYTE.copy(work_arr%%(), dest_arr%%()) +END SUB + + +'' +' Filters a _BYTE array to only elements between min and max +' +' @param _BYTE ARRAY source_arr%%() array to work on +' @param _BYTE ARRAY dest_arr%%() array to store in +' @param _BYTE min%% to be greater than or equal to be returned +' @param _BYTE max%% to be less than or equal to be returned +' +SUB ARR_BYTE.between(source_arr%%(), dest_arr%%(), min%%, max%%) + DIM AS LONG lb, ub, i + DIM tmp AS _BYTE + lb& = LBOUND(source_arr%%) : ub& = UBOUND(source_arr%%) + DIM work_arr(0) AS _BYTE + FOR i& = lb& TO ub& + IF source_arr%%(i&) >= min%% _ + AND source_arr%%(i&) <= max%% THEN + CALL ARR_BYTE.push(work_arr%%(), source_arr%%(i&)) + END IF + NEXT i& + CALL ARR_BYTE.shift(work_arr%%(), tmp%%) + CALL ARR_BYTE.copy(work_arr%%(), dest_arr%%()) +END SUB + + +'' +' Sorts _BYTE array in ascending order +' +' @param _BYTE ARRAY source_arr%%() array to sort +' @param _BYTE ARRAY dest_arr%%() array to store sorted in +' +SUB ARR_BYTE.sort(source_arr%%(), dest_arr%%()) + DIM AS LONG lb, ub, count + lb& = LBOUND(source_arr%%) : ub& = UBOUND(source_arr%%) + count& = ub& - lb& + REDIM dest_arr(0 TO count&) AS _BYTE + CALL ARR_BYTE.copy(source_arr%%(), dest_arr%%()) + CALL ARR_BYTE.quicksort(dest_arr%%(), lb&, ub&, 0) +END SUB + + +'' +' Sorts _BYTE array in descending order +' +' @param _BYTE ARRAY source_arr%%() array to sort +' @param _BYTE ARRAY dest_arr%%() array to store sorted in +' +SUB ARR_BYTE.rsort(source_arr%%(), dest_arr%%()) + DIM AS LONG lb, ub, count + lb& = LBOUND(source_arr%%) : ub& = UBOUND(source_arr%%) + count& = ub& - lb& + REDIM dest_arr(0 TO count&) AS _BYTE + CALL ARR_BYTE.copy(source_arr%%(), dest_arr%%()) + CALL ARR_BYTE.quicksort(dest_arr%%(), lb&, ub&, 1) +END SUB + + +'' +' Quicksort array with pivot algorithm by logiclrd +' +' @link https://www.tek-tips.com/faqs.cfm?fid=336 +' @param _BYTE ARRAY array%%() to sort +' @param INTEGER start% of range to sort +' @param INTEGER finish% range of sort +' @param INTEGER order% to sort by (0 = asc / 1 = desc) +' +SUB ARR_BYTE.quicksort(arr%%(), start%, finish%, order%) + DIM i AS LONG + DIM pivot AS INTEGER + DIM pivotvalue AS _BYTE + + 'first, partition the array + pivot% = start% + pivotvalue%% = arr%%(pivot%) + FOR i& = start% + 1 TO finish% + IF order% = 0 THEN ' ascending order + IF arr%%(i&) < pivotvalue%% THEN + arr%%(pivot%) = arr%%(i&) + arr%%(i&) = arr%%(pivot% + 1) + arr%%(pivot% + 1) = pivotvalue%% + pivot% = pivot% + 1 + END IF + ELSEIF order% = 1 THEN ' descending order + IF arr%%(i&) > pivotvalue%% THEN + arr%%(pivot%) = arr%%(i&) + arr%%(i&) = arr%%(pivot% + 1) + arr%%(pivot% + 1) = pivotvalue%% + pivot% = pivot% + 1 + END IF + END IF + NEXT i& + + 'then, sort the subarrays to each side of the pivot + IF pivot% - start% >= 2 THEN + CALL ARR_BYTE.quicksort(arr%%(), start%, pivot% - 1, order%) + END IF + IF finish% - pivot% >= 2 THEN + CALL ARR_BYTE.quicksort(arr%%(), pivot% + 1, finish%, order%) + END IF +END SUB diff --git a/ARR/ARR_DBL.BAS b/ARR/ARR_DBL.BAS new file mode 100644 index 0000000..216cde8 --- /dev/null +++ b/ARR/ARR_DBL.BAS @@ -0,0 +1,820 @@ +'$DYNAMIC +$LET GJ_LIB_ARR_DBL_BAS = 1 + +'' +' Slice an array from source to destination starting at index and count slices +' +' @param DOUBLE() source_arr# to slice from +' @param DOUBLE() dest_arr# to put slices into +' @param INTEGER start_idx% starting index to use as slice range +' @param INTEGER count% number of slices - if negative, backwards from index +' +SUB ARR_DBL.slice(source_arr#(), dest_arr#(), start_idx%, count%) + DIM AS LONG ub, lb, i, n + lb& = LBOUND(source_arr#) : ub& = UBOUND(source_arr#) + IF start_idx% < lb& OR start_idx% + count% > ub& THEN EXIT SUB ' out of range + IF ub& - lb& < count% THEN EXIT SUB ' too many and not enough + REDIM dest_arr(0 TO ABS(count%)) AS DOUBLE + IF SGN(count%) = -1 THEN + IF ((start_idx% - 1) - ABS(count%)) < 0 THEN EXIT SUB ' out of range + n& = 0 + FOR i& = (start_idx% - 1) TO ((start_idx% - 1) - ABS(count%)) STEP -1 + dest_arr#(n&) = source_arr#(i&) + n& = n& + 1 + NEXT i& + ELSE + IF ((start_idx% + 1) + ABS(count%)) > (ub& - lb&) THEN EXIT SUB ' out of range + n& = 0 + FOR i& = start_idx% + 1 TO ((start_idx% + 1) + count%) STEP 1 + dest_arr#(n&) = source_arr#(i&) + n& = n& + 1 + NEXT i& + END IF +END SUB + + +'' +' Push a dbl onto the end of a DOUBLE array +' +' @param DOUBLE arr#() array to push into +' @param DOUBLE value# of byte to push +' +SUB ARR_DBL.push(arr#(), value#) + DIM AS LONG ub, lb + lb& = LBOUND(arr#) : ub& = UBOUND(arr#) + REDIM _PRESERVE arr(lb& TO (ub& + 1)) AS DOUBLE + arr#(ub& + 1) = value# +END SUB + + +'' +' Pop a dbl from the end of a DOUBLE array +' +' @param DOUBLE arr#() array to pop from +' @param DOUBLE var# of dbl to store popped dbl +' +SUB ARR_DBL.pop(arr#(), var#) + DIM AS LONG ub, lb + lb& = LBOUND(arr#) : ub& = UBOUND(arr#) + var# = arr#(ub&) + REDIM _PRESERVE arr(lb& TO (ub& - 1)) AS DOUBLE +END SUB + + +'' +' Pop a dbl from the beginning of a DOUBLE array +' +' @param DOUBLE arr#() array to pop from +' @param DOUBLE var# of dbl to store popped dbl +' +SUB ARR_DBL.shift(arr#(), var#) + DIM AS LONG ub, lb, i + lb& = LBOUND(arr#) : ub& = UBOUND(arr#) + var# = arr#(lb&) + FOR i& = lb& TO ub& - 1 + arr#(i&) = arr#(i& + 1) + NEXT i& + REDIM _PRESERVE arr(lb& + 1 TO ub&) AS DOUBLE +END SUB + + +'' +' Copy an array of DBLs to another DOUBLE array +' +' @param DOUBLE ARRAY source_arr#() source array to copy +' @param DOUBLE ARRAY dest_arr#() dest array to copy into +' +SUB ARR_DBL.copy(source_arr#(), dest_arr#()) + DIM AS LONG ub, lb, i + lb& = LBOUND(source_arr#) : ub& = UBOUND(source_arr#) + REDIM dest_arr(lb& TO ub&) AS DOUBLE + FOR i& = lb& TO ub& + dest_arr#(i&) = source_arr#(i&) + NEXT i& +END SUB + + +'' +' Push a dbl into the beginning of a DOUBLE array +' +' @param DOUBLE arr#() array to push into +' @param DOUBLE value# of dbl to push +' +SUB ARR_DBL.unshift(arr#(), value#) + DIM AS LONG ub, lb, i + lb& = LBOUND(arr#) : ub& = UBOUND(arr#) + DIM work(lb& TO ub&) AS DOUBLE + CALL ARR_DBL.copy(arr#(), work#()) + REDIM _PRESERVE arr(lb& TO (ub& + 1)) AS DOUBLE + FOR i& = lb& + 1 TO ub& + 1 + arr#(i&) = work#(i& - 1) + NEXT i& + arr#(lb&) = value# +END SUB + + +'' +' Joins an array of DBLs as a string +' +' @param DOUBLE ARRAY arr#() to get as a string +' @param STRING s$ to store stringified array in +' +SUB ARR_DBL.join(arr#(), s$) + DIM AS LONG ub, lb, i + s$ = "" + lb& = LBOUND(arr#) : ub& = UBOUND(arr#) + FOR i& = lb& TO ub& + s$ = s$ + _TRIM$(STR$(arr#(i&))) + ", " + NEXT i& + ' remove trailing comma + s$ = MID$(s$, 1, (LEN(s$)-LEN(", "))) +END SUB + + +'' +' Create a new DOUBLE array using string of dbls seperated by commas +' +' @param DOUBLE ARRAY arr#() to store the dbls in +' @param STRING s$ string of comma separated dbls +' +SUB ARR_DBL.new(arr#(), s$) + DIM AS LONG i, count + DIM t AS STRING + count& = 0 + FOR i& = 1 TO LEN(s$) + IF ASC(s$, i&) = ASC(",") THEN count& = count& + 1 + NEXT i& + REDIM arr(0 TO count&) AS DOUBLE + IF count& = 0 THEN + arr#(0) = VAL(s$) + EXIT SUB + END IF + count& = 0 + FOR i& = 1 TO LEN(s$) + t$ = t$ + CHR$(ASC(s$, i&)) + IF ASC(s$, i&) = ASC(",") OR i& = LEN(s$) THEN + arr#(count&) = VAL(t$) + count& = count& + 1 + t$ = "" + END IF + NEXT i& +END SUB + + +'' +' Return the visually longest element of a DOUBLE array +' +' @param DOUBLE ARRAY arr#() to check in +' @return DOUBLE value of visually longest element +' +FUNCTION ARR_DBL.longest#(arr#()) + DIM AS LONG lb, ub, i, res, lw + lb& = LBOUND(arr#) : ub& = UBOUND(arr#) + res& = 0 : lw& = 0 + FOR i& = lb& TO ub& + IF LEN(_TRIM$(STR$(arr#(i&)))) > lw& THEN + lw& = LEN(_TRIM$(STR$(arr#(i&)))) + res& = i& + END IF + NEXT i& + ARR_DBL.longest# = arr#(res&) +END FUNCTION + + +'' +' Perform some math on every element of a DOUBLE array +' +' @param DOUBLE ARRAY source_arr#() to do math on +' @param DOUBLE ARRAY dest_arr#() to store results in +' @param STRING op$ one of: +' and or xor shl shr +' "+", "-", "*", "\", "&&", "||", "!!", "<<", ">>" +' @param DOUBLE value# to use for operand +' +SUB ARR_DBL.math(source_arr#(), dest_arr#(), op$, value#) + DIM AS LONG lb, ub, i + lb& = LBOUND(source_arr#) : ub& = UBOUND(source_arr#) + REDIM dest_arr(lb& TO ub&) AS DOUBLE + FOR i& = lb& TO ub& + SELECT CASE op$ + CASE "+": + dest_arr#(i&) = source_arr#(i&) + value# + CASE "-": + dest_arr#(i&) = source_arr#(i&) - value# + CASE "*": + dest_arr#(i&) = source_arr#(i&) * value# + CASE "\": + IF value# > 0 THEN + dest_arr#(i&) = source_arr#(i&) \ value# + END IF + CASE "&&": + dest_arr#(i&) = source_arr#(i&) AND value# + CASE "||": + dest_arr#(i&) = source_arr#(i&) OR value# + CASE "!!": + dest_arr#(i&) = source_arr#(i&) XOR value# + CASE "<<": + dest_arr#(i&) = _SHL(source_arr#(i&), value#) + CASE ">>": + dest_arr#(i&) = _SHR(source_arr#(i&), value#) + END SELECT + NEXT i& +END SUB + + +'' +' Return the minimum element value in DOUBLE array +' +' @param DOUBLE ARRAY arr#() to check in +' @return DOUBLE minimum value found +' +FUNCTION ARR_DBL.min#(arr#()) + DIM AS LONG lb, ub, i + DIM AS DOUBLE s + lb& = LBOUND(arr#) : ub& = UBOUND(arr#) + s# = 127 + FOR i& = lb& TO ub& + IF arr#(i&) < s# THEN + s# = arr#(i&) + END IF + NEXT i& + ARR_DBL.min# = s# +END FUNCTION + + +'' +' Return the maximum element value in DOUBLE array +' +' @param DOUBLE ARRAY arr#() to check in +' @return DOUBLE maximum value found +' +FUNCTION ARR_DBL.max#(arr#()) + DIM AS LONG lb, ub, i + DIM AS DOUBLE s + lb& = LBOUND(arr#) : ub& = UBOUND(arr#) + s# = 0 + FOR i& = lb& TO ub& + IF arr#(i&) > s# THEN + s# = arr#(i&) + END IF + NEXT i& + ARR_DBL.max# = s# +END FUNCTION + + +'' +' Return the visually shortest element of a DOUBLE array +' +' @param DOUBLE ARRAY arr#() to check in +' @return DOUBLE value of visually shortest element +' +FUNCTION ARR_DBL.shortest#(arr#()) + DIM AS LONG lb, ub, i, res, sw + lb& = LBOUND(arr#) : ub& = UBOUND(arr#) + res& = 0 : sw& = 0 + FOR i& = lb& TO ub& + IF LEN(_TRIM$(STR$(arr#(i&)))) < sw& THEN + sw& = LEN(_TRIM$(STR$(arr#(i&)))) + res& = i& + END IF + NEXT i& + ARR_DBL.shortest# = arr#(res&) +END FUNCTION + + +'' +' Return the first element of a DOUBLE array +' +' @param DOUBLE ARRAY arr#() to check in +' @return DOUBLE value of first element +' +FUNCTION ARR_DBL.first#(arr#()) + ARR_DBL.first# = arr#(LBOUND(arr#)) +END FUNCTION + + +'' +' Return the last element of a DOUBLE array +' +' @param DOUBLE ARRAY arr#() to check in +' @return DOUBLE value of last element +' +FUNCTION ARR_DBL.last#(arr#()) + ARR_DBL.last# = arr#(UBOUND(arr#)) +END FUNCTION + + +'' +' Return every nth array element of a DOUBLE array +' +' @param DOUBLE ARRAY source_arr#() to get from +' @param DOUBLE ARRAY dest_arr#() to store in +' @param INTEGER nth% element +' +SUB ARR_DBL.nth(source_arr#(), dest_arr#(), nth%) + DIM AS LONG lb, ub, i, n + lb& = LBOUND(source_arr#) : ub& = UBOUND(source_arr#) + n& = (ub& - lb&) \ nth% + REDIM dest_arr(n&) AS DOUBLE + n& = 0 + FOR i& = lb& to ub& + IF i& MOD nth% = 0 THEN + dest_arr#(n&) = source_arr#(i&) + n& = n& + 1 + END IF + NEXT i& +END SUB + + +'' +' Checks if value exists in DOUBLE array +' +' @param DOUBLE ARRAY arr#() to check in +' @param DOUBLE value# value to check for +' @return INTEGER -1 if found or 0 if not found +' +FUNCTION ARR_DBL.in%(arr#(), value#) + DIM AS LONG lb, ub, i + lb& = LBOUND(arr#) : ub& = UBOUND(arr#) + FOR i& = lb& TO ub& + IF arr#(i&) = value# THEN + ARR_DBL.in% = -1 + EXIT FUNCTION + END IF + NEXT i& + ARR_DBL.in% = 0 +END FUNCTION + + +'' +' Checks if value exists in DOUBLE array and returns index if found +' +' @param DOUBLE ARRAY arr#() to check in +' @param DOUBLE value# value to check for +' @return INTEGER index of element if found or -1 if not found +' +FUNCTION ARR_DBL.find%(arr#(), value#) + DIM AS LONG lb, ub, i + lb& = LBOUND(arr#) : ub& = UBOUND(arr#) + FOR i& = lb& TO ub& + IF arr#(i&) = value# THEN + ARR_DBL.find% = i& + EXIT FUNCTION + END IF + NEXT i& + ARR_DBL.find% = -1 +END FUNCTION + + +'' +' Return the number of elements in a DOUBLE array +' +' @param DOUBLE ARRAY arr#() to count +' @return INTEGER number of elements +' +FUNCTION ARR_DBL.count&(arr#()) + ARR_DBL.count& = UBOUND(arr#) - LBOUND(arr#) +END FUNCTION + + +'' +' Return the size of a DOUBLE array +' +' @param DOUBLE ARRAY arr#() to get size of +' @return LONG size in bytes +' +FUNCTION ARR_DBL.size&(arr#()) + ARR_DBL.size& = LEN(arr#()) +END FUNCTION + + +'' +' Reverses the elements of a DOUBLE array +' +' @param DOUBLE ARRAY source_arr#() to reverse +' @param DOUBLE ARRAY dest_arr#() to store reversed array in +' +SUB ARR_DBL.reverse(source_arr#(), dest_arr#()) + DIM AS LONG lb, ub, i, n + lb& = LBOUND(source_arr#) : ub& = UBOUND(source_arr#) + REDIM dest_arr(0 TO (ub& - lb&)) AS DOUBLE + n& = 0 + FOR i& = ub& TO lb& STEP -1 + dest_arr#(n&) = source_arr#(i&) + n& = n& + 1 + NEXT i& +END SUB + + +'' +' Returns a random dbl from a DOUBLE array +' +' @param DOUBLE ARRAY arr#() array to get random element from +' @return DOUBLE random element +' +FUNCTION ARR_DBL.random#(arr#()) + DIM AS LONG lb, ub + lb& = LBOUND(arr#) : ub& = UBOUND(arr#) + RANDOMIZE TIMER + ARR_DBL.random# = arr#(INT(RND * (ub& - lb&)) + 1) +END FUNCTION + + +'' +' Returns the sum of all elements in a DOUBLE array +' +' @param DOUBLE ARRAY arr#() array to get some for +' @return LONG sum of all elements +' +FUNCTION ARR_DBL.sum&(arr#()) + DIM AS LONG lb, ub, i + DIM sum AS LONG + lb& = LBOUND(arr#) : ub& = UBOUND(arr#) + FOR i& = lb& TO ub& + sum& = sum& + arr#(i&) + NEXT i& + ARR_DBL.sum& = sum& +END FUNCTION + + +'' +' Returns the average value of elements in a DOUBLE array +' +' @param DOUBLE ARRAY arr#() array to get average for +' @return LONG average of elements +' +FUNCTION ARR_DBL.avg&(arr#()) + DIM AS LONG lb, ub, i + DIM sum AS LONG + lb& = LBOUND(arr#) : ub& = UBOUND(arr#) + FOR i& = lb& TO ub& + sum& = sum& + arr#(i&) + NEXT i& + ARR_DBL.avg& = sum& / (ub& - lb&) +END FUNCTION + + +'' +' Shuffle the elements of a DOUBLE array +' +' @param DOUBLE ARRAY source_arr#() to shuffle +' @param DOUBLE ARRAY dest_arr#() to store shuffled array in +' +SUB ARR_DBL.shuffle(source_arr#(), dest_arr#()) + DIM AS LONG lb, ub, i, count + lb& = LBOUND(source_arr#) : ub& = UBOUND(source_arr#) + count& = ub& - lb& + REDIM dest_arr(0 TO count&) AS DOUBLE + CALL ARR_DBL.copy(source_arr#(), dest_arr#()) + RANDOMIZE TIMER + FOR i& = 0 TO count& + SWAP dest_arr#(i&), dest_arr#(lb& + RND * (ub& - lb&)) + NEXT i& +END SUB + + +'' +' Makes a DOUBLE array contain only unique values +' +' @param DOUBLE ARRAY source_arr#() array to get uniques for +' @param DOUBLE ARRAY dest_arr#() array to store uniques in +' +SUB ARR_DBL.unique(source_arr#(), dest_arr#()) + DIM AS LONG lb, ub, i + DIM tmp AS DOUBLE + lb& = LBOUND(source_arr#) : ub& = UBOUND(source_arr#) + DIM work_arr(0) AS DOUBLE + FOR i& = lb& TO ub& + IF NOT ARR_DBL.in%(work_arr#(), source_arr#(i&)) THEN + CALL ARR_DBL.push(work_arr#(), source_arr#(i&)) + END IF + NEXT i& + CALL ARR_DBL.shift(work_arr#(), tmp#) + CALL ARR_DBL.copy(work_arr#(), dest_arr#()) +END SUB + + +'' +' Filters a DOUBLE array to only elements greater than value +' +' @param DOUBLE ARRAY source_arr#() array to work on +' @param DOUBLE ARRAY dest_arr#() array to store in +' @param DOUBLE value# to be greater than to be returned +' +SUB ARR_DBL.gt(source_arr#(), dest_arr#(), value#) + DIM AS LONG lb, ub, i + DIM tmp AS DOUBLE + lb& = LBOUND(source_arr#) : ub& = UBOUND(source_arr#) + DIM work_arr(0) AS DOUBLE + FOR i& = lb& TO ub& + IF source_arr#(i&) > value# THEN + CALL ARR_DBL.push(work_arr#(), source_arr#(i&)) + END IF + NEXT i& + CALL ARR_DBL.shift(work_arr#(), tmp#) + CALL ARR_DBL.copy(work_arr#(), dest_arr#()) +END SUB + + +'' +' Filters a DOUBLE array to only elements greater than or equal to value +' +' @param DOUBLE ARRAY source_arr#() array to work on +' @param DOUBLE ARRAY dest_arr#() array to store in +' @param DOUBLE value# to be greater than or equal to be returned +' +SUB ARR_DBL.gte(source_arr#(), dest_arr#(), value#) + DIM AS LONG lb, ub, i + DIM tmp AS DOUBLE + lb& = LBOUND(source_arr#) : ub& = UBOUND(source_arr#) + DIM work_arr(0) AS DOUBLE + FOR i& = lb& TO ub& + IF source_arr#(i&) >= value# THEN + CALL ARR_DBL.push(work_arr#(), source_arr#(i&)) + END IF + NEXT i& + CALL ARR_DBL.shift(work_arr#(), tmp#) + CALL ARR_DBL.copy(work_arr#(), dest_arr#()) +END SUB + + +'' +' Filters a DOUBLE array to only elements less than value +' +' @param DOUBLE ARRAY source_arr#() array to work on +' @param DOUBLE ARRAY dest_arr#() array to store in +' @param DOUBLE value# to be less than to be returned +' +SUB ARR_DBL.lt(source_arr#(), dest_arr#(), value#) + DIM AS LONG lb, ub, i + DIM tmp AS DOUBLE + lb& = LBOUND(source_arr#) : ub& = UBOUND(source_arr#) + DIM work_arr(0) AS DOUBLE + FOR i& = lb& TO ub& + IF source_arr#(i&) < value# THEN + CALL ARR_DBL.push(work_arr#(), source_arr#(i&)) + END IF + NEXT i& + CALL ARR_DBL.shift(work_arr#(), tmp#) + CALL ARR_DBL.copy(work_arr#(), dest_arr#()) +END SUB + + +'' +' Filters a DOUBLE array to only elements less than or equal to value +' +' @param DOUBLE ARRAY source_arr#() array to work on +' @param DOUBLE ARRAY dest_arr#() array to store in +' @param DOUBLE value# to be less than or equal to be returned +' +SUB ARR_DBL.lte(source_arr#(), dest_arr#(), value#) + DIM AS LONG lb, ub, i + DIM tmp AS DOUBLE + lb& = LBOUND(source_arr#) : ub& = UBOUND(source_arr#) + DIM work_arr(0) AS DOUBLE + FOR i& = lb& TO ub& + IF source_arr#(i&) <= value# THEN + CALL ARR_DBL.push(work_arr#(), source_arr#(i&)) + END IF + NEXT i& + CALL ARR_DBL.shift(work_arr#(), tmp#) + CALL ARR_DBL.copy(work_arr#(), dest_arr#()) +END SUB + + +'' +' Finds and replaces values across all elements in a DOUBLE ARRAY +' +' @param DOUBLE ARRAY arr#() to check in +' @param DOUBLE find# value to find +' @param DOUBLE replace# value to replace with if found +' +SUB ARR_DBL.replace(arr#(), find#, replace#) + DIM AS LONG lb, ub, i + lb& = LBOUND(arr#) : ub& = UBOUND(arr#) + FOR i& = lb& TO ub& + IF arr#(i&) = find# THEN + arr#(i&) = replace# + END IF + NEXT i& +END SUB + + +'' +' Inserts a new element into DOUBLE array after index +' +' @param DOUBLE ARRAY arr#() array to work on +' @param DOUBLE value# to insert +' @param INTEGER index% of element to insert at +' +SUB ARR_DBL.insert(arr#(), value#, index%) + DIM AS LONG lb, ub, i + DIM tmp AS DOUBLE + DIM work_arr(0) AS DOUBLE + lb& = LBOUND(arr#) : ub& = UBOUND(arr#) + IF index% >= lb& AND index% <= ub& THEN + ' build new array up to index + FOR i& = lb& TO index% + CALL ARR_DBL.push(work_arr#(), arr#(i&)) + NEXT i& + ' insert new element + CALL ARR_DBL.push(work_arr#(), value#) + ' finish building array from index + 1 + FOR i& = index% + 1 TO ub& + CALL ARR_DBL.push(work_arr#(), arr#(i&)) + NEXT i& + CALL ARR_DBL.shift(work_arr#(), tmp#) + CALL ARR_DBL.copy(work_arr#(), arr#()) + END IF +END SUB + + +'' +' Removes element from a DOUBLE array by element index +' +' @param DOUBLE ARRAY arr#() array to work on +' @param INTEGER index% of element to remove +' +SUB ARR_DBL.remove(arr#(), index%) + DIM AS LONG lb, ub, i + DIM tmp AS DOUBLE + DIM work_arr(0) AS DOUBLE + lb& = LBOUND(arr#) : ub& = UBOUND(arr#) + IF index% >= lb& AND index% <= ub& THEN + ' build new array up to index + FOR i& = lb& TO index% - 1 + CALL ARR_DBL.push(work_arr#(), arr#(i&)) + NEXT i& + ' skip elements + FOR i& = index% + 1 TO ub& + CALL ARR_DBL.push(work_arr#(), arr#(i&)) + NEXT i& + CALL ARR_DBL.shift(work_arr#(), tmp#) + CALL ARR_DBL.copy(work_arr#(), arr#()) + END IF +END SUB + + +'' +' Filters a DOUBLE array to only elements that have odd values +' +' @param DOUBLE ARRAY source_arr#() array to work on +' @param DOUBLE ARRAY dest_arr#() array to store in +' +SUB ARR_DBL.odd(source_arr#(), dest_arr#()) + DIM AS LONG lb, ub, i + DIM tmp AS DOUBLE + lb& = LBOUND(source_arr#) : ub& = UBOUND(source_arr#) + DIM work_arr(0) AS DOUBLE + FOR i& = lb& TO ub& + IF source_arr#(i&) MOD 2 <> 0 THEN + CALL ARR_DBL.push(work_arr#(), source_arr#(i&)) + END IF + NEXT i& + CALL ARR_DBL.shift(work_arr#(), tmp#) + CALL ARR_DBL.copy(work_arr#(), dest_arr#()) +END SUB + + +'' +' Filters a DOUBLE array to only elements that have even values +' +' @param DOUBLE ARRAY source_arr#() array to work on +' @param DOUBLE ARRAY dest_arr#() array to store in +' +SUB ARR_DBL.even(source_arr#(), dest_arr#()) + DIM AS LONG lb, ub, i + DIM tmp AS DOUBLE + lb& = LBOUND(source_arr#) : ub& = UBOUND(source_arr#) + DIM work_arr(0) AS DOUBLE + FOR i& = lb& TO ub& + IF source_arr#(i&) MOD 2 = 0 THEN + CALL ARR_DBL.push(work_arr#(), source_arr#(i&)) + END IF + NEXT i& + CALL ARR_DBL.shift(work_arr#(), tmp#) + CALL ARR_DBL.copy(work_arr#(), dest_arr#()) +END SUB + + +'' +' Filters a DOUBLE array to only elements that have values evenly divisible by divisor +' +' @param DOUBLE ARRAY source_arr#() array to work on +' @param DOUBLE ARRAY dest_arr#() array to store in +' @param DOUBLE divisor# for modulo +' +SUB ARR_DBL.mod(source_arr#(), dest_arr#(), divisor#) + DIM AS LONG lb, ub, i + DIM tmp AS DOUBLE + lb& = LBOUND(source_arr#) : ub& = UBOUND(source_arr#) + DIM work_arr(0) AS DOUBLE + FOR i& = lb& TO ub& + IF source_arr#(i&) MOD divisor# = 0 THEN + CALL ARR_DBL.push(work_arr#(), source_arr#(i&)) + END IF + NEXT i& + CALL ARR_DBL.shift(work_arr#(), tmp#) + CALL ARR_DBL.copy(work_arr#(), dest_arr#()) +END SUB + + +'' +' Filters a DOUBLE array to only elements between min and max +' +' @param DOUBLE ARRAY source_arr#() array to work on +' @param DOUBLE ARRAY dest_arr#() array to store in +' @param DOUBLE min# to be greater than or equal to be returned +' @param DOUBLE max# to be less than or equal to be returned +' +SUB ARR_DBL.between(source_arr#(), dest_arr#(), min#, max#) + DIM AS LONG lb, ub, i + DIM tmp AS DOUBLE + lb& = LBOUND(source_arr#) : ub& = UBOUND(source_arr#) + DIM work_arr(0) AS DOUBLE + FOR i& = lb& TO ub& + IF source_arr#(i&) >= min# _ + AND source_arr#(i&) <= max# THEN + CALL ARR_DBL.push(work_arr#(), source_arr#(i&)) + END IF + NEXT i& + CALL ARR_DBL.shift(work_arr#(), tmp#) + CALL ARR_DBL.copy(work_arr#(), dest_arr#()) +END SUB + + +'' +' Sorts DOUBLE array in ascending order +' +' @param DOUBLE ARRAY source_arr#() array to sort +' @param DOUBLE ARRAY dest_arr#() array to store sorted in +' +SUB ARR_DBL.sort(source_arr#(), dest_arr#()) + DIM AS LONG lb, ub, count + lb& = LBOUND(source_arr#) : ub& = UBOUND(source_arr#) + count& = ub& - lb& + REDIM dest_arr(0 TO count&) AS DOUBLE + CALL ARR_DBL.copy(source_arr#(), dest_arr#()) + CALL ARR_DBL.quicksort(dest_arr#(), lb&, ub&, 0) +END SUB + + +'' +' Sorts DOUBLE array in descending order +' +' @param DOUBLE ARRAY source_arr#() array to sort +' @param DOUBLE ARRAY dest_arr#() array to store sorted in +' +SUB ARR_DBL.rsort(source_arr#(), dest_arr#()) + DIM AS LONG lb, ub, count + lb& = LBOUND(source_arr#) : ub& = UBOUND(source_arr#) + count& = ub& - lb& + REDIM dest_arr(0 TO count&) AS DOUBLE + CALL ARR_DBL.copy(source_arr#(), dest_arr#()) + CALL ARR_DBL.quicksort(dest_arr#(), lb&, ub&, 1) +END SUB + + +'' +' Quicksort array with pivot algorithm by logiclrd +' +' @link https://www.tek-tips.com/faqs.cfm?fid=336 +' @param DOUBLE ARRAY array#() to sort +' @param INTEGER start% of range to sort +' @param INTEGER finish% range of sort +' @param INTEGER order% to sort by (0 = asc / 1 = desc) +' +SUB ARR_DBL.quicksort(arr#(), start%, finish%, order%) + DIM i AS LONG + DIM pivot AS INTEGER + DIM pivotvalue AS DOUBLE + + 'first, partition the array + pivot% = start% + pivotvalue# = arr#(pivot%) + FOR i& = start% + 1 TO finish% + IF order% = 0 THEN ' ascending order + IF arr#(i&) < pivotvalue# THEN + arr#(pivot%) = arr#(i&) + arr#(i&) = arr#(pivot% + 1) + arr#(pivot% + 1) = pivotvalue# + pivot% = pivot% + 1 + END IF + ELSEIF order% = 1 THEN ' descending order + IF arr#(i&) > pivotvalue# THEN + arr#(pivot%) = arr#(i&) + arr#(i&) = arr#(pivot% + 1) + arr#(pivot% + 1) = pivotvalue# + pivot% = pivot% + 1 + END IF + END IF + NEXT i& + + 'then, sort the subarrays to each side of the pivot + IF pivot% - start% >= 2 THEN + CALL ARR_DBL.quicksort(arr#(), start%, pivot% - 1, order%) + END IF + IF finish% - pivot% >= 2 THEN + CALL ARR_DBL.quicksort(arr#(), pivot% + 1, finish%, order%) + END IF +END SUB diff --git a/ARR/ARR_FLT.BAS b/ARR/ARR_FLT.BAS new file mode 100644 index 0000000..15058ed --- /dev/null +++ b/ARR/ARR_FLT.BAS @@ -0,0 +1,820 @@ +'$DYNAMIC +$LET GJ_LIB_ARR_FLT_BAS = 1 + +'' +' Slice an array from source to destination starting at index and count slices +' +' @param _FLOAT() source_arr## to slice from +' @param _FLOAT() dest_arr## to put slices into +' @param INTEGER start_idx% starting index to use as slice range +' @param INTEGER count% number of slices - if negative, backwards from index +' +SUB ARR_FLT.slice(source_arr##(), dest_arr##(), start_idx%, count%) + DIM AS LONG ub, lb, i, n + lb& = LBOUND(source_arr##) : ub& = UBOUND(source_arr##) + IF start_idx% < lb& OR start_idx% + count% > ub& THEN EXIT SUB ' out of range + IF ub& - lb& < count% THEN EXIT SUB ' too many and not enough + REDIM dest_arr(0 TO ABS(count%)) AS _FLOAT + IF SGN(count%) = -1 THEN + IF ((start_idx% - 1) - ABS(count%)) < 0 THEN EXIT SUB ' out of range + n& = 0 + FOR i& = (start_idx% - 1) TO ((start_idx% - 1) - ABS(count%)) STEP -1 + dest_arr##(n&) = source_arr##(i&) + n& = n& + 1 + NEXT i& + ELSE + IF ((start_idx% + 1) + ABS(count%)) > (ub& - lb&) THEN EXIT SUB ' out of range + n& = 0 + FOR i& = start_idx% + 1 TO ((start_idx% + 1) + count%) STEP 1 + dest_arr##(n&) = source_arr##(i&) + n& = n& + 1 + NEXT i& + END IF +END SUB + + +'' +' Push a flt onto the end of a _FLOAT array +' +' @param _FLOAT arr##() array to push into +' @param _FLOAT value## of byte to push +' +SUB ARR_FLT.push(arr##(), value##) + DIM AS LONG ub, lb + lb& = LBOUND(arr##) : ub& = UBOUND(arr##) + REDIM _PRESERVE arr(lb& TO (ub& + 1)) AS _FLOAT + arr##(ub& + 1) = value## +END SUB + + +'' +' Pop a flt from the end of a _FLOAT array +' +' @param _FLOAT arr##() array to pop from +' @param _FLOAT var## of flt to store popped flt +' +SUB ARR_FLT.pop(arr##(), var##) + DIM AS LONG ub, lb + lb& = LBOUND(arr##) : ub& = UBOUND(arr##) + var## = arr##(ub&) + REDIM _PRESERVE arr(lb& TO (ub& - 1)) AS _FLOAT +END SUB + + +'' +' Pop a flt from the beginning of a _FLOAT array +' +' @param _FLOAT arr##() array to pop from +' @param _FLOAT var## of flt to store popped flt +' +SUB ARR_FLT.shift(arr##(), var##) + DIM AS LONG ub, lb, i + lb& = LBOUND(arr##) : ub& = UBOUND(arr##) + var## = arr##(lb&) + FOR i& = lb& TO ub& - 1 + arr##(i&) = arr##(i& + 1) + NEXT i& + REDIM _PRESERVE arr(lb& + 1 TO ub&) AS _FLOAT +END SUB + + +'' +' Copy an array of FLTs to another _FLOAT array +' +' @param _FLOAT ARRAY source_arr##() source array to copy +' @param _FLOAT ARRAY dest_arr##() dest array to copy into +' +SUB ARR_FLT.copy(source_arr##(), dest_arr##()) + DIM AS LONG ub, lb, i + lb& = LBOUND(source_arr##) : ub& = UBOUND(source_arr##) + REDIM dest_arr(lb& TO ub&) AS _FLOAT + FOR i& = lb& TO ub& + dest_arr##(i&) = source_arr##(i&) + NEXT i& +END SUB + + +'' +' Push a flt into the beginning of a _FLOAT array +' +' @param _FLOAT arr##() array to push into +' @param _FLOAT value## of flt to push +' +SUB ARR_FLT.unshift(arr##(), value##) + DIM AS LONG ub, lb, i + lb& = LBOUND(arr##) : ub& = UBOUND(arr##) + DIM work(lb& TO ub&) AS _FLOAT + CALL ARR_FLT.copy(arr##(), work##()) + REDIM _PRESERVE arr(lb& TO (ub& + 1)) AS _FLOAT + FOR i& = lb& + 1 TO ub& + 1 + arr##(i&) = work##(i& - 1) + NEXT i& + arr##(lb&) = value## +END SUB + + +'' +' Joins an array of FLTs as a string +' +' @param _FLOAT ARRAY arr##() to get as a string +' @param STRING s$ to store stringified array in +' +SUB ARR_FLT.join(arr##(), s$) + DIM AS LONG ub, lb, i + s$ = "" + lb& = LBOUND(arr##) : ub& = UBOUND(arr##) + FOR i& = lb& TO ub& + s$ = s$ + _TRIM$(STR$(arr##(i&))) + ", " + NEXT i& + ' remove trailing comma + s$ = MID$(s$, 1, (LEN(s$)-LEN(", "))) +END SUB + + +'' +' Create a new _FLOAT array using string of flts seperated by commas +' +' @param _FLOAT ARRAY arr##() to store the flts in +' @param STRING s$ string of comma separated flts +' +SUB ARR_FLT.new(arr##(), s$) + DIM AS LONG i, count + DIM t AS STRING + count& = 0 + FOR i& = 1 TO LEN(s$) + IF ASC(s$, i&) = ASC(",") THEN count& = count& + 1 + NEXT i& + REDIM arr(0 TO count&) AS _FLOAT + IF count& = 0 THEN + arr##(0) = VAL(s$) + EXIT SUB + END IF + count& = 0 + FOR i& = 1 TO LEN(s$) + t$ = t$ + CHR$(ASC(s$, i&)) + IF ASC(s$, i&) = ASC(",") OR i& = LEN(s$) THEN + arr##(count&) = VAL(t$) + count& = count& + 1 + t$ = "" + END IF + NEXT i& +END SUB + + +'' +' Return the visually longest element of a _FLOAT array +' +' @param _FLOAT ARRAY arr##() to check in +' @return _FLOAT value of visually longest element +' +FUNCTION ARR_FLT.longest##(arr##()) + DIM AS LONG lb, ub, i, res, lw + lb& = LBOUND(arr##) : ub& = UBOUND(arr##) + res& = 0 : lw& = 0 + FOR i& = lb& TO ub& + IF LEN(_TRIM$(STR$(arr##(i&)))) > lw& THEN + lw& = LEN(_TRIM$(STR$(arr##(i&)))) + res& = i& + END IF + NEXT i& + ARR_FLT.longest## = arr##(res&) +END FUNCTION + + +'' +' Perform some math on every element of a _FLOAT array +' +' @param _FLOAT ARRAY source_arr##() to do math on +' @param _FLOAT ARRAY dest_arr##() to store results in +' @param STRING op$ one of: +' and or xor shl shr +' "+", "-", "*", "\", "&&", "||", "!!", "<<", ">>" +' @param _FLOAT value## to use for operand +' +SUB ARR_FLT.math(source_arr##(), dest_arr##(), op$, value##) + DIM AS LONG lb, ub, i + lb& = LBOUND(source_arr##) : ub& = UBOUND(source_arr##) + REDIM dest_arr(lb& TO ub&) AS _FLOAT + FOR i& = lb& TO ub& + SELECT CASE op$ + CASE "+": + dest_arr##(i&) = source_arr##(i&) + value## + CASE "-": + dest_arr##(i&) = source_arr##(i&) - value## + CASE "*": + dest_arr##(i&) = source_arr##(i&) * value## + CASE "\": + IF value## > 0 THEN + dest_arr##(i&) = source_arr##(i&) \ value## + END IF + CASE "&&": + dest_arr##(i&) = source_arr##(i&) AND value## + CASE "||": + dest_arr##(i&) = source_arr##(i&) OR value## + CASE "!!": + dest_arr##(i&) = source_arr##(i&) XOR value## + CASE "<<": + dest_arr##(i&) = _SHL(source_arr##(i&), value##) + CASE ">>": + dest_arr##(i&) = _SHR(source_arr##(i&), value##) + END SELECT + NEXT i& +END SUB + + +'' +' Return the minimum element value in _FLOAT array +' +' @param _FLOAT ARRAY arr##() to check in +' @return _FLOAT minimum value found +' +FUNCTION ARR_FLT.min##(arr##()) + DIM AS LONG lb, ub, i + DIM AS _FLOAT s + lb& = LBOUND(arr##) : ub& = UBOUND(arr##) + s## = 127 + FOR i& = lb& TO ub& + IF arr##(i&) < s## THEN + s## = arr##(i&) + END IF + NEXT i& + ARR_FLT.min## = s## +END FUNCTION + + +'' +' Return the maximum element value in _FLOAT array +' +' @param _FLOAT ARRAY arr##() to check in +' @return _FLOAT maximum value found +' +FUNCTION ARR_FLT.max##(arr##()) + DIM AS LONG lb, ub, i + DIM AS _FLOAT s + lb& = LBOUND(arr##) : ub& = UBOUND(arr##) + s## = 0 + FOR i& = lb& TO ub& + IF arr##(i&) > s## THEN + s## = arr##(i&) + END IF + NEXT i& + ARR_FLT.max## = s## +END FUNCTION + + +'' +' Return the visually shortest element of a _FLOAT array +' +' @param _FLOAT ARRAY arr##() to check in +' @return _FLOAT value of visually shortest element +' +FUNCTION ARR_FLT.shortest##(arr##()) + DIM AS LONG lb, ub, i, res, sw + lb& = LBOUND(arr##) : ub& = UBOUND(arr##) + res& = 0 : sw& = 0 + FOR i& = lb& TO ub& + IF LEN(_TRIM$(STR$(arr##(i&)))) < sw& THEN + sw& = LEN(_TRIM$(STR$(arr##(i&)))) + res& = i& + END IF + NEXT i& + ARR_FLT.shortest## = arr##(res&) +END FUNCTION + + +'' +' Return the first element of a _FLOAT array +' +' @param _FLOAT ARRAY arr##() to check in +' @return _FLOAT value of first element +' +FUNCTION ARR_FLT.first##(arr##()) + ARR_FLT.first## = arr##(LBOUND(arr##)) +END FUNCTION + + +'' +' Return the last element of a _FLOAT array +' +' @param _FLOAT ARRAY arr##() to check in +' @return _FLOAT value of last element +' +FUNCTION ARR_FLT.last##(arr##()) + ARR_FLT.last## = arr##(UBOUND(arr##)) +END FUNCTION + + +'' +' Return every nth array element of a _FLOAT array +' +' @param _FLOAT ARRAY source_arr##() to get from +' @param _FLOAT ARRAY dest_arr##() to store in +' @param INTEGER nth% element +' +SUB ARR_FLT.nth(source_arr##(), dest_arr##(), nth%) + DIM AS LONG lb, ub, i, n + lb& = LBOUND(source_arr##) : ub& = UBOUND(source_arr##) + n& = (ub& - lb&) \ nth% + REDIM dest_arr(n&) AS _FLOAT + n& = 0 + FOR i& = lb& to ub& + IF i& MOD nth% = 0 THEN + dest_arr##(n&) = source_arr##(i&) + n& = n& + 1 + END IF + NEXT i& +END SUB + + +'' +' Checks if value exists in _FLOAT array +' +' @param _FLOAT ARRAY arr##() to check in +' @param _FLOAT value## value to check for +' @return INTEGER -1 if found or 0 if not found +' +FUNCTION ARR_FLT.in%(arr##(), value##) + DIM AS LONG lb, ub, i + lb& = LBOUND(arr##) : ub& = UBOUND(arr##) + FOR i& = lb& TO ub& + IF arr##(i&) = value## THEN + ARR_FLT.in% = -1 + EXIT FUNCTION + END IF + NEXT i& + ARR_FLT.in% = 0 +END FUNCTION + + +'' +' Checks if value exists in _FLOAT array and returns index if found +' +' @param _FLOAT ARRAY arr##() to check in +' @param _FLOAT value## value to check for +' @return INTEGER index of element if found or -1 if not found +' +FUNCTION ARR_FLT.find%(arr##(), value##) + DIM AS LONG lb, ub, i + lb& = LBOUND(arr##) : ub& = UBOUND(arr##) + FOR i& = lb& TO ub& + IF arr##(i&) = value## THEN + ARR_FLT.find% = i& + EXIT FUNCTION + END IF + NEXT i& + ARR_FLT.find% = -1 +END FUNCTION + + +'' +' Return the number of elements in a _FLOAT array +' +' @param _FLOAT ARRAY arr##() to count +' @return INTEGER number of elements +' +FUNCTION ARR_FLT.count&(arr##()) + ARR_FLT.count& = UBOUND(arr##) - LBOUND(arr##) +END FUNCTION + + +'' +' Return the size of a _FLOAT array +' +' @param _FLOAT ARRAY arr##() to get size of +' @return LONG size in bytes +' +FUNCTION ARR_FLT.size&(arr##()) + ARR_FLT.size& = LEN(arr##()) +END FUNCTION + + +'' +' Reverses the elements of a _FLOAT array +' +' @param _FLOAT ARRAY source_arr##() to reverse +' @param _FLOAT ARRAY dest_arr##() to store reversed array in +' +SUB ARR_FLT.reverse(source_arr##(), dest_arr##()) + DIM AS LONG lb, ub, i, n + lb& = LBOUND(source_arr##) : ub& = UBOUND(source_arr##) + REDIM dest_arr(0 TO (ub& - lb&)) AS _FLOAT + n& = 0 + FOR i& = ub& TO lb& STEP -1 + dest_arr##(n&) = source_arr##(i&) + n& = n& + 1 + NEXT i& +END SUB + + +'' +' Returns a random flt from a _FLOAT array +' +' @param _FLOAT ARRAY arr##() array to get random element from +' @return _FLOAT random element +' +FUNCTION ARR_FLT.random##(arr##()) + DIM AS LONG lb, ub + lb& = LBOUND(arr##) : ub& = UBOUND(arr##) + RANDOMIZE TIMER + ARR_FLT.random## = arr##(INT(RND * (ub& - lb&)) + 1) +END FUNCTION + + +'' +' Returns the sum of all elements in a _FLOAT array +' +' @param _FLOAT ARRAY arr##() array to get some for +' @return LONG sum of all elements +' +FUNCTION ARR_FLT.sum&(arr##()) + DIM AS LONG lb, ub, i + DIM sum AS LONG + lb& = LBOUND(arr##) : ub& = UBOUND(arr##) + FOR i& = lb& TO ub& + sum& = sum& + arr##(i&) + NEXT i& + ARR_FLT.sum& = sum& +END FUNCTION + + +'' +' Returns the average value of elements in a _FLOAT array +' +' @param _FLOAT ARRAY arr##() array to get average for +' @return LONG average of elements +' +FUNCTION ARR_FLT.avg&(arr##()) + DIM AS LONG lb, ub, i + DIM sum AS LONG + lb& = LBOUND(arr##) : ub& = UBOUND(arr##) + FOR i& = lb& TO ub& + sum& = sum& + arr##(i&) + NEXT i& + ARR_FLT.avg& = sum& / (ub& - lb&) +END FUNCTION + + +'' +' Shuffle the elements of a _FLOAT array +' +' @param _FLOAT ARRAY source_arr##() to shuffle +' @param _FLOAT ARRAY dest_arr##() to store shuffled array in +' +SUB ARR_FLT.shuffle(source_arr##(), dest_arr##()) + DIM AS LONG lb, ub, i, count + lb& = LBOUND(source_arr##) : ub& = UBOUND(source_arr##) + count& = ub& - lb& + REDIM dest_arr(0 TO count&) AS _FLOAT + CALL ARR_FLT.copy(source_arr##(), dest_arr##()) + RANDOMIZE TIMER + FOR i& = 0 TO count& + SWAP dest_arr##(i&), dest_arr##(lb& + RND * (ub& - lb&)) + NEXT i& +END SUB + + +'' +' Makes a _FLOAT array contain only unique values +' +' @param _FLOAT ARRAY source_arr##() array to get uniques for +' @param _FLOAT ARRAY dest_arr##() array to store uniques in +' +SUB ARR_FLT.unique(source_arr##(), dest_arr##()) + DIM AS LONG lb, ub, i + DIM tmp AS _FLOAT + lb& = LBOUND(source_arr##) : ub& = UBOUND(source_arr##) + DIM work_arr(0) AS _FLOAT + FOR i& = lb& TO ub& + IF NOT ARR_FLT.in%(work_arr##(), source_arr##(i&)) THEN + CALL ARR_FLT.push(work_arr##(), source_arr##(i&)) + END IF + NEXT i& + CALL ARR_FLT.shift(work_arr##(), tmp##) + CALL ARR_FLT.copy(work_arr##(), dest_arr##()) +END SUB + + +'' +' Filters a _FLOAT array to only elements greater than value +' +' @param _FLOAT ARRAY source_arr##() array to work on +' @param _FLOAT ARRAY dest_arr##() array to store in +' @param _FLOAT value## to be greater than to be returned +' +SUB ARR_FLT.gt(source_arr##(), dest_arr##(), value##) + DIM AS LONG lb, ub, i + DIM tmp AS _FLOAT + lb& = LBOUND(source_arr##) : ub& = UBOUND(source_arr##) + DIM work_arr(0) AS _FLOAT + FOR i& = lb& TO ub& + IF source_arr##(i&) > value## THEN + CALL ARR_FLT.push(work_arr##(), source_arr##(i&)) + END IF + NEXT i& + CALL ARR_FLT.shift(work_arr##(), tmp##) + CALL ARR_FLT.copy(work_arr##(), dest_arr##()) +END SUB + + +'' +' Filters a _FLOAT array to only elements greater than or equal to value +' +' @param _FLOAT ARRAY source_arr##() array to work on +' @param _FLOAT ARRAY dest_arr##() array to store in +' @param _FLOAT value## to be greater than or equal to be returned +' +SUB ARR_FLT.gte(source_arr##(), dest_arr##(), value##) + DIM AS LONG lb, ub, i + DIM tmp AS _FLOAT + lb& = LBOUND(source_arr##) : ub& = UBOUND(source_arr##) + DIM work_arr(0) AS _FLOAT + FOR i& = lb& TO ub& + IF source_arr##(i&) >= value## THEN + CALL ARR_FLT.push(work_arr##(), source_arr##(i&)) + END IF + NEXT i& + CALL ARR_FLT.shift(work_arr##(), tmp##) + CALL ARR_FLT.copy(work_arr##(), dest_arr##()) +END SUB + + +'' +' Filters a _FLOAT array to only elements less than value +' +' @param _FLOAT ARRAY source_arr##() array to work on +' @param _FLOAT ARRAY dest_arr##() array to store in +' @param _FLOAT value## to be less than to be returned +' +SUB ARR_FLT.lt(source_arr##(), dest_arr##(), value##) + DIM AS LONG lb, ub, i + DIM tmp AS _FLOAT + lb& = LBOUND(source_arr##) : ub& = UBOUND(source_arr##) + DIM work_arr(0) AS _FLOAT + FOR i& = lb& TO ub& + IF source_arr##(i&) < value## THEN + CALL ARR_FLT.push(work_arr##(), source_arr##(i&)) + END IF + NEXT i& + CALL ARR_FLT.shift(work_arr##(), tmp##) + CALL ARR_FLT.copy(work_arr##(), dest_arr##()) +END SUB + + +'' +' Filters a _FLOAT array to only elements less than or equal to value +' +' @param _FLOAT ARRAY source_arr##() array to work on +' @param _FLOAT ARRAY dest_arr##() array to store in +' @param _FLOAT value## to be less than or equal to be returned +' +SUB ARR_FLT.lte(source_arr##(), dest_arr##(), value##) + DIM AS LONG lb, ub, i + DIM tmp AS _FLOAT + lb& = LBOUND(source_arr##) : ub& = UBOUND(source_arr##) + DIM work_arr(0) AS _FLOAT + FOR i& = lb& TO ub& + IF source_arr##(i&) <= value## THEN + CALL ARR_FLT.push(work_arr##(), source_arr##(i&)) + END IF + NEXT i& + CALL ARR_FLT.shift(work_arr##(), tmp##) + CALL ARR_FLT.copy(work_arr##(), dest_arr##()) +END SUB + + +'' +' Finds and replaces values across all elements in a _FLOAT ARRAY +' +' @param _FLOAT ARRAY arr##() to check in +' @param _FLOAT find## value to find +' @param _FLOAT replace## value to replace with if found +' +SUB ARR_FLT.replace(arr##(), find##, replace##) + DIM AS LONG lb, ub, i + lb& = LBOUND(arr##) : ub& = UBOUND(arr##) + FOR i& = lb& TO ub& + IF arr##(i&) = find## THEN + arr##(i&) = replace## + END IF + NEXT i& +END SUB + + +'' +' Inserts a new element into _FLOAT array after index +' +' @param _FLOAT ARRAY arr##() array to work on +' @param _FLOAT value## to insert +' @param INTEGER index% of element to insert at +' +SUB ARR_FLT.insert(arr##(), value##, index%) + DIM AS LONG lb, ub, i + DIM tmp AS _FLOAT + DIM work_arr(0) AS _FLOAT + lb& = LBOUND(arr##) : ub& = UBOUND(arr##) + IF index% >= lb& AND index% <= ub& THEN + ' build new array up to index + FOR i& = lb& TO index% + CALL ARR_FLT.push(work_arr##(), arr##(i&)) + NEXT i& + ' insert new element + CALL ARR_FLT.push(work_arr##(), value##) + ' finish building array from index + 1 + FOR i& = index% + 1 TO ub& + CALL ARR_FLT.push(work_arr##(), arr##(i&)) + NEXT i& + CALL ARR_FLT.shift(work_arr##(), tmp##) + CALL ARR_FLT.copy(work_arr##(), arr##()) + END IF +END SUB + + +'' +' Removes element from a _FLOAT array by element index +' +' @param _FLOAT ARRAY arr##() array to work on +' @param INTEGER index% of element to remove +' +SUB ARR_FLT.remove(arr##(), index%) + DIM AS LONG lb, ub, i + DIM tmp AS _FLOAT + DIM work_arr(0) AS _FLOAT + lb& = LBOUND(arr##) : ub& = UBOUND(arr##) + IF index% >= lb& AND index% <= ub& THEN + ' build new array up to index + FOR i& = lb& TO index% - 1 + CALL ARR_FLT.push(work_arr##(), arr##(i&)) + NEXT i& + ' skip elements + FOR i& = index% + 1 TO ub& + CALL ARR_FLT.push(work_arr##(), arr##(i&)) + NEXT i& + CALL ARR_FLT.shift(work_arr##(), tmp##) + CALL ARR_FLT.copy(work_arr##(), arr##()) + END IF +END SUB + + +'' +' Filters a _FLOAT array to only elements that have odd values +' +' @param _FLOAT ARRAY source_arr##() array to work on +' @param _FLOAT ARRAY dest_arr##() array to store in +' +SUB ARR_FLT.odd(source_arr##(), dest_arr##()) + DIM AS LONG lb, ub, i + DIM tmp AS _FLOAT + lb& = LBOUND(source_arr##) : ub& = UBOUND(source_arr##) + DIM work_arr(0) AS _FLOAT + FOR i& = lb& TO ub& + IF source_arr##(i&) MOD 2 <> 0 THEN + CALL ARR_FLT.push(work_arr##(), source_arr##(i&)) + END IF + NEXT i& + CALL ARR_FLT.shift(work_arr##(), tmp##) + CALL ARR_FLT.copy(work_arr##(), dest_arr##()) +END SUB + + +'' +' Filters a _FLOAT array to only elements that have even values +' +' @param _FLOAT ARRAY source_arr##() array to work on +' @param _FLOAT ARRAY dest_arr##() array to store in +' +SUB ARR_FLT.even(source_arr##(), dest_arr##()) + DIM AS LONG lb, ub, i + DIM tmp AS _FLOAT + lb& = LBOUND(source_arr##) : ub& = UBOUND(source_arr##) + DIM work_arr(0) AS _FLOAT + FOR i& = lb& TO ub& + IF source_arr##(i&) MOD 2 = 0 THEN + CALL ARR_FLT.push(work_arr##(), source_arr##(i&)) + END IF + NEXT i& + CALL ARR_FLT.shift(work_arr##(), tmp##) + CALL ARR_FLT.copy(work_arr##(), dest_arr##()) +END SUB + + +'' +' Filters a _FLOAT array to only elements that have values evenly divisible by divisor +' +' @param _FLOAT ARRAY source_arr##() array to work on +' @param _FLOAT ARRAY dest_arr##() array to store in +' @param _FLOAT divisor## for modulo +' +SUB ARR_FLT.mod(source_arr##(), dest_arr##(), divisor##) + DIM AS LONG lb, ub, i + DIM tmp AS _FLOAT + lb& = LBOUND(source_arr##) : ub& = UBOUND(source_arr##) + DIM work_arr(0) AS _FLOAT + FOR i& = lb& TO ub& + IF source_arr##(i&) MOD divisor## = 0 THEN + CALL ARR_FLT.push(work_arr##(), source_arr##(i&)) + END IF + NEXT i& + CALL ARR_FLT.shift(work_arr##(), tmp##) + CALL ARR_FLT.copy(work_arr##(), dest_arr##()) +END SUB + + +'' +' Filters a _FLOAT array to only elements between min and max +' +' @param _FLOAT ARRAY source_arr##() array to work on +' @param _FLOAT ARRAY dest_arr##() array to store in +' @param _FLOAT min## to be greater than or equal to be returned +' @param _FLOAT max## to be less than or equal to be returned +' +SUB ARR_FLT.between(source_arr##(), dest_arr##(), min##, max##) + DIM AS LONG lb, ub, i + DIM tmp AS _FLOAT + lb& = LBOUND(source_arr##) : ub& = UBOUND(source_arr##) + DIM work_arr(0) AS _FLOAT + FOR i& = lb& TO ub& + IF source_arr##(i&) >= min## _ + AND source_arr##(i&) <= max## THEN + CALL ARR_FLT.push(work_arr##(), source_arr##(i&)) + END IF + NEXT i& + CALL ARR_FLT.shift(work_arr##(), tmp##) + CALL ARR_FLT.copy(work_arr##(), dest_arr##()) +END SUB + + +'' +' Sorts _FLOAT array in ascending order +' +' @param _FLOAT ARRAY source_arr##() array to sort +' @param _FLOAT ARRAY dest_arr##() array to store sorted in +' +SUB ARR_FLT.sort(source_arr##(), dest_arr##()) + DIM AS LONG lb, ub, count + lb& = LBOUND(source_arr##) : ub& = UBOUND(source_arr##) + count& = ub& - lb& + REDIM dest_arr(0 TO count&) AS _FLOAT + CALL ARR_FLT.copy(source_arr##(), dest_arr##()) + CALL ARR_FLT.quicksort(dest_arr##(), lb&, ub&, 0) +END SUB + + +'' +' Sorts _FLOAT array in descending order +' +' @param _FLOAT ARRAY source_arr##() array to sort +' @param _FLOAT ARRAY dest_arr##() array to store sorted in +' +SUB ARR_FLT.rsort(source_arr##(), dest_arr##()) + DIM AS LONG lb, ub, count + lb& = LBOUND(source_arr##) : ub& = UBOUND(source_arr##) + count& = ub& - lb& + REDIM dest_arr(0 TO count&) AS _FLOAT + CALL ARR_FLT.copy(source_arr##(), dest_arr##()) + CALL ARR_FLT.quicksort(dest_arr##(), lb&, ub&, 1) +END SUB + + +'' +' Quicksort array with pivot algorithm by logiclrd +' +' @link https://www.tek-tips.com/faqs.cfm?fid=336 +' @param _FLOAT ARRAY array##() to sort +' @param INTEGER start% of range to sort +' @param INTEGER finish% range of sort +' @param INTEGER order% to sort by (0 = asc / 1 = desc) +' +SUB ARR_FLT.quicksort(arr##(), start%, finish%, order%) + DIM i AS LONG + DIM pivot AS INTEGER + DIM pivotvalue AS _FLOAT + + 'first, partition the array + pivot% = start% + pivotvalue## = arr##(pivot%) + FOR i& = start% + 1 TO finish% + IF order% = 0 THEN ' ascending order + IF arr##(i&) < pivotvalue## THEN + arr##(pivot%) = arr##(i&) + arr##(i&) = arr##(pivot% + 1) + arr##(pivot% + 1) = pivotvalue## + pivot% = pivot% + 1 + END IF + ELSEIF order% = 1 THEN ' descending order + IF arr##(i&) > pivotvalue## THEN + arr##(pivot%) = arr##(i&) + arr##(i&) = arr##(pivot% + 1) + arr##(pivot% + 1) = pivotvalue## + pivot% = pivot% + 1 + END IF + END IF + NEXT i& + + 'then, sort the subarrays to each side of the pivot + IF pivot% - start% >= 2 THEN + CALL ARR_FLT.quicksort(arr##(), start%, pivot% - 1, order%) + END IF + IF finish% - pivot% >= 2 THEN + CALL ARR_FLT.quicksort(arr##(), pivot% + 1, finish%, order%) + END IF +END SUB diff --git a/ARR/ARR_INT.BAS b/ARR/ARR_INT.BAS new file mode 100644 index 0000000..30d4c38 --- /dev/null +++ b/ARR/ARR_INT.BAS @@ -0,0 +1,820 @@ +'$DYNAMIC +$LET GJ_LIB_ARR_INT_BAS = 1 + +'' +' Slice an array from source to destination starting at index and count slices +' +' @param INTEGER() source_arr% to slice from +' @param INTEGER() dest_arr% to put slices into +' @param INTEGER start_idx% starting index to use as slice range +' @param INTEGER count% number of slices - if negative, backwards from index +' +SUB ARR_INT.slice(source_arr%(), dest_arr%(), start_idx%, count%) + DIM AS LONG ub, lb, i, n + lb& = LBOUND(source_arr%) : ub& = UBOUND(source_arr%) + IF start_idx% < lb& OR start_idx% + count% > ub& THEN EXIT SUB ' out of range + IF ub& - lb& < count% THEN EXIT SUB ' too many and not enough + REDIM dest_arr(0 TO ABS(count%)) AS INTEGER + IF SGN(count%) = -1 THEN + IF ((start_idx% - 1) - ABS(count%)) < 0 THEN EXIT SUB ' out of range + n& = 0 + FOR i& = (start_idx% - 1) TO ((start_idx% - 1) - ABS(count%)) STEP -1 + dest_arr%(n&) = source_arr%(i&) + n& = n& + 1 + NEXT i& + ELSE + IF ((start_idx% + 1) + ABS(count%)) > (ub& - lb&) THEN EXIT SUB ' out of range + n& = 0 + FOR i& = start_idx% + 1 TO ((start_idx% + 1) + count%) STEP 1 + dest_arr%(n&) = source_arr%(i&) + n& = n& + 1 + NEXT i& + END IF +END SUB + + +'' +' Push a int onto the end of a INTEGER array +' +' @param INTEGER arr%() array to push into +' @param INTEGER value% of byte to push +' +SUB ARR_INT.push(arr%(), value%) + DIM AS LONG ub, lb + lb& = LBOUND(arr%) : ub& = UBOUND(arr%) + REDIM _PRESERVE arr(lb& TO (ub& + 1)) AS INTEGER + arr%(ub& + 1) = value% +END SUB + + +'' +' Pop a int from the end of a INTEGER array +' +' @param INTEGER arr%() array to pop from +' @param INTEGER var% of int to store popped int +' +SUB ARR_INT.pop(arr%(), var%) + DIM AS LONG ub, lb + lb& = LBOUND(arr%) : ub& = UBOUND(arr%) + var% = arr%(ub&) + REDIM _PRESERVE arr(lb& TO (ub& - 1)) AS INTEGER +END SUB + + +'' +' Pop a int from the beginning of a INTEGER array +' +' @param INTEGER arr%() array to pop from +' @param INTEGER var% of int to store popped int +' +SUB ARR_INT.shift(arr%(), var%) + DIM AS LONG ub, lb, i + lb& = LBOUND(arr%) : ub& = UBOUND(arr%) + var% = arr%(lb&) + FOR i& = lb& TO ub& - 1 + arr%(i&) = arr%(i& + 1) + NEXT i& + REDIM _PRESERVE arr(lb& + 1 TO ub&) AS INTEGER +END SUB + + +'' +' Copy an array of INTs to another INTEGER array +' +' @param INTEGER ARRAY source_arr%() source array to copy +' @param INTEGER ARRAY dest_arr%() dest array to copy into +' +SUB ARR_INT.copy(source_arr%(), dest_arr%()) + DIM AS LONG ub, lb, i + lb& = LBOUND(source_arr%) : ub& = UBOUND(source_arr%) + REDIM dest_arr(lb& TO ub&) AS INTEGER + FOR i& = lb& TO ub& + dest_arr%(i&) = source_arr%(i&) + NEXT i& +END SUB + + +'' +' Push a int into the beginning of a INTEGER array +' +' @param INTEGER arr%() array to push into +' @param INTEGER value% of int to push +' +SUB ARR_INT.unshift(arr%(), value%) + DIM AS LONG ub, lb, i + lb& = LBOUND(arr%) : ub& = UBOUND(arr%) + DIM work(lb& TO ub&) AS INTEGER + CALL ARR_INT.copy(arr%(), work%()) + REDIM _PRESERVE arr(lb& TO (ub& + 1)) AS INTEGER + FOR i& = lb& + 1 TO ub& + 1 + arr%(i&) = work%(i& - 1) + NEXT i& + arr%(lb&) = value% +END SUB + + +'' +' Joins an array of INTs as a string +' +' @param INTEGER ARRAY arr%() to get as a string +' @param STRING s$ to store stringified array in +' +SUB ARR_INT.join(arr%(), s$) + DIM AS LONG ub, lb, i + s$ = "" + lb& = LBOUND(arr%) : ub& = UBOUND(arr%) + FOR i& = lb& TO ub& + s$ = s$ + _TRIM$(STR$(arr%(i&))) + ", " + NEXT i& + ' remove trailing comma + s$ = MID$(s$, 1, (LEN(s$)-LEN(", "))) +END SUB + + +'' +' Create a new INTEGER array using string of ints seperated by commas +' +' @param INTEGER ARRAY arr%() to store the ints in +' @param STRING s$ string of comma separated ints +' +SUB ARR_INT.new(arr%(), s$) + DIM AS LONG i, count + DIM t AS STRING + count& = 0 + FOR i& = 1 TO LEN(s$) + IF ASC(s$, i&) = ASC(",") THEN count& = count& + 1 + NEXT i& + REDIM arr(0 TO count&) AS INTEGER + IF count& = 0 THEN + arr%(0) = VAL(s$) + EXIT SUB + END IF + count& = 0 + FOR i& = 1 TO LEN(s$) + t$ = t$ + CHR$(ASC(s$, i&)) + IF ASC(s$, i&) = ASC(",") OR i& = LEN(s$) THEN + arr%(count&) = VAL(t$) + count& = count& + 1 + t$ = "" + END IF + NEXT i& +END SUB + + +'' +' Return the visually longest element of a INTEGER array +' +' @param INTEGER ARRAY arr%() to check in +' @return INTEGER value of visually longest element +' +FUNCTION ARR_INT.longest%(arr%()) + DIM AS LONG lb, ub, i, res, lw + lb& = LBOUND(arr%) : ub& = UBOUND(arr%) + res& = 0 : lw& = 0 + FOR i& = lb& TO ub& + IF LEN(_TRIM$(STR$(arr%(i&)))) > lw& THEN + lw& = LEN(_TRIM$(STR$(arr%(i&)))) + res& = i& + END IF + NEXT i& + ARR_INT.longest% = arr%(res&) +END FUNCTION + + +'' +' Perform some math on every element of a INTEGER array +' +' @param INTEGER ARRAY source_arr%() to do math on +' @param INTEGER ARRAY dest_arr%() to store results in +' @param STRING op$ one of: +' and or xor shl shr +' "+", "-", "*", "\", "&&", "||", "!!", "<<", ">>" +' @param INTEGER value% to use for operand +' +SUB ARR_INT.math(source_arr%(), dest_arr%(), op$, value%) + DIM AS LONG lb, ub, i + lb& = LBOUND(source_arr%) : ub& = UBOUND(source_arr%) + REDIM dest_arr(lb& TO ub&) AS INTEGER + FOR i& = lb& TO ub& + SELECT CASE op$ + CASE "+": + dest_arr%(i&) = source_arr%(i&) + value% + CASE "-": + dest_arr%(i&) = source_arr%(i&) - value% + CASE "*": + dest_arr%(i&) = source_arr%(i&) * value% + CASE "\": + IF value% > 0 THEN + dest_arr%(i&) = source_arr%(i&) \ value% + END IF + CASE "&&": + dest_arr%(i&) = source_arr%(i&) AND value% + CASE "||": + dest_arr%(i&) = source_arr%(i&) OR value% + CASE "!!": + dest_arr%(i&) = source_arr%(i&) XOR value% + CASE "<<": + dest_arr%(i&) = _SHL(source_arr%(i&), value%) + CASE ">>": + dest_arr%(i&) = _SHR(source_arr%(i&), value%) + END SELECT + NEXT i& +END SUB + + +'' +' Return the minimum element value in INTEGER array +' +' @param INTEGER ARRAY arr%() to check in +' @return INTEGER minimum value found +' +FUNCTION ARR_INT.min%(arr%()) + DIM AS LONG lb, ub, i + DIM AS INTEGER s + lb& = LBOUND(arr%) : ub& = UBOUND(arr%) + s% = 127 + FOR i& = lb& TO ub& + IF arr%(i&) < s% THEN + s% = arr%(i&) + END IF + NEXT i& + ARR_INT.min% = s% +END FUNCTION + + +'' +' Return the maximum element value in INTEGER array +' +' @param INTEGER ARRAY arr%() to check in +' @return INTEGER maximum value found +' +FUNCTION ARR_INT.max%(arr%()) + DIM AS LONG lb, ub, i + DIM AS INTEGER s + lb& = LBOUND(arr%) : ub& = UBOUND(arr%) + s% = 0 + FOR i& = lb& TO ub& + IF arr%(i&) > s% THEN + s% = arr%(i&) + END IF + NEXT i& + ARR_INT.max% = s% +END FUNCTION + + +'' +' Return the visually shortest element of a INTEGER array +' +' @param INTEGER ARRAY arr%() to check in +' @return INTEGER value of visually shortest element +' +FUNCTION ARR_INT.shortest%(arr%()) + DIM AS LONG lb, ub, i, res, sw + lb& = LBOUND(arr%) : ub& = UBOUND(arr%) + res& = 0 : sw& = 0 + FOR i& = lb& TO ub& + IF LEN(_TRIM$(STR$(arr%(i&)))) < sw& THEN + sw& = LEN(_TRIM$(STR$(arr%(i&)))) + res& = i& + END IF + NEXT i& + ARR_INT.shortest% = arr%(res&) +END FUNCTION + + +'' +' Return the first element of a INTEGER array +' +' @param INTEGER ARRAY arr%() to check in +' @return INTEGER value of first element +' +FUNCTION ARR_INT.first%(arr%()) + ARR_INT.first% = arr%(LBOUND(arr%)) +END FUNCTION + + +'' +' Return the last element of a INTEGER array +' +' @param INTEGER ARRAY arr%() to check in +' @return INTEGER value of last element +' +FUNCTION ARR_INT.last%(arr%()) + ARR_INT.last% = arr%(UBOUND(arr%)) +END FUNCTION + + +'' +' Return every nth array element of a INTEGER array +' +' @param INTEGER ARRAY source_arr%() to get from +' @param INTEGER ARRAY dest_arr%() to store in +' @param INTEGER nth% element +' +SUB ARR_INT.nth(source_arr%(), dest_arr%(), nth%) + DIM AS LONG lb, ub, i, n + lb& = LBOUND(source_arr%) : ub& = UBOUND(source_arr%) + n& = (ub& - lb&) \ nth% + REDIM dest_arr(n&) AS INTEGER + n& = 0 + FOR i& = lb& to ub& + IF i& MOD nth% = 0 THEN + dest_arr%(n&) = source_arr%(i&) + n& = n& + 1 + END IF + NEXT i& +END SUB + + +'' +' Checks if value exists in INTEGER array +' +' @param INTEGER ARRAY arr%() to check in +' @param INTEGER value% value to check for +' @return INTEGER -1 if found or 0 if not found +' +FUNCTION ARR_INT.in%(arr%(), value%) + DIM AS LONG lb, ub, i + lb& = LBOUND(arr%) : ub& = UBOUND(arr%) + FOR i& = lb& TO ub& + IF arr%(i&) = value% THEN + ARR_INT.in% = -1 + EXIT FUNCTION + END IF + NEXT i& + ARR_INT.in% = 0 +END FUNCTION + + +'' +' Checks if value exists in INTEGER array and returns index if found +' +' @param INTEGER ARRAY arr%() to check in +' @param INTEGER value% value to check for +' @return INTEGER index of element if found or -1 if not found +' +FUNCTION ARR_INT.find%(arr%(), value%) + DIM AS LONG lb, ub, i + lb& = LBOUND(arr%) : ub& = UBOUND(arr%) + FOR i& = lb& TO ub& + IF arr%(i&) = value% THEN + ARR_INT.find% = i& + EXIT FUNCTION + END IF + NEXT i& + ARR_INT.find% = -1 +END FUNCTION + + +'' +' Return the number of elements in a INTEGER array +' +' @param INTEGER ARRAY arr%() to count +' @return INTEGER number of elements +' +FUNCTION ARR_INT.count&(arr%()) + ARR_INT.count& = UBOUND(arr%) - LBOUND(arr%) +END FUNCTION + + +'' +' Return the size of a INTEGER array +' +' @param INTEGER ARRAY arr%() to get size of +' @return LONG size in bytes +' +FUNCTION ARR_INT.size&(arr%()) + ARR_INT.size& = LEN(arr%()) +END FUNCTION + + +'' +' Reverses the elements of a INTEGER array +' +' @param INTEGER ARRAY source_arr%() to reverse +' @param INTEGER ARRAY dest_arr%() to store reversed array in +' +SUB ARR_INT.reverse(source_arr%(), dest_arr%()) + DIM AS LONG lb, ub, i, n + lb& = LBOUND(source_arr%) : ub& = UBOUND(source_arr%) + REDIM dest_arr(0 TO (ub& - lb&)) AS INTEGER + n& = 0 + FOR i& = ub& TO lb& STEP -1 + dest_arr%(n&) = source_arr%(i&) + n& = n& + 1 + NEXT i& +END SUB + + +'' +' Returns a random int from a INTEGER array +' +' @param INTEGER ARRAY arr%() array to get random element from +' @return INTEGER random element +' +FUNCTION ARR_INT.random%(arr%()) + DIM AS LONG lb, ub + lb& = LBOUND(arr%) : ub& = UBOUND(arr%) + RANDOMIZE TIMER + ARR_INT.random% = arr%(INT(RND * (ub& - lb&)) + 1) +END FUNCTION + + +'' +' Returns the sum of all elements in a INTEGER array +' +' @param INTEGER ARRAY arr%() array to get some for +' @return LONG sum of all elements +' +FUNCTION ARR_INT.sum&(arr%()) + DIM AS LONG lb, ub, i + DIM sum AS LONG + lb& = LBOUND(arr%) : ub& = UBOUND(arr%) + FOR i& = lb& TO ub& + sum& = sum& + arr%(i&) + NEXT i& + ARR_INT.sum& = sum& +END FUNCTION + + +'' +' Returns the average value of elements in a INTEGER array +' +' @param INTEGER ARRAY arr%() array to get average for +' @return LONG average of elements +' +FUNCTION ARR_INT.avg&(arr%()) + DIM AS LONG lb, ub, i + DIM sum AS LONG + lb& = LBOUND(arr%) : ub& = UBOUND(arr%) + FOR i& = lb& TO ub& + sum& = sum& + arr%(i&) + NEXT i& + ARR_INT.avg& = sum& / (ub& - lb&) +END FUNCTION + + +'' +' Shuffle the elements of a INTEGER array +' +' @param INTEGER ARRAY source_arr%() to shuffle +' @param INTEGER ARRAY dest_arr%() to store shuffled array in +' +SUB ARR_INT.shuffle(source_arr%(), dest_arr%()) + DIM AS LONG lb, ub, i, count + lb& = LBOUND(source_arr%) : ub& = UBOUND(source_arr%) + count& = ub& - lb& + REDIM dest_arr(0 TO count&) AS INTEGER + CALL ARR_INT.copy(source_arr%(), dest_arr%()) + RANDOMIZE TIMER + FOR i& = 0 TO count& + SWAP dest_arr%(i&), dest_arr%(lb& + RND * (ub& - lb&)) + NEXT i& +END SUB + + +'' +' Makes a INTEGER array contain only unique values +' +' @param INTEGER ARRAY source_arr%() array to get uniques for +' @param INTEGER ARRAY dest_arr%() array to store uniques in +' +SUB ARR_INT.unique(source_arr%(), dest_arr%()) + DIM AS LONG lb, ub, i + DIM tmp AS INTEGER + lb& = LBOUND(source_arr%) : ub& = UBOUND(source_arr%) + DIM work_arr(0) AS INTEGER + FOR i& = lb& TO ub& + IF NOT ARR_INT.in%(work_arr%(), source_arr%(i&)) THEN + CALL ARR_INT.push(work_arr%(), source_arr%(i&)) + END IF + NEXT i& + CALL ARR_INT.shift(work_arr%(), tmp%) + CALL ARR_INT.copy(work_arr%(), dest_arr%()) +END SUB + + +'' +' Filters a INTEGER array to only elements greater than value +' +' @param INTEGER ARRAY source_arr%() array to work on +' @param INTEGER ARRAY dest_arr%() array to store in +' @param INTEGER value% to be greater than to be returned +' +SUB ARR_INT.gt(source_arr%(), dest_arr%(), value%) + DIM AS LONG lb, ub, i + DIM tmp AS INTEGER + lb& = LBOUND(source_arr%) : ub& = UBOUND(source_arr%) + DIM work_arr(0) AS INTEGER + FOR i& = lb& TO ub& + IF source_arr%(i&) > value% THEN + CALL ARR_INT.push(work_arr%(), source_arr%(i&)) + END IF + NEXT i& + CALL ARR_INT.shift(work_arr%(), tmp%) + CALL ARR_INT.copy(work_arr%(), dest_arr%()) +END SUB + + +'' +' Filters a INTEGER array to only elements greater than or equal to value +' +' @param INTEGER ARRAY source_arr%() array to work on +' @param INTEGER ARRAY dest_arr%() array to store in +' @param INTEGER value% to be greater than or equal to be returned +' +SUB ARR_INT.gte(source_arr%(), dest_arr%(), value%) + DIM AS LONG lb, ub, i + DIM tmp AS INTEGER + lb& = LBOUND(source_arr%) : ub& = UBOUND(source_arr%) + DIM work_arr(0) AS INTEGER + FOR i& = lb& TO ub& + IF source_arr%(i&) >= value% THEN + CALL ARR_INT.push(work_arr%(), source_arr%(i&)) + END IF + NEXT i& + CALL ARR_INT.shift(work_arr%(), tmp%) + CALL ARR_INT.copy(work_arr%(), dest_arr%()) +END SUB + + +'' +' Filters a INTEGER array to only elements less than value +' +' @param INTEGER ARRAY source_arr%() array to work on +' @param INTEGER ARRAY dest_arr%() array to store in +' @param INTEGER value% to be less than to be returned +' +SUB ARR_INT.lt(source_arr%(), dest_arr%(), value%) + DIM AS LONG lb, ub, i + DIM tmp AS INTEGER + lb& = LBOUND(source_arr%) : ub& = UBOUND(source_arr%) + DIM work_arr(0) AS INTEGER + FOR i& = lb& TO ub& + IF source_arr%(i&) < value% THEN + CALL ARR_INT.push(work_arr%(), source_arr%(i&)) + END IF + NEXT i& + CALL ARR_INT.shift(work_arr%(), tmp%) + CALL ARR_INT.copy(work_arr%(), dest_arr%()) +END SUB + + +'' +' Filters a INTEGER array to only elements less than or equal to value +' +' @param INTEGER ARRAY source_arr%() array to work on +' @param INTEGER ARRAY dest_arr%() array to store in +' @param INTEGER value% to be less than or equal to be returned +' +SUB ARR_INT.lte(source_arr%(), dest_arr%(), value%) + DIM AS LONG lb, ub, i + DIM tmp AS INTEGER + lb& = LBOUND(source_arr%) : ub& = UBOUND(source_arr%) + DIM work_arr(0) AS INTEGER + FOR i& = lb& TO ub& + IF source_arr%(i&) <= value% THEN + CALL ARR_INT.push(work_arr%(), source_arr%(i&)) + END IF + NEXT i& + CALL ARR_INT.shift(work_arr%(), tmp%) + CALL ARR_INT.copy(work_arr%(), dest_arr%()) +END SUB + + +'' +' Finds and replaces values across all elements in a INTEGER ARRAY +' +' @param INTEGER ARRAY arr%() to check in +' @param INTEGER find% value to find +' @param INTEGER replace% value to replace with if found +' +SUB ARR_INT.replace(arr%(), find%, replace%) + DIM AS LONG lb, ub, i + lb& = LBOUND(arr%) : ub& = UBOUND(arr%) + FOR i& = lb& TO ub& + IF arr%(i&) = find% THEN + arr%(i&) = replace% + END IF + NEXT i& +END SUB + + +'' +' Inserts a new element into INTEGER array after index +' +' @param INTEGER ARRAY arr%() array to work on +' @param INTEGER value% to insert +' @param INTEGER index% of element to insert at +' +SUB ARR_INT.insert(arr%(), value%, index%) + DIM AS LONG lb, ub, i + DIM tmp AS INTEGER + DIM work_arr(0) AS INTEGER + lb& = LBOUND(arr%) : ub& = UBOUND(arr%) + IF index% >= lb& AND index% <= ub& THEN + ' build new array up to index + FOR i& = lb& TO index% + CALL ARR_INT.push(work_arr%(), arr%(i&)) + NEXT i& + ' insert new element + CALL ARR_INT.push(work_arr%(), value%) + ' finish building array from index + 1 + FOR i& = index% + 1 TO ub& + CALL ARR_INT.push(work_arr%(), arr%(i&)) + NEXT i& + CALL ARR_INT.shift(work_arr%(), tmp%) + CALL ARR_INT.copy(work_arr%(), arr%()) + END IF +END SUB + + +'' +' Removes element from a INTEGER array by element index +' +' @param INTEGER ARRAY arr%() array to work on +' @param INTEGER index% of element to remove +' +SUB ARR_INT.remove(arr%(), index%) + DIM AS LONG lb, ub, i + DIM tmp AS INTEGER + DIM work_arr(0) AS INTEGER + lb& = LBOUND(arr%) : ub& = UBOUND(arr%) + IF index% >= lb& AND index% <= ub& THEN + ' build new array up to index + FOR i& = lb& TO index% - 1 + CALL ARR_INT.push(work_arr%(), arr%(i&)) + NEXT i& + ' skip elements + FOR i& = index% + 1 TO ub& + CALL ARR_INT.push(work_arr%(), arr%(i&)) + NEXT i& + CALL ARR_INT.shift(work_arr%(), tmp%) + CALL ARR_INT.copy(work_arr%(), arr%()) + END IF +END SUB + + +'' +' Filters a INTEGER array to only elements that have odd values +' +' @param INTEGER ARRAY source_arr%() array to work on +' @param INTEGER ARRAY dest_arr%() array to store in +' +SUB ARR_INT.odd(source_arr%(), dest_arr%()) + DIM AS LONG lb, ub, i + DIM tmp AS INTEGER + lb& = LBOUND(source_arr%) : ub& = UBOUND(source_arr%) + DIM work_arr(0) AS INTEGER + FOR i& = lb& TO ub& + IF source_arr%(i&) MOD 2 <> 0 THEN + CALL ARR_INT.push(work_arr%(), source_arr%(i&)) + END IF + NEXT i& + CALL ARR_INT.shift(work_arr%(), tmp%) + CALL ARR_INT.copy(work_arr%(), dest_arr%()) +END SUB + + +'' +' Filters a INTEGER array to only elements that have even values +' +' @param INTEGER ARRAY source_arr%() array to work on +' @param INTEGER ARRAY dest_arr%() array to store in +' +SUB ARR_INT.even(source_arr%(), dest_arr%()) + DIM AS LONG lb, ub, i + DIM tmp AS INTEGER + lb& = LBOUND(source_arr%) : ub& = UBOUND(source_arr%) + DIM work_arr(0) AS INTEGER + FOR i& = lb& TO ub& + IF source_arr%(i&) MOD 2 = 0 THEN + CALL ARR_INT.push(work_arr%(), source_arr%(i&)) + END IF + NEXT i& + CALL ARR_INT.shift(work_arr%(), tmp%) + CALL ARR_INT.copy(work_arr%(), dest_arr%()) +END SUB + + +'' +' Filters a INTEGER array to only elements that have values evenly divisible by divisor +' +' @param INTEGER ARRAY source_arr%() array to work on +' @param INTEGER ARRAY dest_arr%() array to store in +' @param INTEGER divisor% for modulo +' +SUB ARR_INT.mod(source_arr%(), dest_arr%(), divisor%) + DIM AS LONG lb, ub, i + DIM tmp AS INTEGER + lb& = LBOUND(source_arr%) : ub& = UBOUND(source_arr%) + DIM work_arr(0) AS INTEGER + FOR i& = lb& TO ub& + IF source_arr%(i&) MOD divisor% = 0 THEN + CALL ARR_INT.push(work_arr%(), source_arr%(i&)) + END IF + NEXT i& + CALL ARR_INT.shift(work_arr%(), tmp%) + CALL ARR_INT.copy(work_arr%(), dest_arr%()) +END SUB + + +'' +' Filters a INTEGER array to only elements between min and max +' +' @param INTEGER ARRAY source_arr%() array to work on +' @param INTEGER ARRAY dest_arr%() array to store in +' @param INTEGER min% to be greater than or equal to be returned +' @param INTEGER max% to be less than or equal to be returned +' +SUB ARR_INT.between(source_arr%(), dest_arr%(), min%, max%) + DIM AS LONG lb, ub, i + DIM tmp AS INTEGER + lb& = LBOUND(source_arr%) : ub& = UBOUND(source_arr%) + DIM work_arr(0) AS INTEGER + FOR i& = lb& TO ub& + IF source_arr%(i&) >= min% _ + AND source_arr%(i&) <= max% THEN + CALL ARR_INT.push(work_arr%(), source_arr%(i&)) + END IF + NEXT i& + CALL ARR_INT.shift(work_arr%(), tmp%) + CALL ARR_INT.copy(work_arr%(), dest_arr%()) +END SUB + + +'' +' Sorts INTEGER array in ascending order +' +' @param INTEGER ARRAY source_arr%() array to sort +' @param INTEGER ARRAY dest_arr%() array to store sorted in +' +SUB ARR_INT.sort(source_arr%(), dest_arr%()) + DIM AS LONG lb, ub, count + lb& = LBOUND(source_arr%) : ub& = UBOUND(source_arr%) + count& = ub& - lb& + REDIM dest_arr(0 TO count&) AS INTEGER + CALL ARR_INT.copy(source_arr%(), dest_arr%()) + CALL ARR_INT.quicksort(dest_arr%(), lb&, ub&, 0) +END SUB + + +'' +' Sorts INTEGER array in descending order +' +' @param INTEGER ARRAY source_arr%() array to sort +' @param INTEGER ARRAY dest_arr%() array to store sorted in +' +SUB ARR_INT.rsort(source_arr%(), dest_arr%()) + DIM AS LONG lb, ub, count + lb& = LBOUND(source_arr%) : ub& = UBOUND(source_arr%) + count& = ub& - lb& + REDIM dest_arr(0 TO count&) AS INTEGER + CALL ARR_INT.copy(source_arr%(), dest_arr%()) + CALL ARR_INT.quicksort(dest_arr%(), lb&, ub&, 1) +END SUB + + +'' +' Quicksort array with pivot algorithm by logiclrd +' +' @link https://www.tek-tips.com/faqs.cfm?fid=336 +' @param INTEGER ARRAY array%() to sort +' @param INTEGER start% of range to sort +' @param INTEGER finish% range of sort +' @param INTEGER order% to sort by (0 = asc / 1 = desc) +' +SUB ARR_INT.quicksort(arr%(), start%, finish%, order%) + DIM i AS LONG + DIM pivot AS INTEGER + DIM pivotvalue AS INTEGER + + 'first, partition the array + pivot% = start% + pivotvalue% = arr%(pivot%) + FOR i& = start% + 1 TO finish% + IF order% = 0 THEN ' ascending order + IF arr%(i&) < pivotvalue% THEN + arr%(pivot%) = arr%(i&) + arr%(i&) = arr%(pivot% + 1) + arr%(pivot% + 1) = pivotvalue% + pivot% = pivot% + 1 + END IF + ELSEIF order% = 1 THEN ' descending order + IF arr%(i&) > pivotvalue% THEN + arr%(pivot%) = arr%(i&) + arr%(i&) = arr%(pivot% + 1) + arr%(pivot% + 1) = pivotvalue% + pivot% = pivot% + 1 + END IF + END IF + NEXT i& + + 'then, sort the subarrays to each side of the pivot + IF pivot% - start% >= 2 THEN + CALL ARR_INT.quicksort(arr%(), start%, pivot% - 1, order%) + END IF + IF finish% - pivot% >= 2 THEN + CALL ARR_INT.quicksort(arr%(), pivot% + 1, finish%, order%) + END IF +END SUB diff --git a/ARR/ARR_INT64.BAS b/ARR/ARR_INT64.BAS new file mode 100644 index 0000000..43be920 --- /dev/null +++ b/ARR/ARR_INT64.BAS @@ -0,0 +1,820 @@ +'$DYNAMIC +$LET GJ_LIB_ARR_INT64_BAS = 1 + +'' +' Slice an array from source to destination starting at index and count slices +' +' @param _INTEGER64() source_arr&& to slice from +' @param _INTEGER64() dest_arr&& to put slices into +' @param INTEGER start_idx% starting index to use as slice range +' @param INTEGER count% number of slices - if negative, backwards from index +' +SUB ARR_INT64.slice(source_arr&&(), dest_arr&&(), start_idx%, count%) + DIM AS LONG ub, lb, i, n + lb& = LBOUND(source_arr&&) : ub& = UBOUND(source_arr&&) + IF start_idx% < lb& OR start_idx% + count% > ub& THEN EXIT SUB ' out of range + IF ub& - lb& < count% THEN EXIT SUB ' too many and not enough + REDIM dest_arr(0 TO ABS(count%)) AS _INTEGER64 + IF SGN(count%) = -1 THEN + IF ((start_idx% - 1) - ABS(count%)) < 0 THEN EXIT SUB ' out of range + n& = 0 + FOR i& = (start_idx% - 1) TO ((start_idx% - 1) - ABS(count%)) STEP -1 + dest_arr&&(n&) = source_arr&&(i&) + n& = n& + 1 + NEXT i& + ELSE + IF ((start_idx% + 1) + ABS(count%)) > (ub& - lb&) THEN EXIT SUB ' out of range + n& = 0 + FOR i& = start_idx% + 1 TO ((start_idx% + 1) + count%) STEP 1 + dest_arr&&(n&) = source_arr&&(i&) + n& = n& + 1 + NEXT i& + END IF +END SUB + + +'' +' Push a int64 onto the end of a _INTEGER64 array +' +' @param _INTEGER64 arr&&() array to push into +' @param _INTEGER64 value&& of byte to push +' +SUB ARR_INT64.push(arr&&(), value&&) + DIM AS LONG ub, lb + lb& = LBOUND(arr&&) : ub& = UBOUND(arr&&) + REDIM _PRESERVE arr(lb& TO (ub& + 1)) AS _INTEGER64 + arr&&(ub& + 1) = value&& +END SUB + + +'' +' Pop a int64 from the end of a _INTEGER64 array +' +' @param _INTEGER64 arr&&() array to pop from +' @param _INTEGER64 var&& of int64 to store popped int64 +' +SUB ARR_INT64.pop(arr&&(), var&&) + DIM AS LONG ub, lb + lb& = LBOUND(arr&&) : ub& = UBOUND(arr&&) + var&& = arr&&(ub&) + REDIM _PRESERVE arr(lb& TO (ub& - 1)) AS _INTEGER64 +END SUB + + +'' +' Pop a int64 from the beginning of a _INTEGER64 array +' +' @param _INTEGER64 arr&&() array to pop from +' @param _INTEGER64 var&& of int64 to store popped int64 +' +SUB ARR_INT64.shift(arr&&(), var&&) + DIM AS LONG ub, lb, i + lb& = LBOUND(arr&&) : ub& = UBOUND(arr&&) + var&& = arr&&(lb&) + FOR i& = lb& TO ub& - 1 + arr&&(i&) = arr&&(i& + 1) + NEXT i& + REDIM _PRESERVE arr(lb& + 1 TO ub&) AS _INTEGER64 +END SUB + + +'' +' Copy an array of INT64s to another _INTEGER64 array +' +' @param _INTEGER64 ARRAY source_arr&&() source array to copy +' @param _INTEGER64 ARRAY dest_arr&&() dest array to copy into +' +SUB ARR_INT64.copy(source_arr&&(), dest_arr&&()) + DIM AS LONG ub, lb, i + lb& = LBOUND(source_arr&&) : ub& = UBOUND(source_arr&&) + REDIM dest_arr(lb& TO ub&) AS _INTEGER64 + FOR i& = lb& TO ub& + dest_arr&&(i&) = source_arr&&(i&) + NEXT i& +END SUB + + +'' +' Push a int64 into the beginning of a _INTEGER64 array +' +' @param _INTEGER64 arr&&() array to push into +' @param _INTEGER64 value&& of int64 to push +' +SUB ARR_INT64.unshift(arr&&(), value&&) + DIM AS LONG ub, lb, i + lb& = LBOUND(arr&&) : ub& = UBOUND(arr&&) + DIM work(lb& TO ub&) AS _INTEGER64 + CALL ARR_INT64.copy(arr&&(), work&&()) + REDIM _PRESERVE arr(lb& TO (ub& + 1)) AS _INTEGER64 + FOR i& = lb& + 1 TO ub& + 1 + arr&&(i&) = work&&(i& - 1) + NEXT i& + arr&&(lb&) = value&& +END SUB + + +'' +' Joins an array of INT64s as a string +' +' @param _INTEGER64 ARRAY arr&&() to get as a string +' @param STRING s$ to store stringified array in +' +SUB ARR_INT64.join(arr&&(), s$) + DIM AS LONG ub, lb, i + s$ = "" + lb& = LBOUND(arr&&) : ub& = UBOUND(arr&&) + FOR i& = lb& TO ub& + s$ = s$ + _TRIM$(STR$(arr&&(i&))) + ", " + NEXT i& + ' remove trailing comma + s$ = MID$(s$, 1, (LEN(s$)-LEN(", "))) +END SUB + + +'' +' Create a new _INTEGER64 array using string of int64s seperated by commas +' +' @param _INTEGER64 ARRAY arr&&() to store the int64s in +' @param STRING s$ string of comma separated int64s +' +SUB ARR_INT64.new(arr&&(), s$) + DIM AS LONG i, count + DIM t AS STRING + count& = 0 + FOR i& = 1 TO LEN(s$) + IF ASC(s$, i&) = ASC(",") THEN count& = count& + 1 + NEXT i& + REDIM arr(0 TO count&) AS _INTEGER64 + IF count& = 0 THEN + arr&&(0) = VAL(s$) + EXIT SUB + END IF + count& = 0 + FOR i& = 1 TO LEN(s$) + t$ = t$ + CHR$(ASC(s$, i&)) + IF ASC(s$, i&) = ASC(",") OR i& = LEN(s$) THEN + arr&&(count&) = VAL(t$) + count& = count& + 1 + t$ = "" + END IF + NEXT i& +END SUB + + +'' +' Return the visually longest element of a _INTEGER64 array +' +' @param _INTEGER64 ARRAY arr&&() to check in +' @return _INTEGER64 value of visually longest element +' +FUNCTION ARR_INT64.longest&&(arr&&()) + DIM AS LONG lb, ub, i, res, lw + lb& = LBOUND(arr&&) : ub& = UBOUND(arr&&) + res& = 0 : lw& = 0 + FOR i& = lb& TO ub& + IF LEN(_TRIM$(STR$(arr&&(i&)))) > lw& THEN + lw& = LEN(_TRIM$(STR$(arr&&(i&)))) + res& = i& + END IF + NEXT i& + ARR_INT64.longest&& = arr&&(res&) +END FUNCTION + + +'' +' Perform some math on every element of a _INTEGER64 array +' +' @param _INTEGER64 ARRAY source_arr&&() to do math on +' @param _INTEGER64 ARRAY dest_arr&&() to store results in +' @param STRING op$ one of: +' and or xor shl shr +' "+", "-", "*", "\", "&&", "||", "!!", "<<", ">>" +' @param _INTEGER64 value&& to use for operand +' +SUB ARR_INT64.math(source_arr&&(), dest_arr&&(), op$, value&&) + DIM AS LONG lb, ub, i + lb& = LBOUND(source_arr&&) : ub& = UBOUND(source_arr&&) + REDIM dest_arr(lb& TO ub&) AS _INTEGER64 + FOR i& = lb& TO ub& + SELECT CASE op$ + CASE "+": + dest_arr&&(i&) = source_arr&&(i&) + value&& + CASE "-": + dest_arr&&(i&) = source_arr&&(i&) - value&& + CASE "*": + dest_arr&&(i&) = source_arr&&(i&) * value&& + CASE "\": + IF value&& > 0 THEN + dest_arr&&(i&) = source_arr&&(i&) \ value&& + END IF + CASE "&&": + dest_arr&&(i&) = source_arr&&(i&) AND value&& + CASE "||": + dest_arr&&(i&) = source_arr&&(i&) OR value&& + CASE "!!": + dest_arr&&(i&) = source_arr&&(i&) XOR value&& + CASE "<<": + dest_arr&&(i&) = _SHL(source_arr&&(i&), value&&) + CASE ">>": + dest_arr&&(i&) = _SHR(source_arr&&(i&), value&&) + END SELECT + NEXT i& +END SUB + + +'' +' Return the minimum element value in _INTEGER64 array +' +' @param _INTEGER64 ARRAY arr&&() to check in +' @return _INTEGER64 minimum value found +' +FUNCTION ARR_INT64.min&&(arr&&()) + DIM AS LONG lb, ub, i + DIM AS _INTEGER64 s + lb& = LBOUND(arr&&) : ub& = UBOUND(arr&&) + s&& = 127 + FOR i& = lb& TO ub& + IF arr&&(i&) < s&& THEN + s&& = arr&&(i&) + END IF + NEXT i& + ARR_INT64.min&& = s&& +END FUNCTION + + +'' +' Return the maximum element value in _INTEGER64 array +' +' @param _INTEGER64 ARRAY arr&&() to check in +' @return _INTEGER64 maximum value found +' +FUNCTION ARR_INT64.max&&(arr&&()) + DIM AS LONG lb, ub, i + DIM AS _INTEGER64 s + lb& = LBOUND(arr&&) : ub& = UBOUND(arr&&) + s&& = 0 + FOR i& = lb& TO ub& + IF arr&&(i&) > s&& THEN + s&& = arr&&(i&) + END IF + NEXT i& + ARR_INT64.max&& = s&& +END FUNCTION + + +'' +' Return the visually shortest element of a _INTEGER64 array +' +' @param _INTEGER64 ARRAY arr&&() to check in +' @return _INTEGER64 value of visually shortest element +' +FUNCTION ARR_INT64.shortest&&(arr&&()) + DIM AS LONG lb, ub, i, res, sw + lb& = LBOUND(arr&&) : ub& = UBOUND(arr&&) + res& = 0 : sw& = 0 + FOR i& = lb& TO ub& + IF LEN(_TRIM$(STR$(arr&&(i&)))) < sw& THEN + sw& = LEN(_TRIM$(STR$(arr&&(i&)))) + res& = i& + END IF + NEXT i& + ARR_INT64.shortest&& = arr&&(res&) +END FUNCTION + + +'' +' Return the first element of a _INTEGER64 array +' +' @param _INTEGER64 ARRAY arr&&() to check in +' @return _INTEGER64 value of first element +' +FUNCTION ARR_INT64.first&&(arr&&()) + ARR_INT64.first&& = arr&&(LBOUND(arr&&)) +END FUNCTION + + +'' +' Return the last element of a _INTEGER64 array +' +' @param _INTEGER64 ARRAY arr&&() to check in +' @return _INTEGER64 value of last element +' +FUNCTION ARR_INT64.last&&(arr&&()) + ARR_INT64.last&& = arr&&(UBOUND(arr&&)) +END FUNCTION + + +'' +' Return every nth array element of a _INTEGER64 array +' +' @param _INTEGER64 ARRAY source_arr&&() to get from +' @param _INTEGER64 ARRAY dest_arr&&() to store in +' @param INTEGER nth% element +' +SUB ARR_INT64.nth(source_arr&&(), dest_arr&&(), nth%) + DIM AS LONG lb, ub, i, n + lb& = LBOUND(source_arr&&) : ub& = UBOUND(source_arr&&) + n& = (ub& - lb&) \ nth% + REDIM dest_arr(n&) AS _INTEGER64 + n& = 0 + FOR i& = lb& to ub& + IF i& MOD nth% = 0 THEN + dest_arr&&(n&) = source_arr&&(i&) + n& = n& + 1 + END IF + NEXT i& +END SUB + + +'' +' Checks if value exists in _INTEGER64 array +' +' @param _INTEGER64 ARRAY arr&&() to check in +' @param _INTEGER64 value&& value to check for +' @return INTEGER -1 if found or 0 if not found +' +FUNCTION ARR_INT64.in%(arr&&(), value&&) + DIM AS LONG lb, ub, i + lb& = LBOUND(arr&&) : ub& = UBOUND(arr&&) + FOR i& = lb& TO ub& + IF arr&&(i&) = value&& THEN + ARR_INT64.in% = -1 + EXIT FUNCTION + END IF + NEXT i& + ARR_INT64.in% = 0 +END FUNCTION + + +'' +' Checks if value exists in _INTEGER64 array and returns index if found +' +' @param _INTEGER64 ARRAY arr&&() to check in +' @param _INTEGER64 value&& value to check for +' @return INTEGER index of element if found or -1 if not found +' +FUNCTION ARR_INT64.find%(arr&&(), value&&) + DIM AS LONG lb, ub, i + lb& = LBOUND(arr&&) : ub& = UBOUND(arr&&) + FOR i& = lb& TO ub& + IF arr&&(i&) = value&& THEN + ARR_INT64.find% = i& + EXIT FUNCTION + END IF + NEXT i& + ARR_INT64.find% = -1 +END FUNCTION + + +'' +' Return the number of elements in a _INTEGER64 array +' +' @param _INTEGER64 ARRAY arr&&() to count +' @return INTEGER number of elements +' +FUNCTION ARR_INT64.count&(arr&&()) + ARR_INT64.count& = UBOUND(arr&&) - LBOUND(arr&&) +END FUNCTION + + +'' +' Return the size of a _INTEGER64 array +' +' @param _INTEGER64 ARRAY arr&&() to get size of +' @return LONG size in bytes +' +FUNCTION ARR_INT64.size&(arr&&()) + ARR_INT64.size& = LEN(arr&&()) +END FUNCTION + + +'' +' Reverses the elements of a _INTEGER64 array +' +' @param _INTEGER64 ARRAY source_arr&&() to reverse +' @param _INTEGER64 ARRAY dest_arr&&() to store reversed array in +' +SUB ARR_INT64.reverse(source_arr&&(), dest_arr&&()) + DIM AS LONG lb, ub, i, n + lb& = LBOUND(source_arr&&) : ub& = UBOUND(source_arr&&) + REDIM dest_arr(0 TO (ub& - lb&)) AS _INTEGER64 + n& = 0 + FOR i& = ub& TO lb& STEP -1 + dest_arr&&(n&) = source_arr&&(i&) + n& = n& + 1 + NEXT i& +END SUB + + +'' +' Returns a random int64 from a _INTEGER64 array +' +' @param _INTEGER64 ARRAY arr&&() array to get random element from +' @return _INTEGER64 random element +' +FUNCTION ARR_INT64.random&&(arr&&()) + DIM AS LONG lb, ub + lb& = LBOUND(arr&&) : ub& = UBOUND(arr&&) + RANDOMIZE TIMER + ARR_INT64.random&& = arr&&(INT(RND * (ub& - lb&)) + 1) +END FUNCTION + + +'' +' Returns the sum of all elements in a _INTEGER64 array +' +' @param _INTEGER64 ARRAY arr&&() array to get some for +' @return LONG sum of all elements +' +FUNCTION ARR_INT64.sum&(arr&&()) + DIM AS LONG lb, ub, i + DIM sum AS LONG + lb& = LBOUND(arr&&) : ub& = UBOUND(arr&&) + FOR i& = lb& TO ub& + sum& = sum& + arr&&(i&) + NEXT i& + ARR_INT64.sum& = sum& +END FUNCTION + + +'' +' Returns the average value of elements in a _INTEGER64 array +' +' @param _INTEGER64 ARRAY arr&&() array to get average for +' @return LONG average of elements +' +FUNCTION ARR_INT64.avg&(arr&&()) + DIM AS LONG lb, ub, i + DIM sum AS LONG + lb& = LBOUND(arr&&) : ub& = UBOUND(arr&&) + FOR i& = lb& TO ub& + sum& = sum& + arr&&(i&) + NEXT i& + ARR_INT64.avg& = sum& / (ub& - lb&) +END FUNCTION + + +'' +' Shuffle the elements of a _INTEGER64 array +' +' @param _INTEGER64 ARRAY source_arr&&() to shuffle +' @param _INTEGER64 ARRAY dest_arr&&() to store shuffled array in +' +SUB ARR_INT64.shuffle(source_arr&&(), dest_arr&&()) + DIM AS LONG lb, ub, i, count + lb& = LBOUND(source_arr&&) : ub& = UBOUND(source_arr&&) + count& = ub& - lb& + REDIM dest_arr(0 TO count&) AS _INTEGER64 + CALL ARR_INT64.copy(source_arr&&(), dest_arr&&()) + RANDOMIZE TIMER + FOR i& = 0 TO count& + SWAP dest_arr&&(i&), dest_arr&&(lb& + RND * (ub& - lb&)) + NEXT i& +END SUB + + +'' +' Makes a _INTEGER64 array contain only unique values +' +' @param _INTEGER64 ARRAY source_arr&&() array to get uniques for +' @param _INTEGER64 ARRAY dest_arr&&() array to store uniques in +' +SUB ARR_INT64.unique(source_arr&&(), dest_arr&&()) + DIM AS LONG lb, ub, i + DIM tmp AS _INTEGER64 + lb& = LBOUND(source_arr&&) : ub& = UBOUND(source_arr&&) + DIM work_arr(0) AS _INTEGER64 + FOR i& = lb& TO ub& + IF NOT ARR_INT64.in%(work_arr&&(), source_arr&&(i&)) THEN + CALL ARR_INT64.push(work_arr&&(), source_arr&&(i&)) + END IF + NEXT i& + CALL ARR_INT64.shift(work_arr&&(), tmp&&) + CALL ARR_INT64.copy(work_arr&&(), dest_arr&&()) +END SUB + + +'' +' Filters a _INTEGER64 array to only elements greater than value +' +' @param _INTEGER64 ARRAY source_arr&&() array to work on +' @param _INTEGER64 ARRAY dest_arr&&() array to store in +' @param _INTEGER64 value&& to be greater than to be returned +' +SUB ARR_INT64.gt(source_arr&&(), dest_arr&&(), value&&) + DIM AS LONG lb, ub, i + DIM tmp AS _INTEGER64 + lb& = LBOUND(source_arr&&) : ub& = UBOUND(source_arr&&) + DIM work_arr(0) AS _INTEGER64 + FOR i& = lb& TO ub& + IF source_arr&&(i&) > value&& THEN + CALL ARR_INT64.push(work_arr&&(), source_arr&&(i&)) + END IF + NEXT i& + CALL ARR_INT64.shift(work_arr&&(), tmp&&) + CALL ARR_INT64.copy(work_arr&&(), dest_arr&&()) +END SUB + + +'' +' Filters a _INTEGER64 array to only elements greater than or equal to value +' +' @param _INTEGER64 ARRAY source_arr&&() array to work on +' @param _INTEGER64 ARRAY dest_arr&&() array to store in +' @param _INTEGER64 value&& to be greater than or equal to be returned +' +SUB ARR_INT64.gte(source_arr&&(), dest_arr&&(), value&&) + DIM AS LONG lb, ub, i + DIM tmp AS _INTEGER64 + lb& = LBOUND(source_arr&&) : ub& = UBOUND(source_arr&&) + DIM work_arr(0) AS _INTEGER64 + FOR i& = lb& TO ub& + IF source_arr&&(i&) >= value&& THEN + CALL ARR_INT64.push(work_arr&&(), source_arr&&(i&)) + END IF + NEXT i& + CALL ARR_INT64.shift(work_arr&&(), tmp&&) + CALL ARR_INT64.copy(work_arr&&(), dest_arr&&()) +END SUB + + +'' +' Filters a _INTEGER64 array to only elements less than value +' +' @param _INTEGER64 ARRAY source_arr&&() array to work on +' @param _INTEGER64 ARRAY dest_arr&&() array to store in +' @param _INTEGER64 value&& to be less than to be returned +' +SUB ARR_INT64.lt(source_arr&&(), dest_arr&&(), value&&) + DIM AS LONG lb, ub, i + DIM tmp AS _INTEGER64 + lb& = LBOUND(source_arr&&) : ub& = UBOUND(source_arr&&) + DIM work_arr(0) AS _INTEGER64 + FOR i& = lb& TO ub& + IF source_arr&&(i&) < value&& THEN + CALL ARR_INT64.push(work_arr&&(), source_arr&&(i&)) + END IF + NEXT i& + CALL ARR_INT64.shift(work_arr&&(), tmp&&) + CALL ARR_INT64.copy(work_arr&&(), dest_arr&&()) +END SUB + + +'' +' Filters a _INTEGER64 array to only elements less than or equal to value +' +' @param _INTEGER64 ARRAY source_arr&&() array to work on +' @param _INTEGER64 ARRAY dest_arr&&() array to store in +' @param _INTEGER64 value&& to be less than or equal to be returned +' +SUB ARR_INT64.lte(source_arr&&(), dest_arr&&(), value&&) + DIM AS LONG lb, ub, i + DIM tmp AS _INTEGER64 + lb& = LBOUND(source_arr&&) : ub& = UBOUND(source_arr&&) + DIM work_arr(0) AS _INTEGER64 + FOR i& = lb& TO ub& + IF source_arr&&(i&) <= value&& THEN + CALL ARR_INT64.push(work_arr&&(), source_arr&&(i&)) + END IF + NEXT i& + CALL ARR_INT64.shift(work_arr&&(), tmp&&) + CALL ARR_INT64.copy(work_arr&&(), dest_arr&&()) +END SUB + + +'' +' Finds and replaces values across all elements in a _INTEGER64 ARRAY +' +' @param _INTEGER64 ARRAY arr&&() to check in +' @param _INTEGER64 find&& value to find +' @param _INTEGER64 replace&& value to replace with if found +' +SUB ARR_INT64.replace(arr&&(), find&&, replace&&) + DIM AS LONG lb, ub, i + lb& = LBOUND(arr&&) : ub& = UBOUND(arr&&) + FOR i& = lb& TO ub& + IF arr&&(i&) = find&& THEN + arr&&(i&) = replace&& + END IF + NEXT i& +END SUB + + +'' +' Inserts a new element into _INTEGER64 array after index +' +' @param _INTEGER64 ARRAY arr&&() array to work on +' @param _INTEGER64 value&& to insert +' @param INTEGER index% of element to insert at +' +SUB ARR_INT64.insert(arr&&(), value&&, index%) + DIM AS LONG lb, ub, i + DIM tmp AS _INTEGER64 + DIM work_arr(0) AS _INTEGER64 + lb& = LBOUND(arr&&) : ub& = UBOUND(arr&&) + IF index% >= lb& AND index% <= ub& THEN + ' build new array up to index + FOR i& = lb& TO index% + CALL ARR_INT64.push(work_arr&&(), arr&&(i&)) + NEXT i& + ' insert new element + CALL ARR_INT64.push(work_arr&&(), value&&) + ' finish building array from index + 1 + FOR i& = index% + 1 TO ub& + CALL ARR_INT64.push(work_arr&&(), arr&&(i&)) + NEXT i& + CALL ARR_INT64.shift(work_arr&&(), tmp&&) + CALL ARR_INT64.copy(work_arr&&(), arr&&()) + END IF +END SUB + + +'' +' Removes element from a _INTEGER64 array by element index +' +' @param _INTEGER64 ARRAY arr&&() array to work on +' @param INTEGER index% of element to remove +' +SUB ARR_INT64.remove(arr&&(), index%) + DIM AS LONG lb, ub, i + DIM tmp AS _INTEGER64 + DIM work_arr(0) AS _INTEGER64 + lb& = LBOUND(arr&&) : ub& = UBOUND(arr&&) + IF index% >= lb& AND index% <= ub& THEN + ' build new array up to index + FOR i& = lb& TO index% - 1 + CALL ARR_INT64.push(work_arr&&(), arr&&(i&)) + NEXT i& + ' skip elements + FOR i& = index% + 1 TO ub& + CALL ARR_INT64.push(work_arr&&(), arr&&(i&)) + NEXT i& + CALL ARR_INT64.shift(work_arr&&(), tmp&&) + CALL ARR_INT64.copy(work_arr&&(), arr&&()) + END IF +END SUB + + +'' +' Filters a _INTEGER64 array to only elements that have odd values +' +' @param _INTEGER64 ARRAY source_arr&&() array to work on +' @param _INTEGER64 ARRAY dest_arr&&() array to store in +' +SUB ARR_INT64.odd(source_arr&&(), dest_arr&&()) + DIM AS LONG lb, ub, i + DIM tmp AS _INTEGER64 + lb& = LBOUND(source_arr&&) : ub& = UBOUND(source_arr&&) + DIM work_arr(0) AS _INTEGER64 + FOR i& = lb& TO ub& + IF source_arr&&(i&) MOD 2 <> 0 THEN + CALL ARR_INT64.push(work_arr&&(), source_arr&&(i&)) + END IF + NEXT i& + CALL ARR_INT64.shift(work_arr&&(), tmp&&) + CALL ARR_INT64.copy(work_arr&&(), dest_arr&&()) +END SUB + + +'' +' Filters a _INTEGER64 array to only elements that have even values +' +' @param _INTEGER64 ARRAY source_arr&&() array to work on +' @param _INTEGER64 ARRAY dest_arr&&() array to store in +' +SUB ARR_INT64.even(source_arr&&(), dest_arr&&()) + DIM AS LONG lb, ub, i + DIM tmp AS _INTEGER64 + lb& = LBOUND(source_arr&&) : ub& = UBOUND(source_arr&&) + DIM work_arr(0) AS _INTEGER64 + FOR i& = lb& TO ub& + IF source_arr&&(i&) MOD 2 = 0 THEN + CALL ARR_INT64.push(work_arr&&(), source_arr&&(i&)) + END IF + NEXT i& + CALL ARR_INT64.shift(work_arr&&(), tmp&&) + CALL ARR_INT64.copy(work_arr&&(), dest_arr&&()) +END SUB + + +'' +' Filters a _INTEGER64 array to only elements that have values evenly divisible by divisor +' +' @param _INTEGER64 ARRAY source_arr&&() array to work on +' @param _INTEGER64 ARRAY dest_arr&&() array to store in +' @param _INTEGER64 divisor&& for modulo +' +SUB ARR_INT64.mod(source_arr&&(), dest_arr&&(), divisor&&) + DIM AS LONG lb, ub, i + DIM tmp AS _INTEGER64 + lb& = LBOUND(source_arr&&) : ub& = UBOUND(source_arr&&) + DIM work_arr(0) AS _INTEGER64 + FOR i& = lb& TO ub& + IF source_arr&&(i&) MOD divisor&& = 0 THEN + CALL ARR_INT64.push(work_arr&&(), source_arr&&(i&)) + END IF + NEXT i& + CALL ARR_INT64.shift(work_arr&&(), tmp&&) + CALL ARR_INT64.copy(work_arr&&(), dest_arr&&()) +END SUB + + +'' +' Filters a _INTEGER64 array to only elements between min and max +' +' @param _INTEGER64 ARRAY source_arr&&() array to work on +' @param _INTEGER64 ARRAY dest_arr&&() array to store in +' @param _INTEGER64 min&& to be greater than or equal to be returned +' @param _INTEGER64 max&& to be less than or equal to be returned +' +SUB ARR_INT64.between(source_arr&&(), dest_arr&&(), min&&, max&&) + DIM AS LONG lb, ub, i + DIM tmp AS _INTEGER64 + lb& = LBOUND(source_arr&&) : ub& = UBOUND(source_arr&&) + DIM work_arr(0) AS _INTEGER64 + FOR i& = lb& TO ub& + IF source_arr&&(i&) >= min&& _ + AND source_arr&&(i&) <= max&& THEN + CALL ARR_INT64.push(work_arr&&(), source_arr&&(i&)) + END IF + NEXT i& + CALL ARR_INT64.shift(work_arr&&(), tmp&&) + CALL ARR_INT64.copy(work_arr&&(), dest_arr&&()) +END SUB + + +'' +' Sorts _INTEGER64 array in ascending order +' +' @param _INTEGER64 ARRAY source_arr&&() array to sort +' @param _INTEGER64 ARRAY dest_arr&&() array to store sorted in +' +SUB ARR_INT64.sort(source_arr&&(), dest_arr&&()) + DIM AS LONG lb, ub, count + lb& = LBOUND(source_arr&&) : ub& = UBOUND(source_arr&&) + count& = ub& - lb& + REDIM dest_arr(0 TO count&) AS _INTEGER64 + CALL ARR_INT64.copy(source_arr&&(), dest_arr&&()) + CALL ARR_INT64.quicksort(dest_arr&&(), lb&, ub&, 0) +END SUB + + +'' +' Sorts _INTEGER64 array in descending order +' +' @param _INTEGER64 ARRAY source_arr&&() array to sort +' @param _INTEGER64 ARRAY dest_arr&&() array to store sorted in +' +SUB ARR_INT64.rsort(source_arr&&(), dest_arr&&()) + DIM AS LONG lb, ub, count + lb& = LBOUND(source_arr&&) : ub& = UBOUND(source_arr&&) + count& = ub& - lb& + REDIM dest_arr(0 TO count&) AS _INTEGER64 + CALL ARR_INT64.copy(source_arr&&(), dest_arr&&()) + CALL ARR_INT64.quicksort(dest_arr&&(), lb&, ub&, 1) +END SUB + + +'' +' Quicksort array with pivot algorithm by logiclrd +' +' @link https://www.tek-tips.com/faqs.cfm?fid=336 +' @param _INTEGER64 ARRAY array&&() to sort +' @param INTEGER start% of range to sort +' @param INTEGER finish% range of sort +' @param INTEGER order% to sort by (0 = asc / 1 = desc) +' +SUB ARR_INT64.quicksort(arr&&(), start%, finish%, order%) + DIM i AS LONG + DIM pivot AS INTEGER + DIM pivotvalue AS _INTEGER64 + + 'first, partition the array + pivot% = start% + pivotvalue&& = arr&&(pivot%) + FOR i& = start% + 1 TO finish% + IF order% = 0 THEN ' ascending order + IF arr&&(i&) < pivotvalue&& THEN + arr&&(pivot%) = arr&&(i&) + arr&&(i&) = arr&&(pivot% + 1) + arr&&(pivot% + 1) = pivotvalue&& + pivot% = pivot% + 1 + END IF + ELSEIF order% = 1 THEN ' descending order + IF arr&&(i&) > pivotvalue&& THEN + arr&&(pivot%) = arr&&(i&) + arr&&(i&) = arr&&(pivot% + 1) + arr&&(pivot% + 1) = pivotvalue&& + pivot% = pivot% + 1 + END IF + END IF + NEXT i& + + 'then, sort the subarrays to each side of the pivot + IF pivot% - start% >= 2 THEN + CALL ARR_INT64.quicksort(arr&&(), start%, pivot% - 1, order%) + END IF + IF finish% - pivot% >= 2 THEN + CALL ARR_INT64.quicksort(arr&&(), pivot% + 1, finish%, order%) + END IF +END SUB diff --git a/ARR/ARR_LONG.BAS b/ARR/ARR_LONG.BAS new file mode 100644 index 0000000..648d9e9 --- /dev/null +++ b/ARR/ARR_LONG.BAS @@ -0,0 +1,820 @@ +'$DYNAMIC +$LET GJ_LIB_ARR_LONG_BAS = 1 + +'' +' Slice an array from source to destination starting at index and count slices +' +' @param LONG() source_arr& to slice from +' @param LONG() dest_arr& to put slices into +' @param INTEGER start_idx% starting index to use as slice range +' @param INTEGER count% number of slices - if negative, backwards from index +' +SUB ARR_LONG.slice(source_arr&(), dest_arr&(), start_idx%, count%) + DIM AS LONG ub, lb, i, n + lb& = LBOUND(source_arr&) : ub& = UBOUND(source_arr&) + IF start_idx% < lb& OR start_idx% + count% > ub& THEN EXIT SUB ' out of range + IF ub& - lb& < count% THEN EXIT SUB ' too many and not enough + REDIM dest_arr(0 TO ABS(count%)) AS LONG + IF SGN(count%) = -1 THEN + IF ((start_idx% - 1) - ABS(count%)) < 0 THEN EXIT SUB ' out of range + n& = 0 + FOR i& = (start_idx% - 1) TO ((start_idx% - 1) - ABS(count%)) STEP -1 + dest_arr&(n&) = source_arr&(i&) + n& = n& + 1 + NEXT i& + ELSE + IF ((start_idx% + 1) + ABS(count%)) > (ub& - lb&) THEN EXIT SUB ' out of range + n& = 0 + FOR i& = start_idx% + 1 TO ((start_idx% + 1) + count%) STEP 1 + dest_arr&(n&) = source_arr&(i&) + n& = n& + 1 + NEXT i& + END IF +END SUB + + +'' +' Push a long onto the end of a LONG array +' +' @param LONG arr&() array to push into +' @param LONG value& of byte to push +' +SUB ARR_LONG.push(arr&(), value&) + DIM AS LONG ub, lb + lb& = LBOUND(arr&) : ub& = UBOUND(arr&) + REDIM _PRESERVE arr(lb& TO (ub& + 1)) AS LONG + arr&(ub& + 1) = value& +END SUB + + +'' +' Pop a long from the end of a LONG array +' +' @param LONG arr&() array to pop from +' @param LONG var& of long to store popped long +' +SUB ARR_LONG.pop(arr&(), var&) + DIM AS LONG ub, lb + lb& = LBOUND(arr&) : ub& = UBOUND(arr&) + var& = arr&(ub&) + REDIM _PRESERVE arr(lb& TO (ub& - 1)) AS LONG +END SUB + + +'' +' Pop a long from the beginning of a LONG array +' +' @param LONG arr&() array to pop from +' @param LONG var& of long to store popped long +' +SUB ARR_LONG.shift(arr&(), var&) + DIM AS LONG ub, lb, i + lb& = LBOUND(arr&) : ub& = UBOUND(arr&) + var& = arr&(lb&) + FOR i& = lb& TO ub& - 1 + arr&(i&) = arr&(i& + 1) + NEXT i& + REDIM _PRESERVE arr(lb& + 1 TO ub&) AS LONG +END SUB + + +'' +' Copy an array of LONGs to another LONG array +' +' @param LONG ARRAY source_arr&() source array to copy +' @param LONG ARRAY dest_arr&() dest array to copy into +' +SUB ARR_LONG.copy(source_arr&(), dest_arr&()) + DIM AS LONG ub, lb, i + lb& = LBOUND(source_arr&) : ub& = UBOUND(source_arr&) + REDIM dest_arr(lb& TO ub&) AS LONG + FOR i& = lb& TO ub& + dest_arr&(i&) = source_arr&(i&) + NEXT i& +END SUB + + +'' +' Push a long into the beginning of a LONG array +' +' @param LONG arr&() array to push into +' @param LONG value& of long to push +' +SUB ARR_LONG.unshift(arr&(), value&) + DIM AS LONG ub, lb, i + lb& = LBOUND(arr&) : ub& = UBOUND(arr&) + DIM work(lb& TO ub&) AS LONG + CALL ARR_LONG.copy(arr&(), work&()) + REDIM _PRESERVE arr(lb& TO (ub& + 1)) AS LONG + FOR i& = lb& + 1 TO ub& + 1 + arr&(i&) = work&(i& - 1) + NEXT i& + arr&(lb&) = value& +END SUB + + +'' +' Joins an array of LONGs as a string +' +' @param LONG ARRAY arr&() to get as a string +' @param STRING s$ to store stringified array in +' +SUB ARR_LONG.join(arr&(), s$) + DIM AS LONG ub, lb, i + s$ = "" + lb& = LBOUND(arr&) : ub& = UBOUND(arr&) + FOR i& = lb& TO ub& + s$ = s$ + _TRIM$(STR$(arr&(i&))) + ", " + NEXT i& + ' remove trailing comma + s$ = MID$(s$, 1, (LEN(s$)-LEN(", "))) +END SUB + + +'' +' Create a new LONG array using string of longs seperated by commas +' +' @param LONG ARRAY arr&() to store the longs in +' @param STRING s$ string of comma separated longs +' +SUB ARR_LONG.new(arr&(), s$) + DIM AS LONG i, count + DIM t AS STRING + count& = 0 + FOR i& = 1 TO LEN(s$) + IF ASC(s$, i&) = ASC(",") THEN count& = count& + 1 + NEXT i& + REDIM arr(0 TO count&) AS LONG + IF count& = 0 THEN + arr&(0) = VAL(s$) + EXIT SUB + END IF + count& = 0 + FOR i& = 1 TO LEN(s$) + t$ = t$ + CHR$(ASC(s$, i&)) + IF ASC(s$, i&) = ASC(",") OR i& = LEN(s$) THEN + arr&(count&) = VAL(t$) + count& = count& + 1 + t$ = "" + END IF + NEXT i& +END SUB + + +'' +' Return the visually longest element of a LONG array +' +' @param LONG ARRAY arr&() to check in +' @return LONG value of visually longest element +' +FUNCTION ARR_LONG.longest&(arr&()) + DIM AS LONG lb, ub, i, res, lw + lb& = LBOUND(arr&) : ub& = UBOUND(arr&) + res& = 0 : lw& = 0 + FOR i& = lb& TO ub& + IF LEN(_TRIM$(STR$(arr&(i&)))) > lw& THEN + lw& = LEN(_TRIM$(STR$(arr&(i&)))) + res& = i& + END IF + NEXT i& + ARR_LONG.longest& = arr&(res&) +END FUNCTION + + +'' +' Perform some math on every element of a LONG array +' +' @param LONG ARRAY source_arr&() to do math on +' @param LONG ARRAY dest_arr&() to store results in +' @param STRING op$ one of: +' and or xor shl shr +' "+", "-", "*", "\", "&&", "||", "!!", "<<", ">>" +' @param LONG value& to use for operand +' +SUB ARR_LONG.math(source_arr&(), dest_arr&(), op$, value&) + DIM AS LONG lb, ub, i + lb& = LBOUND(source_arr&) : ub& = UBOUND(source_arr&) + REDIM dest_arr(lb& TO ub&) AS LONG + FOR i& = lb& TO ub& + SELECT CASE op$ + CASE "+": + dest_arr&(i&) = source_arr&(i&) + value& + CASE "-": + dest_arr&(i&) = source_arr&(i&) - value& + CASE "*": + dest_arr&(i&) = source_arr&(i&) * value& + CASE "\": + IF value& > 0 THEN + dest_arr&(i&) = source_arr&(i&) \ value& + END IF + CASE "&&": + dest_arr&(i&) = source_arr&(i&) AND value& + CASE "||": + dest_arr&(i&) = source_arr&(i&) OR value& + CASE "!!": + dest_arr&(i&) = source_arr&(i&) XOR value& + CASE "<<": + dest_arr&(i&) = _SHL(source_arr&(i&), value&) + CASE ">>": + dest_arr&(i&) = _SHR(source_arr&(i&), value&) + END SELECT + NEXT i& +END SUB + + +'' +' Return the minimum element value in LONG array +' +' @param LONG ARRAY arr&() to check in +' @return LONG minimum value found +' +FUNCTION ARR_LONG.min&(arr&()) + DIM AS LONG lb, ub, i + DIM AS LONG s + lb& = LBOUND(arr&) : ub& = UBOUND(arr&) + s& = 127 + FOR i& = lb& TO ub& + IF arr&(i&) < s& THEN + s& = arr&(i&) + END IF + NEXT i& + ARR_LONG.min& = s& +END FUNCTION + + +'' +' Return the maximum element value in LONG array +' +' @param LONG ARRAY arr&() to check in +' @return LONG maximum value found +' +FUNCTION ARR_LONG.max&(arr&()) + DIM AS LONG lb, ub, i + DIM AS LONG s + lb& = LBOUND(arr&) : ub& = UBOUND(arr&) + s& = 0 + FOR i& = lb& TO ub& + IF arr&(i&) > s& THEN + s& = arr&(i&) + END IF + NEXT i& + ARR_LONG.max& = s& +END FUNCTION + + +'' +' Return the visually shortest element of a LONG array +' +' @param LONG ARRAY arr&() to check in +' @return LONG value of visually shortest element +' +FUNCTION ARR_LONG.shortest&(arr&()) + DIM AS LONG lb, ub, i, res, sw + lb& = LBOUND(arr&) : ub& = UBOUND(arr&) + res& = 0 : sw& = 0 + FOR i& = lb& TO ub& + IF LEN(_TRIM$(STR$(arr&(i&)))) < sw& THEN + sw& = LEN(_TRIM$(STR$(arr&(i&)))) + res& = i& + END IF + NEXT i& + ARR_LONG.shortest& = arr&(res&) +END FUNCTION + + +'' +' Return the first element of a LONG array +' +' @param LONG ARRAY arr&() to check in +' @return LONG value of first element +' +FUNCTION ARR_LONG.first&(arr&()) + ARR_LONG.first& = arr&(LBOUND(arr&)) +END FUNCTION + + +'' +' Return the last element of a LONG array +' +' @param LONG ARRAY arr&() to check in +' @return LONG value of last element +' +FUNCTION ARR_LONG.last&(arr&()) + ARR_LONG.last& = arr&(UBOUND(arr&)) +END FUNCTION + + +'' +' Return every nth array element of a LONG array +' +' @param LONG ARRAY source_arr&() to get from +' @param LONG ARRAY dest_arr&() to store in +' @param INTEGER nth% element +' +SUB ARR_LONG.nth(source_arr&(), dest_arr&(), nth%) + DIM AS LONG lb, ub, i, n + lb& = LBOUND(source_arr&) : ub& = UBOUND(source_arr&) + n& = (ub& - lb&) \ nth% + REDIM dest_arr(n&) AS LONG + n& = 0 + FOR i& = lb& to ub& + IF i& MOD nth% = 0 THEN + dest_arr&(n&) = source_arr&(i&) + n& = n& + 1 + END IF + NEXT i& +END SUB + + +'' +' Checks if value exists in LONG array +' +' @param LONG ARRAY arr&() to check in +' @param LONG value& value to check for +' @return INTEGER -1 if found or 0 if not found +' +FUNCTION ARR_LONG.in%(arr&(), value&) + DIM AS LONG lb, ub, i + lb& = LBOUND(arr&) : ub& = UBOUND(arr&) + FOR i& = lb& TO ub& + IF arr&(i&) = value& THEN + ARR_LONG.in% = -1 + EXIT FUNCTION + END IF + NEXT i& + ARR_LONG.in% = 0 +END FUNCTION + + +'' +' Checks if value exists in LONG array and returns index if found +' +' @param LONG ARRAY arr&() to check in +' @param LONG value& value to check for +' @return INTEGER index of element if found or -1 if not found +' +FUNCTION ARR_LONG.find%(arr&(), value&) + DIM AS LONG lb, ub, i + lb& = LBOUND(arr&) : ub& = UBOUND(arr&) + FOR i& = lb& TO ub& + IF arr&(i&) = value& THEN + ARR_LONG.find% = i& + EXIT FUNCTION + END IF + NEXT i& + ARR_LONG.find% = -1 +END FUNCTION + + +'' +' Return the number of elements in a LONG array +' +' @param LONG ARRAY arr&() to count +' @return INTEGER number of elements +' +FUNCTION ARR_LONG.count&(arr&()) + ARR_LONG.count& = UBOUND(arr&) - LBOUND(arr&) +END FUNCTION + + +'' +' Return the size of a LONG array +' +' @param LONG ARRAY arr&() to get size of +' @return LONG size in bytes +' +FUNCTION ARR_LONG.size&(arr&()) + ARR_LONG.size& = LEN(arr&()) +END FUNCTION + + +'' +' Reverses the elements of a LONG array +' +' @param LONG ARRAY source_arr&() to reverse +' @param LONG ARRAY dest_arr&() to store reversed array in +' +SUB ARR_LONG.reverse(source_arr&(), dest_arr&()) + DIM AS LONG lb, ub, i, n + lb& = LBOUND(source_arr&) : ub& = UBOUND(source_arr&) + REDIM dest_arr(0 TO (ub& - lb&)) AS LONG + n& = 0 + FOR i& = ub& TO lb& STEP -1 + dest_arr&(n&) = source_arr&(i&) + n& = n& + 1 + NEXT i& +END SUB + + +'' +' Returns a random long from a LONG array +' +' @param LONG ARRAY arr&() array to get random element from +' @return LONG random element +' +FUNCTION ARR_LONG.random&(arr&()) + DIM AS LONG lb, ub + lb& = LBOUND(arr&) : ub& = UBOUND(arr&) + RANDOMIZE TIMER + ARR_LONG.random& = arr&(INT(RND * (ub& - lb&)) + 1) +END FUNCTION + + +'' +' Returns the sum of all elements in a LONG array +' +' @param LONG ARRAY arr&() array to get some for +' @return LONG sum of all elements +' +FUNCTION ARR_LONG.sum&(arr&()) + DIM AS LONG lb, ub, i + DIM sum AS LONG + lb& = LBOUND(arr&) : ub& = UBOUND(arr&) + FOR i& = lb& TO ub& + sum& = sum& + arr&(i&) + NEXT i& + ARR_LONG.sum& = sum& +END FUNCTION + + +'' +' Returns the average value of elements in a LONG array +' +' @param LONG ARRAY arr&() array to get average for +' @return LONG average of elements +' +FUNCTION ARR_LONG.avg&(arr&()) + DIM AS LONG lb, ub, i + DIM sum AS LONG + lb& = LBOUND(arr&) : ub& = UBOUND(arr&) + FOR i& = lb& TO ub& + sum& = sum& + arr&(i&) + NEXT i& + ARR_LONG.avg& = sum& / (ub& - lb&) +END FUNCTION + + +'' +' Shuffle the elements of a LONG array +' +' @param LONG ARRAY source_arr&() to shuffle +' @param LONG ARRAY dest_arr&() to store shuffled array in +' +SUB ARR_LONG.shuffle(source_arr&(), dest_arr&()) + DIM AS LONG lb, ub, i, count + lb& = LBOUND(source_arr&) : ub& = UBOUND(source_arr&) + count& = ub& - lb& + REDIM dest_arr(0 TO count&) AS LONG + CALL ARR_LONG.copy(source_arr&(), dest_arr&()) + RANDOMIZE TIMER + FOR i& = 0 TO count& + SWAP dest_arr&(i&), dest_arr&(lb& + RND * (ub& - lb&)) + NEXT i& +END SUB + + +'' +' Makes a LONG array contain only unique values +' +' @param LONG ARRAY source_arr&() array to get uniques for +' @param LONG ARRAY dest_arr&() array to store uniques in +' +SUB ARR_LONG.unique(source_arr&(), dest_arr&()) + DIM AS LONG lb, ub, i + DIM tmp AS LONG + lb& = LBOUND(source_arr&) : ub& = UBOUND(source_arr&) + DIM work_arr(0) AS LONG + FOR i& = lb& TO ub& + IF NOT ARR_LONG.in%(work_arr&(), source_arr&(i&)) THEN + CALL ARR_LONG.push(work_arr&(), source_arr&(i&)) + END IF + NEXT i& + CALL ARR_LONG.shift(work_arr&(), tmp&) + CALL ARR_LONG.copy(work_arr&(), dest_arr&()) +END SUB + + +'' +' Filters a LONG array to only elements greater than value +' +' @param LONG ARRAY source_arr&() array to work on +' @param LONG ARRAY dest_arr&() array to store in +' @param LONG value& to be greater than to be returned +' +SUB ARR_LONG.gt(source_arr&(), dest_arr&(), value&) + DIM AS LONG lb, ub, i + DIM tmp AS LONG + lb& = LBOUND(source_arr&) : ub& = UBOUND(source_arr&) + DIM work_arr(0) AS LONG + FOR i& = lb& TO ub& + IF source_arr&(i&) > value& THEN + CALL ARR_LONG.push(work_arr&(), source_arr&(i&)) + END IF + NEXT i& + CALL ARR_LONG.shift(work_arr&(), tmp&) + CALL ARR_LONG.copy(work_arr&(), dest_arr&()) +END SUB + + +'' +' Filters a LONG array to only elements greater than or equal to value +' +' @param LONG ARRAY source_arr&() array to work on +' @param LONG ARRAY dest_arr&() array to store in +' @param LONG value& to be greater than or equal to be returned +' +SUB ARR_LONG.gte(source_arr&(), dest_arr&(), value&) + DIM AS LONG lb, ub, i + DIM tmp AS LONG + lb& = LBOUND(source_arr&) : ub& = UBOUND(source_arr&) + DIM work_arr(0) AS LONG + FOR i& = lb& TO ub& + IF source_arr&(i&) >= value& THEN + CALL ARR_LONG.push(work_arr&(), source_arr&(i&)) + END IF + NEXT i& + CALL ARR_LONG.shift(work_arr&(), tmp&) + CALL ARR_LONG.copy(work_arr&(), dest_arr&()) +END SUB + + +'' +' Filters a LONG array to only elements less than value +' +' @param LONG ARRAY source_arr&() array to work on +' @param LONG ARRAY dest_arr&() array to store in +' @param LONG value& to be less than to be returned +' +SUB ARR_LONG.lt(source_arr&(), dest_arr&(), value&) + DIM AS LONG lb, ub, i + DIM tmp AS LONG + lb& = LBOUND(source_arr&) : ub& = UBOUND(source_arr&) + DIM work_arr(0) AS LONG + FOR i& = lb& TO ub& + IF source_arr&(i&) < value& THEN + CALL ARR_LONG.push(work_arr&(), source_arr&(i&)) + END IF + NEXT i& + CALL ARR_LONG.shift(work_arr&(), tmp&) + CALL ARR_LONG.copy(work_arr&(), dest_arr&()) +END SUB + + +'' +' Filters a LONG array to only elements less than or equal to value +' +' @param LONG ARRAY source_arr&() array to work on +' @param LONG ARRAY dest_arr&() array to store in +' @param LONG value& to be less than or equal to be returned +' +SUB ARR_LONG.lte(source_arr&(), dest_arr&(), value&) + DIM AS LONG lb, ub, i + DIM tmp AS LONG + lb& = LBOUND(source_arr&) : ub& = UBOUND(source_arr&) + DIM work_arr(0) AS LONG + FOR i& = lb& TO ub& + IF source_arr&(i&) <= value& THEN + CALL ARR_LONG.push(work_arr&(), source_arr&(i&)) + END IF + NEXT i& + CALL ARR_LONG.shift(work_arr&(), tmp&) + CALL ARR_LONG.copy(work_arr&(), dest_arr&()) +END SUB + + +'' +' Finds and replaces values across all elements in a LONG ARRAY +' +' @param LONG ARRAY arr&() to check in +' @param LONG find& value to find +' @param LONG replace& value to replace with if found +' +SUB ARR_LONG.replace(arr&(), find&, replace&) + DIM AS LONG lb, ub, i + lb& = LBOUND(arr&) : ub& = UBOUND(arr&) + FOR i& = lb& TO ub& + IF arr&(i&) = find& THEN + arr&(i&) = replace& + END IF + NEXT i& +END SUB + + +'' +' Inserts a new element into LONG array after index +' +' @param LONG ARRAY arr&() array to work on +' @param LONG value& to insert +' @param INTEGER index% of element to insert at +' +SUB ARR_LONG.insert(arr&(), value&, index%) + DIM AS LONG lb, ub, i + DIM tmp AS LONG + DIM work_arr(0) AS LONG + lb& = LBOUND(arr&) : ub& = UBOUND(arr&) + IF index% >= lb& AND index% <= ub& THEN + ' build new array up to index + FOR i& = lb& TO index% + CALL ARR_LONG.push(work_arr&(), arr&(i&)) + NEXT i& + ' insert new element + CALL ARR_LONG.push(work_arr&(), value&) + ' finish building array from index + 1 + FOR i& = index% + 1 TO ub& + CALL ARR_LONG.push(work_arr&(), arr&(i&)) + NEXT i& + CALL ARR_LONG.shift(work_arr&(), tmp&) + CALL ARR_LONG.copy(work_arr&(), arr&()) + END IF +END SUB + + +'' +' Removes element from a LONG array by element index +' +' @param LONG ARRAY arr&() array to work on +' @param INTEGER index% of element to remove +' +SUB ARR_LONG.remove(arr&(), index%) + DIM AS LONG lb, ub, i + DIM tmp AS LONG + DIM work_arr(0) AS LONG + lb& = LBOUND(arr&) : ub& = UBOUND(arr&) + IF index% >= lb& AND index% <= ub& THEN + ' build new array up to index + FOR i& = lb& TO index% - 1 + CALL ARR_LONG.push(work_arr&(), arr&(i&)) + NEXT i& + ' skip elements + FOR i& = index% + 1 TO ub& + CALL ARR_LONG.push(work_arr&(), arr&(i&)) + NEXT i& + CALL ARR_LONG.shift(work_arr&(), tmp&) + CALL ARR_LONG.copy(work_arr&(), arr&()) + END IF +END SUB + + +'' +' Filters a LONG array to only elements that have odd values +' +' @param LONG ARRAY source_arr&() array to work on +' @param LONG ARRAY dest_arr&() array to store in +' +SUB ARR_LONG.odd(source_arr&(), dest_arr&()) + DIM AS LONG lb, ub, i + DIM tmp AS LONG + lb& = LBOUND(source_arr&) : ub& = UBOUND(source_arr&) + DIM work_arr(0) AS LONG + FOR i& = lb& TO ub& + IF source_arr&(i&) MOD 2 <> 0 THEN + CALL ARR_LONG.push(work_arr&(), source_arr&(i&)) + END IF + NEXT i& + CALL ARR_LONG.shift(work_arr&(), tmp&) + CALL ARR_LONG.copy(work_arr&(), dest_arr&()) +END SUB + + +'' +' Filters a LONG array to only elements that have even values +' +' @param LONG ARRAY source_arr&() array to work on +' @param LONG ARRAY dest_arr&() array to store in +' +SUB ARR_LONG.even(source_arr&(), dest_arr&()) + DIM AS LONG lb, ub, i + DIM tmp AS LONG + lb& = LBOUND(source_arr&) : ub& = UBOUND(source_arr&) + DIM work_arr(0) AS LONG + FOR i& = lb& TO ub& + IF source_arr&(i&) MOD 2 = 0 THEN + CALL ARR_LONG.push(work_arr&(), source_arr&(i&)) + END IF + NEXT i& + CALL ARR_LONG.shift(work_arr&(), tmp&) + CALL ARR_LONG.copy(work_arr&(), dest_arr&()) +END SUB + + +'' +' Filters a LONG array to only elements that have values evenly divisible by divisor +' +' @param LONG ARRAY source_arr&() array to work on +' @param LONG ARRAY dest_arr&() array to store in +' @param LONG divisor& for modulo +' +SUB ARR_LONG.mod(source_arr&(), dest_arr&(), divisor&) + DIM AS LONG lb, ub, i + DIM tmp AS LONG + lb& = LBOUND(source_arr&) : ub& = UBOUND(source_arr&) + DIM work_arr(0) AS LONG + FOR i& = lb& TO ub& + IF source_arr&(i&) MOD divisor& = 0 THEN + CALL ARR_LONG.push(work_arr&(), source_arr&(i&)) + END IF + NEXT i& + CALL ARR_LONG.shift(work_arr&(), tmp&) + CALL ARR_LONG.copy(work_arr&(), dest_arr&()) +END SUB + + +'' +' Filters a LONG array to only elements between min and max +' +' @param LONG ARRAY source_arr&() array to work on +' @param LONG ARRAY dest_arr&() array to store in +' @param LONG min& to be greater than or equal to be returned +' @param LONG max& to be less than or equal to be returned +' +SUB ARR_LONG.between(source_arr&(), dest_arr&(), min&, max&) + DIM AS LONG lb, ub, i + DIM tmp AS LONG + lb& = LBOUND(source_arr&) : ub& = UBOUND(source_arr&) + DIM work_arr(0) AS LONG + FOR i& = lb& TO ub& + IF source_arr&(i&) >= min& _ + AND source_arr&(i&) <= max& THEN + CALL ARR_LONG.push(work_arr&(), source_arr&(i&)) + END IF + NEXT i& + CALL ARR_LONG.shift(work_arr&(), tmp&) + CALL ARR_LONG.copy(work_arr&(), dest_arr&()) +END SUB + + +'' +' Sorts LONG array in ascending order +' +' @param LONG ARRAY source_arr&() array to sort +' @param LONG ARRAY dest_arr&() array to store sorted in +' +SUB ARR_LONG.sort(source_arr&(), dest_arr&()) + DIM AS LONG lb, ub, count + lb& = LBOUND(source_arr&) : ub& = UBOUND(source_arr&) + count& = ub& - lb& + REDIM dest_arr(0 TO count&) AS LONG + CALL ARR_LONG.copy(source_arr&(), dest_arr&()) + CALL ARR_LONG.quicksort(dest_arr&(), lb&, ub&, 0) +END SUB + + +'' +' Sorts LONG array in descending order +' +' @param LONG ARRAY source_arr&() array to sort +' @param LONG ARRAY dest_arr&() array to store sorted in +' +SUB ARR_LONG.rsort(source_arr&(), dest_arr&()) + DIM AS LONG lb, ub, count + lb& = LBOUND(source_arr&) : ub& = UBOUND(source_arr&) + count& = ub& - lb& + REDIM dest_arr(0 TO count&) AS LONG + CALL ARR_LONG.copy(source_arr&(), dest_arr&()) + CALL ARR_LONG.quicksort(dest_arr&(), lb&, ub&, 1) +END SUB + + +'' +' Quicksort array with pivot algorithm by logiclrd +' +' @link https://www.tek-tips.com/faqs.cfm?fid=336 +' @param LONG ARRAY array&() to sort +' @param INTEGER start% of range to sort +' @param INTEGER finish% range of sort +' @param INTEGER order% to sort by (0 = asc / 1 = desc) +' +SUB ARR_LONG.quicksort(arr&(), start%, finish%, order%) + DIM i AS LONG + DIM pivot AS INTEGER + DIM pivotvalue AS LONG + + 'first, partition the array + pivot% = start% + pivotvalue& = arr&(pivot%) + FOR i& = start% + 1 TO finish% + IF order% = 0 THEN ' ascending order + IF arr&(i&) < pivotvalue& THEN + arr&(pivot%) = arr&(i&) + arr&(i&) = arr&(pivot% + 1) + arr&(pivot% + 1) = pivotvalue& + pivot% = pivot% + 1 + END IF + ELSEIF order% = 1 THEN ' descending order + IF arr&(i&) > pivotvalue& THEN + arr&(pivot%) = arr&(i&) + arr&(i&) = arr&(pivot% + 1) + arr&(pivot% + 1) = pivotvalue& + pivot% = pivot% + 1 + END IF + END IF + NEXT i& + + 'then, sort the subarrays to each side of the pivot + IF pivot% - start% >= 2 THEN + CALL ARR_LONG.quicksort(arr&(), start%, pivot% - 1, order%) + END IF + IF finish% - pivot% >= 2 THEN + CALL ARR_LONG.quicksort(arr&(), pivot% + 1, finish%, order%) + END IF +END SUB diff --git a/ARR/ARR_SNG.BAS b/ARR/ARR_SNG.BAS new file mode 100644 index 0000000..5497bc5 --- /dev/null +++ b/ARR/ARR_SNG.BAS @@ -0,0 +1,820 @@ +'$DYNAMIC +$LET GJ_LIB_ARR_SNG_BAS = 1 + +'' +' Slice an array from source to destination starting at index and count slices +' +' @param SINGLE() source_arr! to slice from +' @param SINGLE() dest_arr! to put slices into +' @param INTEGER start_idx% starting index to use as slice range +' @param INTEGER count% number of slices - if negative, backwards from index +' +SUB ARR_SNG.slice(source_arr!(), dest_arr!(), start_idx%, count%) + DIM AS LONG ub, lb, i, n + lb& = LBOUND(source_arr!) : ub& = UBOUND(source_arr!) + IF start_idx% < lb& OR start_idx% + count% > ub& THEN EXIT SUB ' out of range + IF ub& - lb& < count% THEN EXIT SUB ' too many and not enough + REDIM dest_arr(0 TO ABS(count%)) AS SINGLE + IF SGN(count%) = -1 THEN + IF ((start_idx% - 1) - ABS(count%)) < 0 THEN EXIT SUB ' out of range + n& = 0 + FOR i& = (start_idx% - 1) TO ((start_idx% - 1) - ABS(count%)) STEP -1 + dest_arr!(n&) = source_arr!(i&) + n& = n& + 1 + NEXT i& + ELSE + IF ((start_idx% + 1) + ABS(count%)) > (ub& - lb&) THEN EXIT SUB ' out of range + n& = 0 + FOR i& = start_idx% + 1 TO ((start_idx% + 1) + count%) STEP 1 + dest_arr!(n&) = source_arr!(i&) + n& = n& + 1 + NEXT i& + END IF +END SUB + + +'' +' Push a sng onto the end of a SINGLE array +' +' @param SINGLE arr!() array to push into +' @param SINGLE value! of byte to push +' +SUB ARR_SNG.push(arr!(), value!) + DIM AS LONG ub, lb + lb& = LBOUND(arr!) : ub& = UBOUND(arr!) + REDIM _PRESERVE arr(lb& TO (ub& + 1)) AS SINGLE + arr!(ub& + 1) = value! +END SUB + + +'' +' Pop a sng from the end of a SINGLE array +' +' @param SINGLE arr!() array to pop from +' @param SINGLE var! of sng to store popped sng +' +SUB ARR_SNG.pop(arr!(), var!) + DIM AS LONG ub, lb + lb& = LBOUND(arr!) : ub& = UBOUND(arr!) + var! = arr!(ub&) + REDIM _PRESERVE arr(lb& TO (ub& - 1)) AS SINGLE +END SUB + + +'' +' Pop a sng from the beginning of a SINGLE array +' +' @param SINGLE arr!() array to pop from +' @param SINGLE var! of sng to store popped sng +' +SUB ARR_SNG.shift(arr!(), var!) + DIM AS LONG ub, lb, i + lb& = LBOUND(arr!) : ub& = UBOUND(arr!) + var! = arr!(lb&) + FOR i& = lb& TO ub& - 1 + arr!(i&) = arr!(i& + 1) + NEXT i& + REDIM _PRESERVE arr(lb& + 1 TO ub&) AS SINGLE +END SUB + + +'' +' Copy an array of SNGs to another SINGLE array +' +' @param SINGLE ARRAY source_arr!() source array to copy +' @param SINGLE ARRAY dest_arr!() dest array to copy into +' +SUB ARR_SNG.copy(source_arr!(), dest_arr!()) + DIM AS LONG ub, lb, i + lb& = LBOUND(source_arr!) : ub& = UBOUND(source_arr!) + REDIM dest_arr(lb& TO ub&) AS SINGLE + FOR i& = lb& TO ub& + dest_arr!(i&) = source_arr!(i&) + NEXT i& +END SUB + + +'' +' Push a sng into the beginning of a SINGLE array +' +' @param SINGLE arr!() array to push into +' @param SINGLE value! of sng to push +' +SUB ARR_SNG.unshift(arr!(), value!) + DIM AS LONG ub, lb, i + lb& = LBOUND(arr!) : ub& = UBOUND(arr!) + DIM work(lb& TO ub&) AS SINGLE + CALL ARR_SNG.copy(arr!(), work!()) + REDIM _PRESERVE arr(lb& TO (ub& + 1)) AS SINGLE + FOR i& = lb& + 1 TO ub& + 1 + arr!(i&) = work!(i& - 1) + NEXT i& + arr!(lb&) = value! +END SUB + + +'' +' Joins an array of SNGs as a string +' +' @param SINGLE ARRAY arr!() to get as a string +' @param STRING s$ to store stringified array in +' +SUB ARR_SNG.join(arr!(), s$) + DIM AS LONG ub, lb, i + s$ = "" + lb& = LBOUND(arr!) : ub& = UBOUND(arr!) + FOR i& = lb& TO ub& + s$ = s$ + _TRIM$(STR$(arr!(i&))) + ", " + NEXT i& + ' remove trailing comma + s$ = MID$(s$, 1, (LEN(s$)-LEN(", "))) +END SUB + + +'' +' Create a new SINGLE array using string of sngs seperated by commas +' +' @param SINGLE ARRAY arr!() to store the sngs in +' @param STRING s$ string of comma separated sngs +' +SUB ARR_SNG.new(arr!(), s$) + DIM AS LONG i, count + DIM t AS STRING + count& = 0 + FOR i& = 1 TO LEN(s$) + IF ASC(s$, i&) = ASC(",") THEN count& = count& + 1 + NEXT i& + REDIM arr(0 TO count&) AS SINGLE + IF count& = 0 THEN + arr!(0) = VAL(s$) + EXIT SUB + END IF + count& = 0 + FOR i& = 1 TO LEN(s$) + t$ = t$ + CHR$(ASC(s$, i&)) + IF ASC(s$, i&) = ASC(",") OR i& = LEN(s$) THEN + arr!(count&) = VAL(t$) + count& = count& + 1 + t$ = "" + END IF + NEXT i& +END SUB + + +'' +' Return the visually longest element of a SINGLE array +' +' @param SINGLE ARRAY arr!() to check in +' @return SINGLE value of visually longest element +' +FUNCTION ARR_SNG.longest!(arr!()) + DIM AS LONG lb, ub, i, res, lw + lb& = LBOUND(arr!) : ub& = UBOUND(arr!) + res& = 0 : lw& = 0 + FOR i& = lb& TO ub& + IF LEN(_TRIM$(STR$(arr!(i&)))) > lw& THEN + lw& = LEN(_TRIM$(STR$(arr!(i&)))) + res& = i& + END IF + NEXT i& + ARR_SNG.longest! = arr!(res&) +END FUNCTION + + +'' +' Perform some math on every element of a SINGLE array +' +' @param SINGLE ARRAY source_arr!() to do math on +' @param SINGLE ARRAY dest_arr!() to store results in +' @param STRING op$ one of: +' and or xor shl shr +' "+", "-", "*", "\", "&&", "||", "!!", "<<", ">>" +' @param SINGLE value! to use for operand +' +SUB ARR_SNG.math(source_arr!(), dest_arr!(), op$, value!) + DIM AS LONG lb, ub, i + lb& = LBOUND(source_arr!) : ub& = UBOUND(source_arr!) + REDIM dest_arr(lb& TO ub&) AS SINGLE + FOR i& = lb& TO ub& + SELECT CASE op$ + CASE "+": + dest_arr!(i&) = source_arr!(i&) + value! + CASE "-": + dest_arr!(i&) = source_arr!(i&) - value! + CASE "*": + dest_arr!(i&) = source_arr!(i&) * value! + CASE "\": + IF value! > 0 THEN + dest_arr!(i&) = source_arr!(i&) \ value! + END IF + CASE "&&": + dest_arr!(i&) = source_arr!(i&) AND value! + CASE "||": + dest_arr!(i&) = source_arr!(i&) OR value! + CASE "!!": + dest_arr!(i&) = source_arr!(i&) XOR value! + CASE "<<": + dest_arr!(i&) = _SHL(source_arr!(i&), value!) + CASE ">>": + dest_arr!(i&) = _SHR(source_arr!(i&), value!) + END SELECT + NEXT i& +END SUB + + +'' +' Return the minimum element value in SINGLE array +' +' @param SINGLE ARRAY arr!() to check in +' @return SINGLE minimum value found +' +FUNCTION ARR_SNG.min!(arr!()) + DIM AS LONG lb, ub, i + DIM AS SINGLE s + lb& = LBOUND(arr!) : ub& = UBOUND(arr!) + s! = 127 + FOR i& = lb& TO ub& + IF arr!(i&) < s! THEN + s! = arr!(i&) + END IF + NEXT i& + ARR_SNG.min! = s! +END FUNCTION + + +'' +' Return the maximum element value in SINGLE array +' +' @param SINGLE ARRAY arr!() to check in +' @return SINGLE maximum value found +' +FUNCTION ARR_SNG.max!(arr!()) + DIM AS LONG lb, ub, i + DIM AS SINGLE s + lb& = LBOUND(arr!) : ub& = UBOUND(arr!) + s! = 0 + FOR i& = lb& TO ub& + IF arr!(i&) > s! THEN + s! = arr!(i&) + END IF + NEXT i& + ARR_SNG.max! = s! +END FUNCTION + + +'' +' Return the visually shortest element of a SINGLE array +' +' @param SINGLE ARRAY arr!() to check in +' @return SINGLE value of visually shortest element +' +FUNCTION ARR_SNG.shortest!(arr!()) + DIM AS LONG lb, ub, i, res, sw + lb& = LBOUND(arr!) : ub& = UBOUND(arr!) + res& = 0 : sw& = 0 + FOR i& = lb& TO ub& + IF LEN(_TRIM$(STR$(arr!(i&)))) < sw& THEN + sw& = LEN(_TRIM$(STR$(arr!(i&)))) + res& = i& + END IF + NEXT i& + ARR_SNG.shortest! = arr!(res&) +END FUNCTION + + +'' +' Return the first element of a SINGLE array +' +' @param SINGLE ARRAY arr!() to check in +' @return SINGLE value of first element +' +FUNCTION ARR_SNG.first!(arr!()) + ARR_SNG.first! = arr!(LBOUND(arr!)) +END FUNCTION + + +'' +' Return the last element of a SINGLE array +' +' @param SINGLE ARRAY arr!() to check in +' @return SINGLE value of last element +' +FUNCTION ARR_SNG.last!(arr!()) + ARR_SNG.last! = arr!(UBOUND(arr!)) +END FUNCTION + + +'' +' Return every nth array element of a SINGLE array +' +' @param SINGLE ARRAY source_arr!() to get from +' @param SINGLE ARRAY dest_arr!() to store in +' @param INTEGER nth% element +' +SUB ARR_SNG.nth(source_arr!(), dest_arr!(), nth%) + DIM AS LONG lb, ub, i, n + lb& = LBOUND(source_arr!) : ub& = UBOUND(source_arr!) + n& = (ub& - lb&) \ nth% + REDIM dest_arr(n&) AS SINGLE + n& = 0 + FOR i& = lb& to ub& + IF i& MOD nth% = 0 THEN + dest_arr!(n&) = source_arr!(i&) + n& = n& + 1 + END IF + NEXT i& +END SUB + + +'' +' Checks if value exists in SINGLE array +' +' @param SINGLE ARRAY arr!() to check in +' @param SINGLE value! value to check for +' @return INTEGER -1 if found or 0 if not found +' +FUNCTION ARR_SNG.in%(arr!(), value!) + DIM AS LONG lb, ub, i + lb& = LBOUND(arr!) : ub& = UBOUND(arr!) + FOR i& = lb& TO ub& + IF arr!(i&) = value! THEN + ARR_SNG.in% = -1 + EXIT FUNCTION + END IF + NEXT i& + ARR_SNG.in% = 0 +END FUNCTION + + +'' +' Checks if value exists in SINGLE array and returns index if found +' +' @param SINGLE ARRAY arr!() to check in +' @param SINGLE value! value to check for +' @return INTEGER index of element if found or -1 if not found +' +FUNCTION ARR_SNG.find%(arr!(), value!) + DIM AS LONG lb, ub, i + lb& = LBOUND(arr!) : ub& = UBOUND(arr!) + FOR i& = lb& TO ub& + IF arr!(i&) = value! THEN + ARR_SNG.find% = i& + EXIT FUNCTION + END IF + NEXT i& + ARR_SNG.find% = -1 +END FUNCTION + + +'' +' Return the number of elements in a SINGLE array +' +' @param SINGLE ARRAY arr!() to count +' @return INTEGER number of elements +' +FUNCTION ARR_SNG.count&(arr!()) + ARR_SNG.count& = UBOUND(arr!) - LBOUND(arr!) +END FUNCTION + + +'' +' Return the size of a SINGLE array +' +' @param SINGLE ARRAY arr!() to get size of +' @return LONG size in bytes +' +FUNCTION ARR_SNG.size&(arr!()) + ARR_SNG.size& = LEN(arr!()) +END FUNCTION + + +'' +' Reverses the elements of a SINGLE array +' +' @param SINGLE ARRAY source_arr!() to reverse +' @param SINGLE ARRAY dest_arr!() to store reversed array in +' +SUB ARR_SNG.reverse(source_arr!(), dest_arr!()) + DIM AS LONG lb, ub, i, n + lb& = LBOUND(source_arr!) : ub& = UBOUND(source_arr!) + REDIM dest_arr(0 TO (ub& - lb&)) AS SINGLE + n& = 0 + FOR i& = ub& TO lb& STEP -1 + dest_arr!(n&) = source_arr!(i&) + n& = n& + 1 + NEXT i& +END SUB + + +'' +' Returns a random sng from a SINGLE array +' +' @param SINGLE ARRAY arr!() array to get random element from +' @return SINGLE random element +' +FUNCTION ARR_SNG.random!(arr!()) + DIM AS LONG lb, ub + lb& = LBOUND(arr!) : ub& = UBOUND(arr!) + RANDOMIZE TIMER + ARR_SNG.random! = arr!(INT(RND * (ub& - lb&)) + 1) +END FUNCTION + + +'' +' Returns the sum of all elements in a SINGLE array +' +' @param SINGLE ARRAY arr!() array to get some for +' @return LONG sum of all elements +' +FUNCTION ARR_SNG.sum&(arr!()) + DIM AS LONG lb, ub, i + DIM sum AS LONG + lb& = LBOUND(arr!) : ub& = UBOUND(arr!) + FOR i& = lb& TO ub& + sum& = sum& + arr!(i&) + NEXT i& + ARR_SNG.sum& = sum& +END FUNCTION + + +'' +' Returns the average value of elements in a SINGLE array +' +' @param SINGLE ARRAY arr!() array to get average for +' @return LONG average of elements +' +FUNCTION ARR_SNG.avg&(arr!()) + DIM AS LONG lb, ub, i + DIM sum AS LONG + lb& = LBOUND(arr!) : ub& = UBOUND(arr!) + FOR i& = lb& TO ub& + sum& = sum& + arr!(i&) + NEXT i& + ARR_SNG.avg& = sum& / (ub& - lb&) +END FUNCTION + + +'' +' Shuffle the elements of a SINGLE array +' +' @param SINGLE ARRAY source_arr!() to shuffle +' @param SINGLE ARRAY dest_arr!() to store shuffled array in +' +SUB ARR_SNG.shuffle(source_arr!(), dest_arr!()) + DIM AS LONG lb, ub, i, count + lb& = LBOUND(source_arr!) : ub& = UBOUND(source_arr!) + count& = ub& - lb& + REDIM dest_arr(0 TO count&) AS SINGLE + CALL ARR_SNG.copy(source_arr!(), dest_arr!()) + RANDOMIZE TIMER + FOR i& = 0 TO count& + SWAP dest_arr!(i&), dest_arr!(lb& + RND * (ub& - lb&)) + NEXT i& +END SUB + + +'' +' Makes a SINGLE array contain only unique values +' +' @param SINGLE ARRAY source_arr!() array to get uniques for +' @param SINGLE ARRAY dest_arr!() array to store uniques in +' +SUB ARR_SNG.unique(source_arr!(), dest_arr!()) + DIM AS LONG lb, ub, i + DIM tmp AS SINGLE + lb& = LBOUND(source_arr!) : ub& = UBOUND(source_arr!) + DIM work_arr(0) AS SINGLE + FOR i& = lb& TO ub& + IF NOT ARR_SNG.in%(work_arr!(), source_arr!(i&)) THEN + CALL ARR_SNG.push(work_arr!(), source_arr!(i&)) + END IF + NEXT i& + CALL ARR_SNG.shift(work_arr!(), tmp!) + CALL ARR_SNG.copy(work_arr!(), dest_arr!()) +END SUB + + +'' +' Filters a SINGLE array to only elements greater than value +' +' @param SINGLE ARRAY source_arr!() array to work on +' @param SINGLE ARRAY dest_arr!() array to store in +' @param SINGLE value! to be greater than to be returned +' +SUB ARR_SNG.gt(source_arr!(), dest_arr!(), value!) + DIM AS LONG lb, ub, i + DIM tmp AS SINGLE + lb& = LBOUND(source_arr!) : ub& = UBOUND(source_arr!) + DIM work_arr(0) AS SINGLE + FOR i& = lb& TO ub& + IF source_arr!(i&) > value! THEN + CALL ARR_SNG.push(work_arr!(), source_arr!(i&)) + END IF + NEXT i& + CALL ARR_SNG.shift(work_arr!(), tmp!) + CALL ARR_SNG.copy(work_arr!(), dest_arr!()) +END SUB + + +'' +' Filters a SINGLE array to only elements greater than or equal to value +' +' @param SINGLE ARRAY source_arr!() array to work on +' @param SINGLE ARRAY dest_arr!() array to store in +' @param SINGLE value! to be greater than or equal to be returned +' +SUB ARR_SNG.gte(source_arr!(), dest_arr!(), value!) + DIM AS LONG lb, ub, i + DIM tmp AS SINGLE + lb& = LBOUND(source_arr!) : ub& = UBOUND(source_arr!) + DIM work_arr(0) AS SINGLE + FOR i& = lb& TO ub& + IF source_arr!(i&) >= value! THEN + CALL ARR_SNG.push(work_arr!(), source_arr!(i&)) + END IF + NEXT i& + CALL ARR_SNG.shift(work_arr!(), tmp!) + CALL ARR_SNG.copy(work_arr!(), dest_arr!()) +END SUB + + +'' +' Filters a SINGLE array to only elements less than value +' +' @param SINGLE ARRAY source_arr!() array to work on +' @param SINGLE ARRAY dest_arr!() array to store in +' @param SINGLE value! to be less than to be returned +' +SUB ARR_SNG.lt(source_arr!(), dest_arr!(), value!) + DIM AS LONG lb, ub, i + DIM tmp AS SINGLE + lb& = LBOUND(source_arr!) : ub& = UBOUND(source_arr!) + DIM work_arr(0) AS SINGLE + FOR i& = lb& TO ub& + IF source_arr!(i&) < value! THEN + CALL ARR_SNG.push(work_arr!(), source_arr!(i&)) + END IF + NEXT i& + CALL ARR_SNG.shift(work_arr!(), tmp!) + CALL ARR_SNG.copy(work_arr!(), dest_arr!()) +END SUB + + +'' +' Filters a SINGLE array to only elements less than or equal to value +' +' @param SINGLE ARRAY source_arr!() array to work on +' @param SINGLE ARRAY dest_arr!() array to store in +' @param SINGLE value! to be less than or equal to be returned +' +SUB ARR_SNG.lte(source_arr!(), dest_arr!(), value!) + DIM AS LONG lb, ub, i + DIM tmp AS SINGLE + lb& = LBOUND(source_arr!) : ub& = UBOUND(source_arr!) + DIM work_arr(0) AS SINGLE + FOR i& = lb& TO ub& + IF source_arr!(i&) <= value! THEN + CALL ARR_SNG.push(work_arr!(), source_arr!(i&)) + END IF + NEXT i& + CALL ARR_SNG.shift(work_arr!(), tmp!) + CALL ARR_SNG.copy(work_arr!(), dest_arr!()) +END SUB + + +'' +' Finds and replaces values across all elements in a SINGLE ARRAY +' +' @param SINGLE ARRAY arr!() to check in +' @param SINGLE find! value to find +' @param SINGLE replace! value to replace with if found +' +SUB ARR_SNG.replace(arr!(), find!, replace!) + DIM AS LONG lb, ub, i + lb& = LBOUND(arr!) : ub& = UBOUND(arr!) + FOR i& = lb& TO ub& + IF arr!(i&) = find! THEN + arr!(i&) = replace! + END IF + NEXT i& +END SUB + + +'' +' Inserts a new element into SINGLE array after index +' +' @param SINGLE ARRAY arr!() array to work on +' @param SINGLE value! to insert +' @param INTEGER index% of element to insert at +' +SUB ARR_SNG.insert(arr!(), value!, index%) + DIM AS LONG lb, ub, i + DIM tmp AS SINGLE + DIM work_arr(0) AS SINGLE + lb& = LBOUND(arr!) : ub& = UBOUND(arr!) + IF index% >= lb& AND index% <= ub& THEN + ' build new array up to index + FOR i& = lb& TO index% + CALL ARR_SNG.push(work_arr!(), arr!(i&)) + NEXT i& + ' insert new element + CALL ARR_SNG.push(work_arr!(), value!) + ' finish building array from index + 1 + FOR i& = index% + 1 TO ub& + CALL ARR_SNG.push(work_arr!(), arr!(i&)) + NEXT i& + CALL ARR_SNG.shift(work_arr!(), tmp!) + CALL ARR_SNG.copy(work_arr!(), arr!()) + END IF +END SUB + + +'' +' Removes element from a SINGLE array by element index +' +' @param SINGLE ARRAY arr!() array to work on +' @param INTEGER index% of element to remove +' +SUB ARR_SNG.remove(arr!(), index%) + DIM AS LONG lb, ub, i + DIM tmp AS SINGLE + DIM work_arr(0) AS SINGLE + lb& = LBOUND(arr!) : ub& = UBOUND(arr!) + IF index% >= lb& AND index% <= ub& THEN + ' build new array up to index + FOR i& = lb& TO index% - 1 + CALL ARR_SNG.push(work_arr!(), arr!(i&)) + NEXT i& + ' skip elements + FOR i& = index% + 1 TO ub& + CALL ARR_SNG.push(work_arr!(), arr!(i&)) + NEXT i& + CALL ARR_SNG.shift(work_arr!(), tmp!) + CALL ARR_SNG.copy(work_arr!(), arr!()) + END IF +END SUB + + +'' +' Filters a SINGLE array to only elements that have odd values +' +' @param SINGLE ARRAY source_arr!() array to work on +' @param SINGLE ARRAY dest_arr!() array to store in +' +SUB ARR_SNG.odd(source_arr!(), dest_arr!()) + DIM AS LONG lb, ub, i + DIM tmp AS SINGLE + lb& = LBOUND(source_arr!) : ub& = UBOUND(source_arr!) + DIM work_arr(0) AS SINGLE + FOR i& = lb& TO ub& + IF source_arr!(i&) MOD 2 <> 0 THEN + CALL ARR_SNG.push(work_arr!(), source_arr!(i&)) + END IF + NEXT i& + CALL ARR_SNG.shift(work_arr!(), tmp!) + CALL ARR_SNG.copy(work_arr!(), dest_arr!()) +END SUB + + +'' +' Filters a SINGLE array to only elements that have even values +' +' @param SINGLE ARRAY source_arr!() array to work on +' @param SINGLE ARRAY dest_arr!() array to store in +' +SUB ARR_SNG.even(source_arr!(), dest_arr!()) + DIM AS LONG lb, ub, i + DIM tmp AS SINGLE + lb& = LBOUND(source_arr!) : ub& = UBOUND(source_arr!) + DIM work_arr(0) AS SINGLE + FOR i& = lb& TO ub& + IF source_arr!(i&) MOD 2 = 0 THEN + CALL ARR_SNG.push(work_arr!(), source_arr!(i&)) + END IF + NEXT i& + CALL ARR_SNG.shift(work_arr!(), tmp!) + CALL ARR_SNG.copy(work_arr!(), dest_arr!()) +END SUB + + +'' +' Filters a SINGLE array to only elements that have values evenly divisible by divisor +' +' @param SINGLE ARRAY source_arr!() array to work on +' @param SINGLE ARRAY dest_arr!() array to store in +' @param SINGLE divisor! for modulo +' +SUB ARR_SNG.mod(source_arr!(), dest_arr!(), divisor!) + DIM AS LONG lb, ub, i + DIM tmp AS SINGLE + lb& = LBOUND(source_arr!) : ub& = UBOUND(source_arr!) + DIM work_arr(0) AS SINGLE + FOR i& = lb& TO ub& + IF source_arr!(i&) MOD divisor! = 0 THEN + CALL ARR_SNG.push(work_arr!(), source_arr!(i&)) + END IF + NEXT i& + CALL ARR_SNG.shift(work_arr!(), tmp!) + CALL ARR_SNG.copy(work_arr!(), dest_arr!()) +END SUB + + +'' +' Filters a SINGLE array to only elements between min and max +' +' @param SINGLE ARRAY source_arr!() array to work on +' @param SINGLE ARRAY dest_arr!() array to store in +' @param SINGLE min! to be greater than or equal to be returned +' @param SINGLE max! to be less than or equal to be returned +' +SUB ARR_SNG.between(source_arr!(), dest_arr!(), min!, max!) + DIM AS LONG lb, ub, i + DIM tmp AS SINGLE + lb& = LBOUND(source_arr!) : ub& = UBOUND(source_arr!) + DIM work_arr(0) AS SINGLE + FOR i& = lb& TO ub& + IF source_arr!(i&) >= min! _ + AND source_arr!(i&) <= max! THEN + CALL ARR_SNG.push(work_arr!(), source_arr!(i&)) + END IF + NEXT i& + CALL ARR_SNG.shift(work_arr!(), tmp!) + CALL ARR_SNG.copy(work_arr!(), dest_arr!()) +END SUB + + +'' +' Sorts SINGLE array in ascending order +' +' @param SINGLE ARRAY source_arr!() array to sort +' @param SINGLE ARRAY dest_arr!() array to store sorted in +' +SUB ARR_SNG.sort(source_arr!(), dest_arr!()) + DIM AS LONG lb, ub, count + lb& = LBOUND(source_arr!) : ub& = UBOUND(source_arr!) + count& = ub& - lb& + REDIM dest_arr(0 TO count&) AS SINGLE + CALL ARR_SNG.copy(source_arr!(), dest_arr!()) + CALL ARR_SNG.quicksort(dest_arr!(), lb&, ub&, 0) +END SUB + + +'' +' Sorts SINGLE array in descending order +' +' @param SINGLE ARRAY source_arr!() array to sort +' @param SINGLE ARRAY dest_arr!() array to store sorted in +' +SUB ARR_SNG.rsort(source_arr!(), dest_arr!()) + DIM AS LONG lb, ub, count + lb& = LBOUND(source_arr!) : ub& = UBOUND(source_arr!) + count& = ub& - lb& + REDIM dest_arr(0 TO count&) AS SINGLE + CALL ARR_SNG.copy(source_arr!(), dest_arr!()) + CALL ARR_SNG.quicksort(dest_arr!(), lb&, ub&, 1) +END SUB + + +'' +' Quicksort array with pivot algorithm by logiclrd +' +' @link https://www.tek-tips.com/faqs.cfm?fid=336 +' @param SINGLE ARRAY array!() to sort +' @param INTEGER start% of range to sort +' @param INTEGER finish% range of sort +' @param INTEGER order% to sort by (0 = asc / 1 = desc) +' +SUB ARR_SNG.quicksort(arr!(), start%, finish%, order%) + DIM i AS LONG + DIM pivot AS INTEGER + DIM pivotvalue AS SINGLE + + 'first, partition the array + pivot% = start% + pivotvalue! = arr!(pivot%) + FOR i& = start% + 1 TO finish% + IF order% = 0 THEN ' ascending order + IF arr!(i&) < pivotvalue! THEN + arr!(pivot%) = arr!(i&) + arr!(i&) = arr!(pivot% + 1) + arr!(pivot% + 1) = pivotvalue! + pivot% = pivot% + 1 + END IF + ELSEIF order% = 1 THEN ' descending order + IF arr!(i&) > pivotvalue! THEN + arr!(pivot%) = arr!(i&) + arr!(i&) = arr!(pivot% + 1) + arr!(pivot% + 1) = pivotvalue! + pivot% = pivot% + 1 + END IF + END IF + NEXT i& + + 'then, sort the subarrays to each side of the pivot + IF pivot% - start% >= 2 THEN + CALL ARR_SNG.quicksort(arr!(), start%, pivot% - 1, order%) + END IF + IF finish% - pivot% >= 2 THEN + CALL ARR_SNG.quicksort(arr!(), pivot% + 1, finish%, order%) + END IF +END SUB diff --git a/ARR/ARR_STR.BAS b/ARR/ARR_STR.BAS new file mode 100644 index 0000000..60f406a --- /dev/null +++ b/ARR/ARR_STR.BAS @@ -0,0 +1,626 @@ +'$DYNAMIC +$LET GJ_LIB_ARR_STR_BAS = 1 + +'' +' Slice an array from source to destination starting at index and count slices +' +' @param STRING source_arr$() to slice from +' @param STRING dest_arr$() to put slices into +' @param INTEGER start_idx% starting index to use as slice range +' @param INTEGER count% number of slices - if negative, backwards from index +' +SUB ARR_STR.slice(source_arr$(), dest_arr$(), start_idx%, count%) + DIM AS LONG ub, lb, i, n + lb& = LBOUND(source_arr$) : ub& = UBOUND(source_arr$) + IF start_idx% < lb& OR start_idx% + count% > ub& THEN EXIT SUB ' out of range + IF ub& - lb& < count% THEN EXIT SUB ' too many and not enough + REDIM dest_arr(0 TO ABS(count%)) AS STRING + IF SGN(count%) = -1 THEN + IF ((start_idx% - 1) - ABS(count%)) < 0 THEN EXIT SUB ' out of range + n& = 0 + FOR i& = (start_idx% - 1) TO ((start_idx% - 1) - ABS(count%)) STEP -1 + dest_arr$(n&) = source_arr$(i&) + n& = n& + 1 + NEXT i& + ELSE + IF ((start_idx% + 1) + ABS(count%)) > (ub& - lb&) THEN EXIT SUB ' out of range + n& = 0 + FOR i& = start_idx% + 1 TO ((start_idx% + 1) + count%) STEP 1 + dest_arr$(n&) = source_arr$(i&) + n& = n& + 1 + NEXT i& + END IF +END SUB + + +'' +' Push a str onto the end of a STRING array +' +' @param STRING arr$() array to push into +' @param STRING value$ of byte to push +' +SUB ARR_STR.push(arr$(), value$) + DIM AS LONG ub, lb + lb& = LBOUND(arr$) : ub& = UBOUND(arr$) + REDIM _PRESERVE arr(lb& TO (ub& + 1)) AS STRING + arr$(ub& + 1) = value$ +END SUB + + +'' +' Pop a str from the end of a STRING array +' +' @param STRING arr$() array to pop from +' @param STRING var$ of str to store popped str +' +SUB ARR_STR.pop(arr$(), var$) + DIM AS LONG ub, lb + lb& = LBOUND(arr$) : ub& = UBOUND(arr$) + var$ = arr$(ub&) + REDIM _PRESERVE arr(lb& TO (ub& - 1)) AS STRING +END SUB + + +'' +' Pop a str from the beginning of a STRING array +' +' @param STRING arr$() array to pop from +' @param STRING var$ of str to store popped str +' +SUB ARR_STR.shift(arr$(), var$) + DIM AS LONG ub, lb, i + lb& = LBOUND(arr$) : ub& = UBOUND(arr$) + var$ = arr$(lb&) + FOR i& = lb& TO ub& - 1 + arr$(i&) = arr$(i& + 1) + NEXT i& + REDIM _PRESERVE arr(lb& + 1 TO ub&) AS STRING +END SUB + + +'' +' Copy an array of STRs to another STRING array +' +' @param STRING ARRAY source_arr$() source array to copy +' @param STRING ARRAY dest_arr$() dest array to copy into +' +SUB ARR_STR.copy(source_arr$(), dest_arr$()) + DIM AS LONG ub, lb, i + lb& = LBOUND(source_arr$) : ub& = UBOUND(source_arr$) + REDIM dest_arr(lb& TO ub&) AS STRING + FOR i& = lb& TO ub& + dest_arr$(i&) = source_arr$(i&) + NEXT i& +END SUB + + +'' +' Push a str into the beginning of a STRING array +' +' @param STRING arr$() array to push into +' @param STRING value$ of str to push +' +SUB ARR_STR.unshift(arr$(), value$) + DIM AS LONG ub, lb, i + lb& = LBOUND(arr$) : ub& = UBOUND(arr$) + DIM work(lb& TO ub&) AS STRING + CALL ARR_STR.copy(arr$(), work$()) + REDIM _PRESERVE arr(lb& TO (ub& + 1)) AS STRING + FOR i& = lb& + 1 TO ub& + 1 + arr$(i&) = work$(i& - 1) + NEXT i& + arr$(lb&) = value$ +END SUB + + +'' +' Joins an array of STRs as a string +' +' @param STRING ARRAY arr$() to get as a string +' @param STRING s$ to store stringified array in +' +SUB ARR_STR.join(arr$(), s$) + DIM AS LONG ub, lb, i + s$ = "" + lb& = LBOUND(arr$) : ub& = UBOUND(arr$) + FOR i& = lb& TO ub& + s$ = s$ + _TRIM$(arr$(i&)) + ", " + NEXT i& + ' remove trailing comma + s$ = MID$(s$, 1, (LEN(s$)-LEN(", "))) +END SUB + + +'' +' Create a new STRING array using string of strs seperated by commas +' +' @param STRING ARRAY arr$() to store the strs in +' @param STRING s$ string of comma separated strs +' +SUB ARR_STR.new(arr$(), s$) + DIM AS LONG i, count + DIM t AS STRING + count& = 0 + FOR i& = 1 TO LEN(s$) + IF ASC(s$, i&) = ASC(",") THEN count& = count& + 1 + NEXT i& + REDIM arr(0 TO count&) AS STRING + IF count& = 0 THEN + arr$(0) = s$ + EXIT SUB + END IF + count& = 0 + FOR i& = 1 TO LEN(s$) + t$ = t$ + CHR$(ASC(s$, i&)) + IF ASC(s$, i&) = ASC(",") THEN + IF i& <> LEN(s$) THEN + arr$(count&) = LEFT$(t$, LEN(t$) - 1) + END IF + count& = count& + 1 + t$ = "" + END IF + arr$(count&) = t$ + NEXT i& +END SUB + + +'' +' Return the visually longest element of a STRING array +' +' @param STRING ARRAY arr$() to check in +' @return STRING value of visually longest element +' +FUNCTION ARR_STR.longest$(arr$()) + DIM AS LONG lb, ub, i, res, lw + lb& = LBOUND(arr$) : ub& = UBOUND(arr$) + res& = 0 : lw& = 0 + FOR i& = lb& TO ub& + IF LEN(_TRIM$(arr$(i&))) > lw& THEN + lw& = LEN(_TRIM$(arr$(i&))) + res& = i& + END IF + NEXT i& + ARR_STR.longest$ = arr$(res&) +END FUNCTION + + +'' +' Return the visually shortest element of a STRING array +' +' @param STRING ARRAY arr$() to check in +' @return STRING value of visually shortest element +' +FUNCTION ARR_STR.shortest$(arr$()) + DIM AS LONG lb, ub, i, res, sw + lb& = LBOUND(arr$) : ub& = UBOUND(arr$) + res& = 0 : sw& = 255 + FOR i& = lb& TO ub& + IF LEN(_TRIM$(arr$(i&))) < sw& THEN + sw& = LEN(_TRIM$(arr$(i&))) + res& = i& + END IF + NEXT i& + ARR_STR.shortest$ = arr$(res&) +END FUNCTION + + +'' +' Return the first element of a STRING array +' +' @param STRING ARRAY arr$() to check in +' @return STRING value of first element +' +FUNCTION ARR_STR.first$(arr$()) + ARR_STR.first$ = arr$(LBOUND(arr$)) +END FUNCTION + + +'' +' Return the last element of a STRING array +' +' @param STRING ARRAY arr$() to check in +' @return STRING value of last element +' +FUNCTION ARR_STR.last$(arr$()) + ARR_STR.last$ = arr$(UBOUND(arr$)) +END FUNCTION + + +'' +' Return every nth array element of a STRING array +' +' @param STRING ARRAY source_arr$() to get from +' @param STRING ARRAY dest_arr$() to store in +' @param INTEGER nth% element +' +SUB ARR_STR.nth(source_arr$(), dest_arr$(), nth%) + DIM AS LONG lb, ub, i, n + lb& = LBOUND(source_arr$) : ub& = UBOUND(source_arr$) + n& = (ub& - lb&) \ nth% + REDIM dest_arr(n&) AS STRING + n& = 0 + FOR i& = lb& to ub& + IF i& MOD nth% = 0 THEN + dest_arr$(n&) = source_arr$(i&) + n& = n& + 1 + END IF + NEXT i& +END SUB + + +'' +' Checks if value exists in STRING array +' +' @param STRING ARRAY arr$() to check in +' @param STRING value$ value to check for +' @return INTEGER -1 if found or 0 if not found +' +FUNCTION ARR_STR.in%(arr$(), value$) + DIM AS LONG lb, ub, i + lb& = LBOUND(arr$) : ub& = UBOUND(arr$) + FOR i& = lb& TO ub& + IF arr$(i&) = value$ THEN + ARR_STR.in% = -1 + EXIT FUNCTION + END IF + NEXT i& + ARR_STR.in% = 0 +END FUNCTION + + +'' +' Checks if value exists in STRING array and returns index if found +' +' @param STRING ARRAY arr$() to check in +' @param STRING value$ value to check for +' @return INTEGER index of element if found or -1 if not found +' +FUNCTION ARR_STR.find%(arr$(), value$) + DIM AS LONG lb, ub, i + lb& = LBOUND(arr$) : ub& = UBOUND(arr$) + FOR i& = lb& TO ub& + IF arr$(i&) = value$ THEN + ARR_STR.find% = i& + EXIT FUNCTION + END IF + NEXT i& + ARR_STR.find% = -1 +END FUNCTION + + +'' +' Return the number of elements in a STRING array +' +' @param STRING ARRAY arr$() to count +' @return INTEGER number of elements +' +FUNCTION ARR_STR.count&(arr$()) + ARR_STR.count& = UBOUND(arr$) - LBOUND(arr$) +END FUNCTION + + +'' +' Return the size of a STRING array +' +' @param STRING ARRAY arr$() to get size of +' @return LONG size in bytes +' +FUNCTION ARR_STR.size&(arr$()) + DIM AS LONG lb, ub, i, n + lb& = LBOUND(arr$) : ub& = UBOUND(arr$) + n& = 0 + FOR i& = lb& TO ub& + n& = n& + LEN(arr$(i&)) + NEXT i& + ARR_STR.size& = n& +END FUNCTION + + +'' +' Reverses the elements of a STRING array +' +' @param STRING ARRAY source_arr$() to reverse +' @param STRING ARRAY dest_arr$() to store reversed array in +' +SUB ARR_STR.reverse(source_arr$(), dest_arr$()) + DIM AS LONG lb, ub, i, n + lb& = LBOUND(source_arr$) : ub& = UBOUND(source_arr$) + REDIM dest_arr(0 TO (ub& - lb&)) AS STRING + n& = 0 + FOR i& = ub& TO lb& STEP -1 + dest_arr$(n&) = source_arr$(i&) + n& = n& + 1 + NEXT i& +END SUB + + +'' +' Returns a random str from a STRING array +' +' @param STRING ARRAY arr$() array to get random element from +' @return STRING random element +' +FUNCTION ARR_STR.random$(arr$()) + DIM AS LONG lb, ub + lb& = LBOUND(arr$) : ub& = UBOUND(arr$) + RANDOMIZE TIMER + ARR_STR.random$ = arr$(INT(RND * (ub& - lb&)) + 1) +END FUNCTION + + +'' +' Shuffle the elements of a STRING array +' +' @param STRING ARRAY source_arr$() to shuffle +' @param STRING ARRAY dest_arr$() to store shuffled array in +' +SUB ARR_STR.shuffle(source_arr$(), dest_arr$()) + DIM AS LONG lb, ub, i, count + lb& = LBOUND(source_arr$) : ub& = UBOUND(source_arr$) + count& = ub& - lb& + REDIM dest_arr(0 TO count&) AS STRING + CALL ARR_STR.copy(source_arr$(), dest_arr$()) + RANDOMIZE TIMER + FOR i& = 0 TO count& + SWAP dest_arr$(i&), dest_arr$(lb& + RND * (ub& - lb&)) + NEXT i& +END SUB + + +'' +' Makes a STRING array contain only unique values +' +' @param STRING ARRAY source_arr$() array to get uniques for +' @param STRING ARRAY dest_arr$() array to store uniques in +' +SUB ARR_STR.unique(source_arr$(), dest_arr$()) + DIM AS LONG lb, ub, i + DIM tmp AS STRING + lb& = LBOUND(source_arr$) : ub& = UBOUND(source_arr$) + DIM work_arr(0) AS STRING + FOR i& = lb& TO ub& + IF NOT ARR_STR.in%(work_arr$(), source_arr$(i&)) THEN + CALL ARR_STR.push(work_arr$(), source_arr$(i&)) + END IF + NEXT i& + CALL ARR_STR.shift(work_arr$(), tmp$) + CALL ARR_STR.copy(work_arr$(), dest_arr$()) +END SUB + + +'' +' Finds and replaces values across all elements in a STRING ARRAY +' +' @param STRING ARRAY arr$() to check in +' @param STRING find$ value to find +' @param STRING replace$ value to replace with if found +' +SUB ARR_STR.replace(arr$(), find$, replace$) + DIM AS LONG lb, ub, i + lb& = LBOUND(arr$) : ub& = UBOUND(arr$) + FOR i& = lb& TO ub& + IF arr$(i&) = find$ THEN + arr$(i&) = replace$ + END IF + NEXT i& +END SUB + + +'' +' Inserts a new element into STRING array after index +' +' @param STRING ARRAY arr$() array to work on +' @param STRING value$ to insert +' @param INTEGER index% of element to insert at +' +SUB ARR_STR.insert(arr$(), value$, index%) + DIM AS LONG lb, ub, i + DIM tmp AS STRING + DIM work_arr(0) AS STRING + lb& = LBOUND(arr$) : ub& = UBOUND(arr$) + IF index% >= lb& AND index% <= ub& THEN + ' build new array up to index + FOR i& = lb& TO index% + CALL ARR_STR.push(work_arr$(), arr$(i&)) + NEXT i& + ' insert new element + CALL ARR_STR.push(work_arr$(), value$) + ' finish building array from index + 1 + FOR i& = index% + 1 TO ub& + CALL ARR_STR.push(work_arr$(), arr$(i&)) + NEXT i& + CALL ARR_STR.shift(work_arr$(), tmp$) + CALL ARR_STR.copy(work_arr$(), arr$()) + END IF +END SUB + + +'' +' Removes element from a STRING array by element index +' +' @param STRING ARRAY arr$() array to work on +' @param INTEGER index% of element to remove +' +SUB ARR_STR.remove(arr$(), index%) + DIM AS LONG lb, ub, i + DIM tmp AS STRING + DIM work_arr(0) AS STRING + lb& = LBOUND(arr$) : ub& = UBOUND(arr$) + IF index% >= lb& AND index% <= ub& THEN + ' build new array up to index + FOR i& = lb& TO index% - 1 + CALL ARR_STR.push(work_arr$(), arr$(i&)) + NEXT i& + ' skip elements + FOR i& = index% + 1 TO ub& + CALL ARR_STR.push(work_arr$(), arr$(i&)) + NEXT i& + CALL ARR_STR.shift(work_arr$(), tmp$) + CALL ARR_STR.copy(work_arr$(), arr$()) + END IF +END SUB + + +'' +' Filters a STRING array to only elements that have odd values +' +' @param STRING ARRAY source_arr$() array to work on +' @param STRING ARRAY dest_arr$() array to store in +' +SUB ARR_STR.odd(source_arr$(), dest_arr$()) + DIM AS LONG lb, ub, i + DIM tmp AS STRING + lb& = LBOUND(source_arr$) : ub& = UBOUND(source_arr$) + DIM work_arr(0) AS STRING + FOR i& = lb& TO ub& + IF i& MOD 2 <> 0 THEN + CALL ARR_STR.push(work_arr$(), source_arr$(i&)) + END IF + NEXT i& + CALL ARR_STR.shift(work_arr$(), tmp$) + CALL ARR_STR.copy(work_arr$(), dest_arr$()) +END SUB + + +'' +' Filters a STRING array to only elements that have even values +' +' @param STRING ARRAY source_arr$() array to work on +' @param STRING ARRAY dest_arr$() array to store in +' +SUB ARR_STR.even(source_arr$(), dest_arr$()) + DIM AS LONG lb, ub, i + DIM tmp AS STRING + lb& = LBOUND(source_arr$) : ub& = UBOUND(source_arr$) + DIM work_arr(0) AS STRING + FOR i& = lb& TO ub& + IF i& MOD 2 = 0 THEN + CALL ARR_STR.push(work_arr$(), source_arr$(i&)) + END IF + NEXT i& + CALL ARR_STR.shift(work_arr$(), tmp$) + CALL ARR_STR.copy(work_arr$(), dest_arr$()) +END SUB + + +'' +' Filters a STRING array to only elements that have values evenly divisible by divisor +' +' @param STRING ARRAY source_arr$() array to work on +' @param STRING ARRAY dest_arr$() array to store in +' @param INTEGER divisor% for modulo +' +SUB ARR_STR.mod(source_arr$(), dest_arr$(), divisor%) + DIM AS LONG lb, ub, i + DIM tmp AS STRING + lb& = LBOUND(source_arr$) : ub& = UBOUND(source_arr$) + DIM work_arr(0) AS STRING + FOR i& = lb& TO ub& + IF i& MOD divisor% = 0 THEN + CALL ARR_STR.push(work_arr$(), source_arr$(i&)) + END IF + NEXT i& + CALL ARR_STR.shift(work_arr$(), tmp$) + CALL ARR_STR.copy(work_arr$(), dest_arr$()) +END SUB + + +'' +' Filters a STRING array to only elements between min and max +' +' @param STRING ARRAY source_arr$() array to work on +' @param STRING ARRAY dest_arr$() array to store in +' @param INTEGER min% to be greater than or equal to be returned +' @param INTEGER max% to be less than or equal to be returned +' +SUB ARR_STR.between(source_arr$(), dest_arr$(), min%, max%) + DIM AS LONG lb, ub, i + DIM tmp AS STRING + lb& = LBOUND(source_arr$) : ub& = UBOUND(source_arr$) + DIM work_arr(0) AS STRING + FOR i& = lb& TO ub& + IF VAL(source_arr$(i&)) >= min% _ + AND VAL(source_arr$(i&)) <= max% THEN + CALL ARR_STR.push(work_arr$(), source_arr$(i&)) + END IF + NEXT i& + CALL ARR_STR.shift(work_arr$(), tmp$) + CALL ARR_STR.copy(work_arr$(), dest_arr$()) +END SUB + + +'' +' Sorts STRING array in ascending order +' +' @param STRING ARRAY source_arr$() array to sort +' @param STRING ARRAY dest_arr$() array to store sorted in +' +SUB ARR_STR.sort(source_arr$(), dest_arr$()) + DIM AS LONG lb, ub, count + lb& = LBOUND(source_arr$) : ub& = UBOUND(source_arr$) + count& = ub& - lb& + REDIM dest_arr(0 TO count&) AS STRING + CALL ARR_STR.copy(source_arr$(), dest_arr$()) + CALL ARR_STR.quicksort(dest_arr$(), lb&, ub&, 0) +END SUB + + +'' +' Sorts STRING array in descending order +' +' @param STRING ARRAY source_arr$() array to sort +' @param STRING ARRAY dest_arr$() array to store sorted in +' +SUB ARR_STR.rsort(source_arr$(), dest_arr$()) + DIM AS LONG lb, ub, count + lb& = LBOUND(source_arr$) : ub& = UBOUND(source_arr$) + count& = ub& - lb& + REDIM dest_arr(0 TO count&) AS STRING + CALL ARR_STR.copy(source_arr$(), dest_arr$()) + CALL ARR_STR.quicksort(dest_arr$(), lb&, ub&, 1) +END SUB + + +'' +' Quicksort array with pivot algorithm by logiclrd +' +' @link https://www.tek-tips.com/faqs.cfm?fid=336 +' @param STRING ARRAY array$() to sort +' @param INTEGER start% of range to sort +' @param INTEGER finish% range of sort +' @param INTEGER order% to sort by (0 = asc / 1 = desc) +' +SUB ARR_STR.quicksort(arr$(), start%, finish%, order%) + DIM i AS LONG + DIM pivot AS INTEGER + DIM pivotvalue AS STRING + + 'first, partition the array + pivot% = start% + pivotvalue$ = arr$(pivot%) + FOR i& = start% + 1 TO finish% + IF order% = 0 THEN ' ascending order + IF arr$(i&) < pivotvalue$ THEN + arr$(pivot%) = arr$(i&) + arr$(i&) = arr$(pivot% + 1) + arr$(pivot% + 1) = pivotvalue$ + pivot% = pivot% + 1 + END IF + ELSEIF order% = 1 THEN ' descending order + IF arr$(i&) > pivotvalue$ THEN + arr$(pivot%) = arr$(i&) + arr$(i&) = arr$(pivot% + 1) + arr$(pivot% + 1) = pivotvalue$ + pivot% = pivot% + 1 + END IF + END IF + NEXT i& + + 'then, sort the subarrays to each side of the pivot + IF pivot% - start% >= 2 THEN + CALL ARR_STR.quicksort(arr$(), start%, pivot% - 1, order%) + END IF + IF finish% - pivot% >= 2 THEN + CALL ARR_STR.quicksort(arr$(), pivot% + 1, finish%, order%) + END IF +END SUB diff --git a/ARR/ARR_TEMPLATE.BAS b/ARR/ARR_TEMPLATE.BAS new file mode 100644 index 0000000..ddc14c9 --- /dev/null +++ b/ARR/ARR_TEMPLATE.BAS @@ -0,0 +1,820 @@ +'$DYNAMIC +$LET GJ_LIB_ARR_{UT}_BAS = 1 + +'' +' Slice an array from source to destination starting at index and count slices +' +' @param {Q}() source_arr{SY} to slice from +' @param {Q}() dest_arr{SY} to put slices into +' @param INTEGER start_idx% starting index to use as slice range +' @param INTEGER count% number of slices - if negative, backwards from index +' +SUB ARR_{UT}.slice(source_arr{SY}(), dest_arr{SY}(), start_idx%, count%) + DIM AS LONG ub, lb, i, n + lb& = LBOUND(source_arr{SY}) : ub& = UBOUND(source_arr{SY}) + IF start_idx% < lb& OR start_idx% + count% > ub& THEN EXIT SUB ' out of range + IF ub& - lb& < count% THEN EXIT SUB ' too many and not enough + REDIM dest_arr(0 TO ABS(count%)) AS {Q} + IF SGN(count%) = -1 THEN + IF ((start_idx% - 1) - ABS(count%)) < 0 THEN EXIT SUB ' out of range + n& = 0 + FOR i& = (start_idx% - 1) TO ((start_idx% - 1) - ABS(count%)) STEP -1 + dest_arr{SY}(n&) = source_arr{SY}(i&) + n& = n& + 1 + NEXT i& + ELSE + IF ((start_idx% + 1) + ABS(count%)) > (ub& - lb&) THEN EXIT SUB ' out of range + n& = 0 + FOR i& = start_idx% + 1 TO ((start_idx% + 1) + count%) STEP 1 + dest_arr{SY}(n&) = source_arr{SY}(i&) + n& = n& + 1 + NEXT i& + END IF +END SUB + + +'' +' Push a {LT} onto the end of a {Q} array +' +' @param {Q} arr{SY}() array to push into +' @param {Q} value{SY} of byte to push +' +SUB ARR_{UT}.push(arr{SY}(), value{SY}) + DIM AS LONG ub, lb + lb& = LBOUND(arr{SY}) : ub& = UBOUND(arr{SY}) + REDIM _PRESERVE arr(lb& TO (ub& + 1)) AS {Q} + arr{SY}(ub& + 1) = value{SY} +END SUB + + +'' +' Pop a {LT} from the end of a {Q} array +' +' @param {Q} arr{SY}() array to pop from +' @param {Q} var{SY} of {LT} to store popped {LT} +' +SUB ARR_{UT}.pop(arr{SY}(), var{SY}) + DIM AS LONG ub, lb + lb& = LBOUND(arr{SY}) : ub& = UBOUND(arr{SY}) + var{SY} = arr{SY}(ub&) + REDIM _PRESERVE arr(lb& TO (ub& - 1)) AS {Q} +END SUB + + +'' +' Pop a {LT} from the beginning of a {Q} array +' +' @param {Q} arr{SY}() array to pop from +' @param {Q} var{SY} of {LT} to store popped {LT} +' +SUB ARR_{UT}.shift(arr{SY}(), var{SY}) + DIM AS LONG ub, lb, i + lb& = LBOUND(arr{SY}) : ub& = UBOUND(arr{SY}) + var{SY} = arr{SY}(lb&) + FOR i& = lb& TO ub& - 1 + arr{SY}(i&) = arr{SY}(i& + 1) + NEXT i& + REDIM _PRESERVE arr(lb& + 1 TO ub&) AS {Q} +END SUB + + +'' +' Copy an array of {UT}s to another {Q} array +' +' @param {Q} ARRAY source_arr{SY}() source array to copy +' @param {Q} ARRAY dest_arr{SY}() dest array to copy into +' +SUB ARR_{UT}.copy(source_arr{SY}(), dest_arr{SY}()) + DIM AS LONG ub, lb, i + lb& = LBOUND(source_arr{SY}) : ub& = UBOUND(source_arr{SY}) + REDIM dest_arr(lb& TO ub&) AS {Q} + FOR i& = lb& TO ub& + dest_arr{SY}(i&) = source_arr{SY}(i&) + NEXT i& +END SUB + + +'' +' Push a {LT} into the beginning of a {Q} array +' +' @param {Q} arr{SY}() array to push into +' @param {Q} value{SY} of {LT} to push +' +SUB ARR_{UT}.unshift(arr{SY}(), value{SY}) + DIM AS LONG ub, lb, i + lb& = LBOUND(arr{SY}) : ub& = UBOUND(arr{SY}) + DIM work(lb& TO ub&) AS {Q} + CALL ARR_{UT}.copy(arr{SY}(), work{SY}()) + REDIM _PRESERVE arr(lb& TO (ub& + 1)) AS {Q} + FOR i& = lb& + 1 TO ub& + 1 + arr{SY}(i&) = work{SY}(i& - 1) + NEXT i& + arr{SY}(lb&) = value{SY} +END SUB + + +'' +' Joins an array of {UT}s as a string +' +' @param {Q} ARRAY arr{SY}() to get as a string +' @param STRING s$ to store stringified array in +' +SUB ARR_{UT}.join(arr{SY}(), s$) + DIM AS LONG ub, lb, i + s$ = "" + lb& = LBOUND(arr{SY}) : ub& = UBOUND(arr{SY}) + FOR i& = lb& TO ub& + s$ = s$ + _TRIM$(STR$(arr{SY}(i&))) + ", " + NEXT i& + ' remove trailing comma + s$ = MID$(s$, 1, (LEN(s$)-LEN(", "))) +END SUB + + +'' +' Create a new {Q} array using string of {LT}s seperated by commas +' +' @param {Q} ARRAY arr{SY}() to store the {LT}s in +' @param STRING s$ string of comma separated {LT}s +' +SUB ARR_{UT}.new(arr{SY}(), s$) + DIM AS LONG i, count + DIM t AS STRING + count& = 0 + FOR i& = 1 TO LEN(s$) + IF ASC(s$, i&) = ASC(",") THEN count& = count& + 1 + NEXT i& + REDIM arr(0 TO count&) AS {Q} + IF count& = 0 THEN + arr{SY}(0) = VAL(s$) + EXIT SUB + END IF + count& = 0 + FOR i& = 1 TO LEN(s$) + t$ = t$ + CHR$(ASC(s$, i&)) + IF ASC(s$, i&) = ASC(",") OR i& = LEN(s$) THEN + arr{SY}(count&) = VAL(t$) + count& = count& + 1 + t$ = "" + END IF + NEXT i& +END SUB + + +'' +' Return the visually longest element of a {Q} array +' +' @param {Q} ARRAY arr{SY}() to check in +' @return {Q} value of visually longest element +' +FUNCTION ARR_{UT}.longest{SY}(arr{SY}()) + DIM AS LONG lb, ub, i, res, lw + lb& = LBOUND(arr{SY}) : ub& = UBOUND(arr{SY}) + res& = 0 : lw& = 0 + FOR i& = lb& TO ub& + IF LEN(_TRIM$(STR$(arr{SY}(i&)))) > lw& THEN + lw& = LEN(_TRIM$(STR$(arr{SY}(i&)))) + res& = i& + END IF + NEXT i& + ARR_{UT}.longest{SY} = arr{SY}(res&) +END FUNCTION + + +'' +' Perform some math on every element of a {Q} array +' +' @param {Q} ARRAY source_arr{SY}() to do math on +' @param {Q} ARRAY dest_arr{SY}() to store results in +' @param STRING op$ one of: +' and or xor shl shr +' "+", "-", "*", "\", "&&", "||", "!!", "<<", ">>" +' @param {Q} value{SY} to use for operand +' +SUB ARR_{UT}.math(source_arr{SY}(), dest_arr{SY}(), op$, value{SY}) + DIM AS LONG lb, ub, i + lb& = LBOUND(source_arr{SY}) : ub& = UBOUND(source_arr{SY}) + REDIM dest_arr(lb& TO ub&) AS {Q} + FOR i& = lb& TO ub& + SELECT CASE op$ + CASE "+": + dest_arr{SY}(i&) = source_arr{SY}(i&) + value{SY} + CASE "-": + dest_arr{SY}(i&) = source_arr{SY}(i&) - value{SY} + CASE "*": + dest_arr{SY}(i&) = source_arr{SY}(i&) * value{SY} + CASE "\": + IF value{SY} > 0 THEN + dest_arr{SY}(i&) = source_arr{SY}(i&) \ value{SY} + END IF + CASE "&&": + dest_arr{SY}(i&) = source_arr{SY}(i&) AND value{SY} + CASE "||": + dest_arr{SY}(i&) = source_arr{SY}(i&) OR value{SY} + CASE "!!": + dest_arr{SY}(i&) = source_arr{SY}(i&) XOR value{SY} + CASE "<<": + dest_arr{SY}(i&) = _SHL(source_arr{SY}(i&), value{SY}) + CASE ">>": + dest_arr{SY}(i&) = _SHR(source_arr{SY}(i&), value{SY}) + END SELECT + NEXT i& +END SUB + + +'' +' Return the minimum element value in {Q} array +' +' @param {Q} ARRAY arr{SY}() to check in +' @return {Q} minimum value found +' +FUNCTION ARR_{UT}.min{SY}(arr{SY}()) + DIM AS LONG lb, ub, i + DIM AS {Q} s + lb& = LBOUND(arr{SY}) : ub& = UBOUND(arr{SY}) + s{SY} = 127 + FOR i& = lb& TO ub& + IF arr{SY}(i&) < s{SY} THEN + s{SY} = arr{SY}(i&) + END IF + NEXT i& + ARR_{UT}.min{SY} = s{SY} +END FUNCTION + + +'' +' Return the maximum element value in {Q} array +' +' @param {Q} ARRAY arr{SY}() to check in +' @return {Q} maximum value found +' +FUNCTION ARR_{UT}.max{SY}(arr{SY}()) + DIM AS LONG lb, ub, i + DIM AS {Q} s + lb& = LBOUND(arr{SY}) : ub& = UBOUND(arr{SY}) + s{SY} = 0 + FOR i& = lb& TO ub& + IF arr{SY}(i&) > s{SY} THEN + s{SY} = arr{SY}(i&) + END IF + NEXT i& + ARR_{UT}.max{SY} = s{SY} +END FUNCTION + + +'' +' Return the visually shortest element of a {Q} array +' +' @param {Q} ARRAY arr{SY}() to check in +' @return {Q} value of visually shortest element +' +FUNCTION ARR_{UT}.shortest{SY}(arr{SY}()) + DIM AS LONG lb, ub, i, res, sw + lb& = LBOUND(arr{SY}) : ub& = UBOUND(arr{SY}) + res& = 0 : sw& = 0 + FOR i& = lb& TO ub& + IF LEN(_TRIM$(STR$(arr{SY}(i&)))) < sw& THEN + sw& = LEN(_TRIM$(STR$(arr{SY}(i&)))) + res& = i& + END IF + NEXT i& + ARR_{UT}.shortest{SY} = arr{SY}(res&) +END FUNCTION + + +'' +' Return the first element of a {Q} array +' +' @param {Q} ARRAY arr{SY}() to check in +' @return {Q} value of first element +' +FUNCTION ARR_{UT}.first{SY}(arr{SY}()) + ARR_{UT}.first{SY} = arr{SY}(LBOUND(arr{SY})) +END FUNCTION + + +'' +' Return the last element of a {Q} array +' +' @param {Q} ARRAY arr{SY}() to check in +' @return {Q} value of last element +' +FUNCTION ARR_{UT}.last{SY}(arr{SY}()) + ARR_{UT}.last{SY} = arr{SY}(UBOUND(arr{SY})) +END FUNCTION + + +'' +' Return every nth array element of a {Q} array +' +' @param {Q} ARRAY source_arr{SY}() to get from +' @param {Q} ARRAY dest_arr{SY}() to store in +' @param INTEGER nth% element +' +SUB ARR_{UT}.nth(source_arr{SY}(), dest_arr{SY}(), nth%) + DIM AS LONG lb, ub, i, n + lb& = LBOUND(source_arr{SY}) : ub& = UBOUND(source_arr{SY}) + n& = (ub& - lb&) \ nth% + REDIM dest_arr(n&) AS {Q} + n& = 0 + FOR i& = lb& to ub& + IF i& MOD nth% = 0 THEN + dest_arr{SY}(n&) = source_arr{SY}(i&) + n& = n& + 1 + END IF + NEXT i& +END SUB + + +'' +' Checks if value exists in {Q} array +' +' @param {Q} ARRAY arr{SY}() to check in +' @param {Q} value{SY} value to check for +' @return INTEGER -1 if found or 0 if not found +' +FUNCTION ARR_{UT}.in%(arr{SY}(), value{SY}) + DIM AS LONG lb, ub, i + lb& = LBOUND(arr{SY}) : ub& = UBOUND(arr{SY}) + FOR i& = lb& TO ub& + IF arr{SY}(i&) = value{SY} THEN + ARR_{UT}.in% = -1 + EXIT FUNCTION + END IF + NEXT i& + ARR_{UT}.in% = 0 +END FUNCTION + + +'' +' Checks if value exists in {Q} array and returns index if found +' +' @param {Q} ARRAY arr{SY}() to check in +' @param {Q} value{SY} value to check for +' @return INTEGER index of element if found or -1 if not found +' +FUNCTION ARR_{UT}.find%(arr{SY}(), value{SY}) + DIM AS LONG lb, ub, i + lb& = LBOUND(arr{SY}) : ub& = UBOUND(arr{SY}) + FOR i& = lb& TO ub& + IF arr{SY}(i&) = value{SY} THEN + ARR_{UT}.find% = i& + EXIT FUNCTION + END IF + NEXT i& + ARR_{UT}.find% = -1 +END FUNCTION + + +'' +' Return the number of elements in a {Q} array +' +' @param {Q} ARRAY arr{SY}() to count +' @return INTEGER number of elements +' +FUNCTION ARR_{UT}.count&(arr{SY}()) + ARR_{UT}.count& = UBOUND(arr{SY}) - LBOUND(arr{SY}) +END FUNCTION + + +'' +' Return the size of a {Q} array +' +' @param {Q} ARRAY arr{SY}() to get size of +' @return LONG size in bytes +' +FUNCTION ARR_{UT}.size&(arr{SY}()) + ARR_{UT}.size& = LEN(arr{SY}()) +END FUNCTION + + +'' +' Reverses the elements of a {Q} array +' +' @param {Q} ARRAY source_arr{SY}() to reverse +' @param {Q} ARRAY dest_arr{SY}() to store reversed array in +' +SUB ARR_{UT}.reverse(source_arr{SY}(), dest_arr{SY}()) + DIM AS LONG lb, ub, i, n + lb& = LBOUND(source_arr{SY}) : ub& = UBOUND(source_arr{SY}) + REDIM dest_arr(0 TO (ub& - lb&)) AS {Q} + n& = 0 + FOR i& = ub& TO lb& STEP -1 + dest_arr{SY}(n&) = source_arr{SY}(i&) + n& = n& + 1 + NEXT i& +END SUB + + +'' +' Returns a random {LT} from a {Q} array +' +' @param {Q} ARRAY arr{SY}() array to get random element from +' @return {Q} random element +' +FUNCTION ARR_{UT}.random{SY}(arr{SY}()) + DIM AS LONG lb, ub + lb& = LBOUND(arr{SY}) : ub& = UBOUND(arr{SY}) + RANDOMIZE TIMER + ARR_{UT}.random{SY} = arr{SY}(INT(RND * (ub& - lb&)) + 1) +END FUNCTION + + +'' +' Returns the sum of all elements in a {Q} array +' +' @param {Q} ARRAY arr{SY}() array to get some for +' @return LONG sum of all elements +' +FUNCTION ARR_{UT}.sum&(arr{SY}()) + DIM AS LONG lb, ub, i + DIM sum AS LONG + lb& = LBOUND(arr{SY}) : ub& = UBOUND(arr{SY}) + FOR i& = lb& TO ub& + sum& = sum& + arr{SY}(i&) + NEXT i& + ARR_{UT}.sum& = sum& +END FUNCTION + + +'' +' Returns the average value of elements in a {Q} array +' +' @param {Q} ARRAY arr{SY}() array to get average for +' @return LONG average of elements +' +FUNCTION ARR_{UT}.avg&(arr{SY}()) + DIM AS LONG lb, ub, i + DIM sum AS LONG + lb& = LBOUND(arr{SY}) : ub& = UBOUND(arr{SY}) + FOR i& = lb& TO ub& + sum& = sum& + arr{SY}(i&) + NEXT i& + ARR_{UT}.avg& = sum& / (ub& - lb&) +END FUNCTION + + +'' +' Shuffle the elements of a {Q} array +' +' @param {Q} ARRAY source_arr{SY}() to shuffle +' @param {Q} ARRAY dest_arr{SY}() to store shuffled array in +' +SUB ARR_{UT}.shuffle(source_arr{SY}(), dest_arr{SY}()) + DIM AS LONG lb, ub, i, count + lb& = LBOUND(source_arr{SY}) : ub& = UBOUND(source_arr{SY}) + count& = ub& - lb& + REDIM dest_arr(0 TO count&) AS {Q} + CALL ARR_{UT}.copy(source_arr{SY}(), dest_arr{SY}()) + RANDOMIZE TIMER + FOR i& = 0 TO count& + SWAP dest_arr{SY}(i&), dest_arr{SY}(lb& + RND * (ub& - lb&)) + NEXT i& +END SUB + + +'' +' Makes a {Q} array contain only unique values +' +' @param {Q} ARRAY source_arr{SY}() array to get uniques for +' @param {Q} ARRAY dest_arr{SY}() array to store uniques in +' +SUB ARR_{UT}.unique(source_arr{SY}(), dest_arr{SY}()) + DIM AS LONG lb, ub, i + DIM tmp AS {Q} + lb& = LBOUND(source_arr{SY}) : ub& = UBOUND(source_arr{SY}) + DIM work_arr(0) AS {Q} + FOR i& = lb& TO ub& + IF NOT ARR_{UT}.in%(work_arr{SY}(), source_arr{SY}(i&)) THEN + CALL ARR_{UT}.push(work_arr{SY}(), source_arr{SY}(i&)) + END IF + NEXT i& + CALL ARR_{UT}.shift(work_arr{SY}(), tmp{SY}) + CALL ARR_{UT}.copy(work_arr{SY}(), dest_arr{SY}()) +END SUB + + +'' +' Filters a {Q} array to only elements greater than value +' +' @param {Q} ARRAY source_arr{SY}() array to work on +' @param {Q} ARRAY dest_arr{SY}() array to store in +' @param {Q} value{SY} to be greater than to be returned +' +SUB ARR_{UT}.gt(source_arr{SY}(), dest_arr{SY}(), value{SY}) + DIM AS LONG lb, ub, i + DIM tmp AS {Q} + lb& = LBOUND(source_arr{SY}) : ub& = UBOUND(source_arr{SY}) + DIM work_arr(0) AS {Q} + FOR i& = lb& TO ub& + IF source_arr{SY}(i&) > value{SY} THEN + CALL ARR_{UT}.push(work_arr{SY}(), source_arr{SY}(i&)) + END IF + NEXT i& + CALL ARR_{UT}.shift(work_arr{SY}(), tmp{SY}) + CALL ARR_{UT}.copy(work_arr{SY}(), dest_arr{SY}()) +END SUB + + +'' +' Filters a {Q} array to only elements greater than or equal to value +' +' @param {Q} ARRAY source_arr{SY}() array to work on +' @param {Q} ARRAY dest_arr{SY}() array to store in +' @param {Q} value{SY} to be greater than or equal to be returned +' +SUB ARR_{UT}.gte(source_arr{SY}(), dest_arr{SY}(), value{SY}) + DIM AS LONG lb, ub, i + DIM tmp AS {Q} + lb& = LBOUND(source_arr{SY}) : ub& = UBOUND(source_arr{SY}) + DIM work_arr(0) AS {Q} + FOR i& = lb& TO ub& + IF source_arr{SY}(i&) >= value{SY} THEN + CALL ARR_{UT}.push(work_arr{SY}(), source_arr{SY}(i&)) + END IF + NEXT i& + CALL ARR_{UT}.shift(work_arr{SY}(), tmp{SY}) + CALL ARR_{UT}.copy(work_arr{SY}(), dest_arr{SY}()) +END SUB + + +'' +' Filters a {Q} array to only elements less than value +' +' @param {Q} ARRAY source_arr{SY}() array to work on +' @param {Q} ARRAY dest_arr{SY}() array to store in +' @param {Q} value{SY} to be less than to be returned +' +SUB ARR_{UT}.lt(source_arr{SY}(), dest_arr{SY}(), value{SY}) + DIM AS LONG lb, ub, i + DIM tmp AS {Q} + lb& = LBOUND(source_arr{SY}) : ub& = UBOUND(source_arr{SY}) + DIM work_arr(0) AS {Q} + FOR i& = lb& TO ub& + IF source_arr{SY}(i&) < value{SY} THEN + CALL ARR_{UT}.push(work_arr{SY}(), source_arr{SY}(i&)) + END IF + NEXT i& + CALL ARR_{UT}.shift(work_arr{SY}(), tmp{SY}) + CALL ARR_{UT}.copy(work_arr{SY}(), dest_arr{SY}()) +END SUB + + +'' +' Filters a {Q} array to only elements less than or equal to value +' +' @param {Q} ARRAY source_arr{SY}() array to work on +' @param {Q} ARRAY dest_arr{SY}() array to store in +' @param {Q} value{SY} to be less than or equal to be returned +' +SUB ARR_{UT}.lte(source_arr{SY}(), dest_arr{SY}(), value{SY}) + DIM AS LONG lb, ub, i + DIM tmp AS {Q} + lb& = LBOUND(source_arr{SY}) : ub& = UBOUND(source_arr{SY}) + DIM work_arr(0) AS {Q} + FOR i& = lb& TO ub& + IF source_arr{SY}(i&) <= value{SY} THEN + CALL ARR_{UT}.push(work_arr{SY}(), source_arr{SY}(i&)) + END IF + NEXT i& + CALL ARR_{UT}.shift(work_arr{SY}(), tmp{SY}) + CALL ARR_{UT}.copy(work_arr{SY}(), dest_arr{SY}()) +END SUB + + +'' +' Finds and replaces values across all elements in a {Q} ARRAY +' +' @param {Q} ARRAY arr{SY}() to check in +' @param {Q} find{SY} value to find +' @param {Q} replace{SY} value to replace with if found +' +SUB ARR_{UT}.replace(arr{SY}(), find{SY}, replace{SY}) + DIM AS LONG lb, ub, i + lb& = LBOUND(arr{SY}) : ub& = UBOUND(arr{SY}) + FOR i& = lb& TO ub& + IF arr{SY}(i&) = find{SY} THEN + arr{SY}(i&) = replace{SY} + END IF + NEXT i& +END SUB + + +'' +' Inserts a new element into {Q} array after index +' +' @param {Q} ARRAY arr{SY}() array to work on +' @param {Q} value{SY} to insert +' @param INTEGER index% of element to insert at +' +SUB ARR_{UT}.insert(arr{SY}(), value{SY}, index%) + DIM AS LONG lb, ub, i + DIM tmp AS {Q} + DIM work_arr(0) AS {Q} + lb& = LBOUND(arr{SY}) : ub& = UBOUND(arr{SY}) + IF index% >= lb& AND index% <= ub& THEN + ' build new array up to index + FOR i& = lb& TO index% + CALL ARR_{UT}.push(work_arr{SY}(), arr{SY}(i&)) + NEXT i& + ' insert new element + CALL ARR_{UT}.push(work_arr{SY}(), value{SY}) + ' finish building array from index + 1 + FOR i& = index% + 1 TO ub& + CALL ARR_{UT}.push(work_arr{SY}(), arr{SY}(i&)) + NEXT i& + CALL ARR_{UT}.shift(work_arr{SY}(), tmp{SY}) + CALL ARR_{UT}.copy(work_arr{SY}(), arr{SY}()) + END IF +END SUB + + +'' +' Removes element from a {Q} array by element index +' +' @param {Q} ARRAY arr{SY}() array to work on +' @param INTEGER index% of element to remove +' +SUB ARR_{UT}.remove(arr{SY}(), index%) + DIM AS LONG lb, ub, i + DIM tmp AS {Q} + DIM work_arr(0) AS {Q} + lb& = LBOUND(arr{SY}) : ub& = UBOUND(arr{SY}) + IF index% >= lb& AND index% <= ub& THEN + ' build new array up to index + FOR i& = lb& TO index% - 1 + CALL ARR_{UT}.push(work_arr{SY}(), arr{SY}(i&)) + NEXT i& + ' skip elements + FOR i& = index% + 1 TO ub& + CALL ARR_{UT}.push(work_arr{SY}(), arr{SY}(i&)) + NEXT i& + CALL ARR_{UT}.shift(work_arr{SY}(), tmp{SY}) + CALL ARR_{UT}.copy(work_arr{SY}(), arr{SY}()) + END IF +END SUB + + +'' +' Filters a {Q} array to only elements that have odd values +' +' @param {Q} ARRAY source_arr{SY}() array to work on +' @param {Q} ARRAY dest_arr{SY}() array to store in +' +SUB ARR_{UT}.odd(source_arr{SY}(), dest_arr{SY}()) + DIM AS LONG lb, ub, i + DIM tmp AS {Q} + lb& = LBOUND(source_arr{SY}) : ub& = UBOUND(source_arr{SY}) + DIM work_arr(0) AS {Q} + FOR i& = lb& TO ub& + IF source_arr{SY}(i&) MOD 2 <> 0 THEN + CALL ARR_{UT}.push(work_arr{SY}(), source_arr{SY}(i&)) + END IF + NEXT i& + CALL ARR_{UT}.shift(work_arr{SY}(), tmp{SY}) + CALL ARR_{UT}.copy(work_arr{SY}(), dest_arr{SY}()) +END SUB + + +'' +' Filters a {Q} array to only elements that have even values +' +' @param {Q} ARRAY source_arr{SY}() array to work on +' @param {Q} ARRAY dest_arr{SY}() array to store in +' +SUB ARR_{UT}.even(source_arr{SY}(), dest_arr{SY}()) + DIM AS LONG lb, ub, i + DIM tmp AS {Q} + lb& = LBOUND(source_arr{SY}) : ub& = UBOUND(source_arr{SY}) + DIM work_arr(0) AS {Q} + FOR i& = lb& TO ub& + IF source_arr{SY}(i&) MOD 2 = 0 THEN + CALL ARR_{UT}.push(work_arr{SY}(), source_arr{SY}(i&)) + END IF + NEXT i& + CALL ARR_{UT}.shift(work_arr{SY}(), tmp{SY}) + CALL ARR_{UT}.copy(work_arr{SY}(), dest_arr{SY}()) +END SUB + + +'' +' Filters a {Q} array to only elements that have values evenly divisible by divisor +' +' @param {Q} ARRAY source_arr{SY}() array to work on +' @param {Q} ARRAY dest_arr{SY}() array to store in +' @param {Q} divisor{SY} for modulo +' +SUB ARR_{UT}.mod(source_arr{SY}(), dest_arr{SY}(), divisor{SY}) + DIM AS LONG lb, ub, i + DIM tmp AS {Q} + lb& = LBOUND(source_arr{SY}) : ub& = UBOUND(source_arr{SY}) + DIM work_arr(0) AS {Q} + FOR i& = lb& TO ub& + IF source_arr{SY}(i&) MOD divisor{SY} = 0 THEN + CALL ARR_{UT}.push(work_arr{SY}(), source_arr{SY}(i&)) + END IF + NEXT i& + CALL ARR_{UT}.shift(work_arr{SY}(), tmp{SY}) + CALL ARR_{UT}.copy(work_arr{SY}(), dest_arr{SY}()) +END SUB + + +'' +' Filters a {Q} array to only elements between min and max +' +' @param {Q} ARRAY source_arr{SY}() array to work on +' @param {Q} ARRAY dest_arr{SY}() array to store in +' @param {Q} min{SY} to be greater than or equal to be returned +' @param {Q} max{SY} to be less than or equal to be returned +' +SUB ARR_{UT}.between(source_arr{SY}(), dest_arr{SY}(), min{SY}, max{SY}) + DIM AS LONG lb, ub, i + DIM tmp AS {Q} + lb& = LBOUND(source_arr{SY}) : ub& = UBOUND(source_arr{SY}) + DIM work_arr(0) AS {Q} + FOR i& = lb& TO ub& + IF source_arr{SY}(i&) >= min{SY} _ + AND source_arr{SY}(i&) <= max{SY} THEN + CALL ARR_{UT}.push(work_arr{SY}(), source_arr{SY}(i&)) + END IF + NEXT i& + CALL ARR_{UT}.shift(work_arr{SY}(), tmp{SY}) + CALL ARR_{UT}.copy(work_arr{SY}(), dest_arr{SY}()) +END SUB + + +'' +' Sorts {Q} array in ascending order +' +' @param {Q} ARRAY source_arr{SY}() array to sort +' @param {Q} ARRAY dest_arr{SY}() array to store sorted in +' +SUB ARR_{UT}.sort(source_arr{SY}(), dest_arr{SY}()) + DIM AS LONG lb, ub, count + lb& = LBOUND(source_arr{SY}) : ub& = UBOUND(source_arr{SY}) + count& = ub& - lb& + REDIM dest_arr(0 TO count&) AS {Q} + CALL ARR_{UT}.copy(source_arr{SY}(), dest_arr{SY}()) + CALL ARR_{UT}.quicksort(dest_arr{SY}(), lb&, ub&, 0) +END SUB + + +'' +' Sorts {Q} array in descending order +' +' @param {Q} ARRAY source_arr{SY}() array to sort +' @param {Q} ARRAY dest_arr{SY}() array to store sorted in +' +SUB ARR_{UT}.rsort(source_arr{SY}(), dest_arr{SY}()) + DIM AS LONG lb, ub, count + lb& = LBOUND(source_arr{SY}) : ub& = UBOUND(source_arr{SY}) + count& = ub& - lb& + REDIM dest_arr(0 TO count&) AS {Q} + CALL ARR_{UT}.copy(source_arr{SY}(), dest_arr{SY}()) + CALL ARR_{UT}.quicksort(dest_arr{SY}(), lb&, ub&, 1) +END SUB + + +'' +' Quicksort array with pivot algorithm by logiclrd +' +' @link https://www.tek-tips.com/faqs.cfm?fid=336 +' @param {Q} ARRAY array{SY}() to sort +' @param INTEGER start% of range to sort +' @param INTEGER finish% range of sort +' @param INTEGER order% to sort by (0 = asc / 1 = desc) +' +SUB ARR_{UT}.quicksort(arr{SY}(), start%, finish%, order%) + DIM i AS LONG + DIM pivot AS INTEGER + DIM pivotvalue AS {Q} + + 'first, partition the array + pivot% = start% + pivotvalue{SY} = arr{SY}(pivot%) + FOR i& = start% + 1 TO finish% + IF order% = 0 THEN ' ascending order + IF arr{SY}(i&) < pivotvalue{SY} THEN + arr{SY}(pivot%) = arr{SY}(i&) + arr{SY}(i&) = arr{SY}(pivot% + 1) + arr{SY}(pivot% + 1) = pivotvalue{SY} + pivot% = pivot% + 1 + END IF + ELSEIF order% = 1 THEN ' descending order + IF arr{SY}(i&) > pivotvalue{SY} THEN + arr{SY}(pivot%) = arr{SY}(i&) + arr{SY}(i&) = arr{SY}(pivot% + 1) + arr{SY}(pivot% + 1) = pivotvalue{SY} + pivot% = pivot% + 1 + END IF + END IF + NEXT i& + + 'then, sort the subarrays to each side of the pivot + IF pivot% - start% >= 2 THEN + CALL ARR_{UT}.quicksort(arr{SY}(), start%, pivot% - 1, order%) + END IF + IF finish% - pivot% >= 2 THEN + CALL ARR_{UT}.quicksort(arr{SY}(), pivot% + 1, finish%, order%) + END IF +END SUB diff --git a/ARR/ARR_UBYTE.BAS b/ARR/ARR_UBYTE.BAS new file mode 100644 index 0000000..a514847 --- /dev/null +++ b/ARR/ARR_UBYTE.BAS @@ -0,0 +1,820 @@ +'$DYNAMIC +$LET GJ_LIB_ARR_UBYTE_BAS = 1 + +'' +' Slice an array from source to destination starting at index and count slices +' +' @param _UNSIGNED _BYTE() source_arr~%% to slice from +' @param _UNSIGNED _BYTE() dest_arr~%% to put slices into +' @param INTEGER start_idx% starting index to use as slice range +' @param INTEGER count% number of slices - if negative, backwards from index +' +SUB ARR_UBYTE.slice(source_arr~%%(), dest_arr~%%(), start_idx%, count%) + DIM AS LONG ub, lb, i, n + lb& = LBOUND(source_arr~%%) : ub& = UBOUND(source_arr~%%) + IF start_idx% < lb& OR start_idx% + count% > ub& THEN EXIT SUB ' out of range + IF ub& - lb& < count% THEN EXIT SUB ' too many and not enough + REDIM dest_arr(0 TO ABS(count%)) AS _UNSIGNED _BYTE + IF SGN(count%) = -1 THEN + IF ((start_idx% - 1) - ABS(count%)) < 0 THEN EXIT SUB ' out of range + n& = 0 + FOR i& = (start_idx% - 1) TO ((start_idx% - 1) - ABS(count%)) STEP -1 + dest_arr~%%(n&) = source_arr~%%(i&) + n& = n& + 1 + NEXT i& + ELSE + IF ((start_idx% + 1) + ABS(count%)) > (ub& - lb&) THEN EXIT SUB ' out of range + n& = 0 + FOR i& = start_idx% + 1 TO ((start_idx% + 1) + count%) STEP 1 + dest_arr~%%(n&) = source_arr~%%(i&) + n& = n& + 1 + NEXT i& + END IF +END SUB + + +'' +' Push a ubyte onto the end of a _UNSIGNED _BYTE array +' +' @param _UNSIGNED _BYTE arr~%%() array to push into +' @param _UNSIGNED _BYTE value~%% of byte to push +' +SUB ARR_UBYTE.push(arr~%%(), value~%%) + DIM AS LONG ub, lb + lb& = LBOUND(arr~%%) : ub& = UBOUND(arr~%%) + REDIM _PRESERVE arr(lb& TO (ub& + 1)) AS _UNSIGNED _BYTE + arr~%%(ub& + 1) = value~%% +END SUB + + +'' +' Pop a ubyte from the end of a _UNSIGNED _BYTE array +' +' @param _UNSIGNED _BYTE arr~%%() array to pop from +' @param _UNSIGNED _BYTE var~%% of ubyte to store popped ubyte +' +SUB ARR_UBYTE.pop(arr~%%(), var~%%) + DIM AS LONG ub, lb + lb& = LBOUND(arr~%%) : ub& = UBOUND(arr~%%) + var~%% = arr~%%(ub&) + REDIM _PRESERVE arr(lb& TO (ub& - 1)) AS _UNSIGNED _BYTE +END SUB + + +'' +' Pop a ubyte from the beginning of a _UNSIGNED _BYTE array +' +' @param _UNSIGNED _BYTE arr~%%() array to pop from +' @param _UNSIGNED _BYTE var~%% of ubyte to store popped ubyte +' +SUB ARR_UBYTE.shift(arr~%%(), var~%%) + DIM AS LONG ub, lb, i + lb& = LBOUND(arr~%%) : ub& = UBOUND(arr~%%) + var~%% = arr~%%(lb&) + FOR i& = lb& TO ub& - 1 + arr~%%(i&) = arr~%%(i& + 1) + NEXT i& + REDIM _PRESERVE arr(lb& + 1 TO ub&) AS _UNSIGNED _BYTE +END SUB + + +'' +' Copy an array of UBYTEs to another _UNSIGNED _BYTE array +' +' @param _UNSIGNED _BYTE ARRAY source_arr~%%() source array to copy +' @param _UNSIGNED _BYTE ARRAY dest_arr~%%() dest array to copy into +' +SUB ARR_UBYTE.copy(source_arr~%%(), dest_arr~%%()) + DIM AS LONG ub, lb, i + lb& = LBOUND(source_arr~%%) : ub& = UBOUND(source_arr~%%) + REDIM dest_arr(lb& TO ub&) AS _UNSIGNED _BYTE + FOR i& = lb& TO ub& + dest_arr~%%(i&) = source_arr~%%(i&) + NEXT i& +END SUB + + +'' +' Push a ubyte into the beginning of a _UNSIGNED _BYTE array +' +' @param _UNSIGNED _BYTE arr~%%() array to push into +' @param _UNSIGNED _BYTE value~%% of ubyte to push +' +SUB ARR_UBYTE.unshift(arr~%%(), value~%%) + DIM AS LONG ub, lb, i + lb& = LBOUND(arr~%%) : ub& = UBOUND(arr~%%) + DIM work(lb& TO ub&) AS _UNSIGNED _BYTE + CALL ARR_UBYTE.copy(arr~%%(), work~%%()) + REDIM _PRESERVE arr(lb& TO (ub& + 1)) AS _UNSIGNED _BYTE + FOR i& = lb& + 1 TO ub& + 1 + arr~%%(i&) = work~%%(i& - 1) + NEXT i& + arr~%%(lb&) = value~%% +END SUB + + +'' +' Joins an array of UBYTEs as a string +' +' @param _UNSIGNED _BYTE ARRAY arr~%%() to get as a string +' @param STRING s$ to store stringified array in +' +SUB ARR_UBYTE.join(arr~%%(), s$) + DIM AS LONG ub, lb, i + s$ = "" + lb& = LBOUND(arr~%%) : ub& = UBOUND(arr~%%) + FOR i& = lb& TO ub& + s$ = s$ + _TRIM$(STR$(arr~%%(i&))) + ", " + NEXT i& + ' remove trailing comma + s$ = MID$(s$, 1, (LEN(s$)-LEN(", "))) +END SUB + + +'' +' Create a new _UNSIGNED _BYTE array using string of ubytes seperated by commas +' +' @param _UNSIGNED _BYTE ARRAY arr~%%() to store the ubytes in +' @param STRING s$ string of comma separated ubytes +' +SUB ARR_UBYTE.new(arr~%%(), s$) + DIM AS LONG i, count + DIM t AS STRING + count& = 0 + FOR i& = 1 TO LEN(s$) + IF ASC(s$, i&) = ASC(",") THEN count& = count& + 1 + NEXT i& + REDIM arr(0 TO count&) AS _UNSIGNED _BYTE + IF count& = 0 THEN + arr~%%(0) = VAL(s$) + EXIT SUB + END IF + count& = 0 + FOR i& = 1 TO LEN(s$) + t$ = t$ + CHR$(ASC(s$, i&)) + IF ASC(s$, i&) = ASC(",") OR i& = LEN(s$) THEN + arr~%%(count&) = VAL(t$) + count& = count& + 1 + t$ = "" + END IF + NEXT i& +END SUB + + +'' +' Return the visually longest element of a _UNSIGNED _BYTE array +' +' @param _UNSIGNED _BYTE ARRAY arr~%%() to check in +' @return _UNSIGNED _BYTE value of visually longest element +' +FUNCTION ARR_UBYTE.longest~%%(arr~%%()) + DIM AS LONG lb, ub, i, res, lw + lb& = LBOUND(arr~%%) : ub& = UBOUND(arr~%%) + res& = 0 : lw& = 0 + FOR i& = lb& TO ub& + IF LEN(_TRIM$(STR$(arr~%%(i&)))) > lw& THEN + lw& = LEN(_TRIM$(STR$(arr~%%(i&)))) + res& = i& + END IF + NEXT i& + ARR_UBYTE.longest~%% = arr~%%(res&) +END FUNCTION + + +'' +' Perform some math on every element of a _UNSIGNED _BYTE array +' +' @param _UNSIGNED _BYTE ARRAY source_arr~%%() to do math on +' @param _UNSIGNED _BYTE ARRAY dest_arr~%%() to store results in +' @param STRING op$ one of: +' and or xor shl shr +' "+", "-", "*", "\", "&&", "||", "!!", "<<", ">>" +' @param _UNSIGNED _BYTE value~%% to use for operand +' +SUB ARR_UBYTE.math(source_arr~%%(), dest_arr~%%(), op$, value~%%) + DIM AS LONG lb, ub, i + lb& = LBOUND(source_arr~%%) : ub& = UBOUND(source_arr~%%) + REDIM dest_arr(lb& TO ub&) AS _UNSIGNED _BYTE + FOR i& = lb& TO ub& + SELECT CASE op$ + CASE "+": + dest_arr~%%(i&) = source_arr~%%(i&) + value~%% + CASE "-": + dest_arr~%%(i&) = source_arr~%%(i&) - value~%% + CASE "*": + dest_arr~%%(i&) = source_arr~%%(i&) * value~%% + CASE "\": + IF value~%% > 0 THEN + dest_arr~%%(i&) = source_arr~%%(i&) \ value~%% + END IF + CASE "&&": + dest_arr~%%(i&) = source_arr~%%(i&) AND value~%% + CASE "||": + dest_arr~%%(i&) = source_arr~%%(i&) OR value~%% + CASE "!!": + dest_arr~%%(i&) = source_arr~%%(i&) XOR value~%% + CASE "<<": + dest_arr~%%(i&) = _SHL(source_arr~%%(i&), value~%%) + CASE ">>": + dest_arr~%%(i&) = _SHR(source_arr~%%(i&), value~%%) + END SELECT + NEXT i& +END SUB + + +'' +' Return the minimum element value in _UNSIGNED _BYTE array +' +' @param _UNSIGNED _BYTE ARRAY arr~%%() to check in +' @return _UNSIGNED _BYTE minimum value found +' +FUNCTION ARR_UBYTE.min~%%(arr~%%()) + DIM AS LONG lb, ub, i + DIM AS _UNSIGNED _BYTE s + lb& = LBOUND(arr~%%) : ub& = UBOUND(arr~%%) + s~%% = 127 + FOR i& = lb& TO ub& + IF arr~%%(i&) < s~%% THEN + s~%% = arr~%%(i&) + END IF + NEXT i& + ARR_UBYTE.min~%% = s~%% +END FUNCTION + + +'' +' Return the maximum element value in _UNSIGNED _BYTE array +' +' @param _UNSIGNED _BYTE ARRAY arr~%%() to check in +' @return _UNSIGNED _BYTE maximum value found +' +FUNCTION ARR_UBYTE.max~%%(arr~%%()) + DIM AS LONG lb, ub, i + DIM AS _UNSIGNED _BYTE s + lb& = LBOUND(arr~%%) : ub& = UBOUND(arr~%%) + s~%% = 0 + FOR i& = lb& TO ub& + IF arr~%%(i&) > s~%% THEN + s~%% = arr~%%(i&) + END IF + NEXT i& + ARR_UBYTE.max~%% = s~%% +END FUNCTION + + +'' +' Return the visually shortest element of a _UNSIGNED _BYTE array +' +' @param _UNSIGNED _BYTE ARRAY arr~%%() to check in +' @return _UNSIGNED _BYTE value of visually shortest element +' +FUNCTION ARR_UBYTE.shortest~%%(arr~%%()) + DIM AS LONG lb, ub, i, res, sw + lb& = LBOUND(arr~%%) : ub& = UBOUND(arr~%%) + res& = 0 : sw& = 0 + FOR i& = lb& TO ub& + IF LEN(_TRIM$(STR$(arr~%%(i&)))) < sw& THEN + sw& = LEN(_TRIM$(STR$(arr~%%(i&)))) + res& = i& + END IF + NEXT i& + ARR_UBYTE.shortest~%% = arr~%%(res&) +END FUNCTION + + +'' +' Return the first element of a _UNSIGNED _BYTE array +' +' @param _UNSIGNED _BYTE ARRAY arr~%%() to check in +' @return _UNSIGNED _BYTE value of first element +' +FUNCTION ARR_UBYTE.first~%%(arr~%%()) + ARR_UBYTE.first~%% = arr~%%(LBOUND(arr~%%)) +END FUNCTION + + +'' +' Return the last element of a _UNSIGNED _BYTE array +' +' @param _UNSIGNED _BYTE ARRAY arr~%%() to check in +' @return _UNSIGNED _BYTE value of last element +' +FUNCTION ARR_UBYTE.last~%%(arr~%%()) + ARR_UBYTE.last~%% = arr~%%(UBOUND(arr~%%)) +END FUNCTION + + +'' +' Return every nth array element of a _UNSIGNED _BYTE array +' +' @param _UNSIGNED _BYTE ARRAY source_arr~%%() to get from +' @param _UNSIGNED _BYTE ARRAY dest_arr~%%() to store in +' @param INTEGER nth% element +' +SUB ARR_UBYTE.nth(source_arr~%%(), dest_arr~%%(), nth%) + DIM AS LONG lb, ub, i, n + lb& = LBOUND(source_arr~%%) : ub& = UBOUND(source_arr~%%) + n& = (ub& - lb&) \ nth% + REDIM dest_arr(n&) AS _UNSIGNED _BYTE + n& = 0 + FOR i& = lb& to ub& + IF i& MOD nth% = 0 THEN + dest_arr~%%(n&) = source_arr~%%(i&) + n& = n& + 1 + END IF + NEXT i& +END SUB + + +'' +' Checks if value exists in _UNSIGNED _BYTE array +' +' @param _UNSIGNED _BYTE ARRAY arr~%%() to check in +' @param _UNSIGNED _BYTE value~%% value to check for +' @return INTEGER -1 if found or 0 if not found +' +FUNCTION ARR_UBYTE.in%(arr~%%(), value~%%) + DIM AS LONG lb, ub, i + lb& = LBOUND(arr~%%) : ub& = UBOUND(arr~%%) + FOR i& = lb& TO ub& + IF arr~%%(i&) = value~%% THEN + ARR_UBYTE.in% = -1 + EXIT FUNCTION + END IF + NEXT i& + ARR_UBYTE.in% = 0 +END FUNCTION + + +'' +' Checks if value exists in _UNSIGNED _BYTE array and returns index if found +' +' @param _UNSIGNED _BYTE ARRAY arr~%%() to check in +' @param _UNSIGNED _BYTE value~%% value to check for +' @return INTEGER index of element if found or -1 if not found +' +FUNCTION ARR_UBYTE.find%(arr~%%(), value~%%) + DIM AS LONG lb, ub, i + lb& = LBOUND(arr~%%) : ub& = UBOUND(arr~%%) + FOR i& = lb& TO ub& + IF arr~%%(i&) = value~%% THEN + ARR_UBYTE.find% = i& + EXIT FUNCTION + END IF + NEXT i& + ARR_UBYTE.find% = -1 +END FUNCTION + + +'' +' Return the number of elements in a _UNSIGNED _BYTE array +' +' @param _UNSIGNED _BYTE ARRAY arr~%%() to count +' @return INTEGER number of elements +' +FUNCTION ARR_UBYTE.count&(arr~%%()) + ARR_UBYTE.count& = UBOUND(arr~%%) - LBOUND(arr~%%) +END FUNCTION + + +'' +' Return the size of a _UNSIGNED _BYTE array +' +' @param _UNSIGNED _BYTE ARRAY arr~%%() to get size of +' @return LONG size in bytes +' +FUNCTION ARR_UBYTE.size&(arr~%%()) + ARR_UBYTE.size& = LEN(arr~%%()) +END FUNCTION + + +'' +' Reverses the elements of a _UNSIGNED _BYTE array +' +' @param _UNSIGNED _BYTE ARRAY source_arr~%%() to reverse +' @param _UNSIGNED _BYTE ARRAY dest_arr~%%() to store reversed array in +' +SUB ARR_UBYTE.reverse(source_arr~%%(), dest_arr~%%()) + DIM AS LONG lb, ub, i, n + lb& = LBOUND(source_arr~%%) : ub& = UBOUND(source_arr~%%) + REDIM dest_arr(0 TO (ub& - lb&)) AS _UNSIGNED _BYTE + n& = 0 + FOR i& = ub& TO lb& STEP -1 + dest_arr~%%(n&) = source_arr~%%(i&) + n& = n& + 1 + NEXT i& +END SUB + + +'' +' Returns a random ubyte from a _UNSIGNED _BYTE array +' +' @param _UNSIGNED _BYTE ARRAY arr~%%() array to get random element from +' @return _UNSIGNED _BYTE random element +' +FUNCTION ARR_UBYTE.random~%%(arr~%%()) + DIM AS LONG lb, ub + lb& = LBOUND(arr~%%) : ub& = UBOUND(arr~%%) + RANDOMIZE TIMER + ARR_UBYTE.random~%% = arr~%%(INT(RND * (ub& - lb&)) + 1) +END FUNCTION + + +'' +' Returns the sum of all elements in a _UNSIGNED _BYTE array +' +' @param _UNSIGNED _BYTE ARRAY arr~%%() array to get some for +' @return LONG sum of all elements +' +FUNCTION ARR_UBYTE.sum&(arr~%%()) + DIM AS LONG lb, ub, i + DIM sum AS LONG + lb& = LBOUND(arr~%%) : ub& = UBOUND(arr~%%) + FOR i& = lb& TO ub& + sum& = sum& + arr~%%(i&) + NEXT i& + ARR_UBYTE.sum& = sum& +END FUNCTION + + +'' +' Returns the average value of elements in a _UNSIGNED _BYTE array +' +' @param _UNSIGNED _BYTE ARRAY arr~%%() array to get average for +' @return LONG average of elements +' +FUNCTION ARR_UBYTE.avg&(arr~%%()) + DIM AS LONG lb, ub, i + DIM sum AS LONG + lb& = LBOUND(arr~%%) : ub& = UBOUND(arr~%%) + FOR i& = lb& TO ub& + sum& = sum& + arr~%%(i&) + NEXT i& + ARR_UBYTE.avg& = sum& / (ub& - lb&) +END FUNCTION + + +'' +' Shuffle the elements of a _UNSIGNED _BYTE array +' +' @param _UNSIGNED _BYTE ARRAY source_arr~%%() to shuffle +' @param _UNSIGNED _BYTE ARRAY dest_arr~%%() to store shuffled array in +' +SUB ARR_UBYTE.shuffle(source_arr~%%(), dest_arr~%%()) + DIM AS LONG lb, ub, i, count + lb& = LBOUND(source_arr~%%) : ub& = UBOUND(source_arr~%%) + count& = ub& - lb& + REDIM dest_arr(0 TO count&) AS _UNSIGNED _BYTE + CALL ARR_UBYTE.copy(source_arr~%%(), dest_arr~%%()) + RANDOMIZE TIMER + FOR i& = 0 TO count& + SWAP dest_arr~%%(i&), dest_arr~%%(lb& + RND * (ub& - lb&)) + NEXT i& +END SUB + + +'' +' Makes a _UNSIGNED _BYTE array contain only unique values +' +' @param _UNSIGNED _BYTE ARRAY source_arr~%%() array to get uniques for +' @param _UNSIGNED _BYTE ARRAY dest_arr~%%() array to store uniques in +' +SUB ARR_UBYTE.unique(source_arr~%%(), dest_arr~%%()) + DIM AS LONG lb, ub, i + DIM tmp AS _UNSIGNED _BYTE + lb& = LBOUND(source_arr~%%) : ub& = UBOUND(source_arr~%%) + DIM work_arr(0) AS _UNSIGNED _BYTE + FOR i& = lb& TO ub& + IF NOT ARR_UBYTE.in%(work_arr~%%(), source_arr~%%(i&)) THEN + CALL ARR_UBYTE.push(work_arr~%%(), source_arr~%%(i&)) + END IF + NEXT i& + CALL ARR_UBYTE.shift(work_arr~%%(), tmp~%%) + CALL ARR_UBYTE.copy(work_arr~%%(), dest_arr~%%()) +END SUB + + +'' +' Filters a _UNSIGNED _BYTE array to only elements greater than value +' +' @param _UNSIGNED _BYTE ARRAY source_arr~%%() array to work on +' @param _UNSIGNED _BYTE ARRAY dest_arr~%%() array to store in +' @param _UNSIGNED _BYTE value~%% to be greater than to be returned +' +SUB ARR_UBYTE.gt(source_arr~%%(), dest_arr~%%(), value~%%) + DIM AS LONG lb, ub, i + DIM tmp AS _UNSIGNED _BYTE + lb& = LBOUND(source_arr~%%) : ub& = UBOUND(source_arr~%%) + DIM work_arr(0) AS _UNSIGNED _BYTE + FOR i& = lb& TO ub& + IF source_arr~%%(i&) > value~%% THEN + CALL ARR_UBYTE.push(work_arr~%%(), source_arr~%%(i&)) + END IF + NEXT i& + CALL ARR_UBYTE.shift(work_arr~%%(), tmp~%%) + CALL ARR_UBYTE.copy(work_arr~%%(), dest_arr~%%()) +END SUB + + +'' +' Filters a _UNSIGNED _BYTE array to only elements greater than or equal to value +' +' @param _UNSIGNED _BYTE ARRAY source_arr~%%() array to work on +' @param _UNSIGNED _BYTE ARRAY dest_arr~%%() array to store in +' @param _UNSIGNED _BYTE value~%% to be greater than or equal to be returned +' +SUB ARR_UBYTE.gte(source_arr~%%(), dest_arr~%%(), value~%%) + DIM AS LONG lb, ub, i + DIM tmp AS _UNSIGNED _BYTE + lb& = LBOUND(source_arr~%%) : ub& = UBOUND(source_arr~%%) + DIM work_arr(0) AS _UNSIGNED _BYTE + FOR i& = lb& TO ub& + IF source_arr~%%(i&) >= value~%% THEN + CALL ARR_UBYTE.push(work_arr~%%(), source_arr~%%(i&)) + END IF + NEXT i& + CALL ARR_UBYTE.shift(work_arr~%%(), tmp~%%) + CALL ARR_UBYTE.copy(work_arr~%%(), dest_arr~%%()) +END SUB + + +'' +' Filters a _UNSIGNED _BYTE array to only elements less than value +' +' @param _UNSIGNED _BYTE ARRAY source_arr~%%() array to work on +' @param _UNSIGNED _BYTE ARRAY dest_arr~%%() array to store in +' @param _UNSIGNED _BYTE value~%% to be less than to be returned +' +SUB ARR_UBYTE.lt(source_arr~%%(), dest_arr~%%(), value~%%) + DIM AS LONG lb, ub, i + DIM tmp AS _UNSIGNED _BYTE + lb& = LBOUND(source_arr~%%) : ub& = UBOUND(source_arr~%%) + DIM work_arr(0) AS _UNSIGNED _BYTE + FOR i& = lb& TO ub& + IF source_arr~%%(i&) < value~%% THEN + CALL ARR_UBYTE.push(work_arr~%%(), source_arr~%%(i&)) + END IF + NEXT i& + CALL ARR_UBYTE.shift(work_arr~%%(), tmp~%%) + CALL ARR_UBYTE.copy(work_arr~%%(), dest_arr~%%()) +END SUB + + +'' +' Filters a _UNSIGNED _BYTE array to only elements less than or equal to value +' +' @param _UNSIGNED _BYTE ARRAY source_arr~%%() array to work on +' @param _UNSIGNED _BYTE ARRAY dest_arr~%%() array to store in +' @param _UNSIGNED _BYTE value~%% to be less than or equal to be returned +' +SUB ARR_UBYTE.lte(source_arr~%%(), dest_arr~%%(), value~%%) + DIM AS LONG lb, ub, i + DIM tmp AS _UNSIGNED _BYTE + lb& = LBOUND(source_arr~%%) : ub& = UBOUND(source_arr~%%) + DIM work_arr(0) AS _UNSIGNED _BYTE + FOR i& = lb& TO ub& + IF source_arr~%%(i&) <= value~%% THEN + CALL ARR_UBYTE.push(work_arr~%%(), source_arr~%%(i&)) + END IF + NEXT i& + CALL ARR_UBYTE.shift(work_arr~%%(), tmp~%%) + CALL ARR_UBYTE.copy(work_arr~%%(), dest_arr~%%()) +END SUB + + +'' +' Finds and replaces values across all elements in a _UNSIGNED _BYTE ARRAY +' +' @param _UNSIGNED _BYTE ARRAY arr~%%() to check in +' @param _UNSIGNED _BYTE find~%% value to find +' @param _UNSIGNED _BYTE replace~%% value to replace with if found +' +SUB ARR_UBYTE.replace(arr~%%(), find~%%, replace~%%) + DIM AS LONG lb, ub, i + lb& = LBOUND(arr~%%) : ub& = UBOUND(arr~%%) + FOR i& = lb& TO ub& + IF arr~%%(i&) = find~%% THEN + arr~%%(i&) = replace~%% + END IF + NEXT i& +END SUB + + +'' +' Inserts a new element into _UNSIGNED _BYTE array after index +' +' @param _UNSIGNED _BYTE ARRAY arr~%%() array to work on +' @param _UNSIGNED _BYTE value~%% to insert +' @param INTEGER index% of element to insert at +' +SUB ARR_UBYTE.insert(arr~%%(), value~%%, index%) + DIM AS LONG lb, ub, i + DIM tmp AS _UNSIGNED _BYTE + DIM work_arr(0) AS _UNSIGNED _BYTE + lb& = LBOUND(arr~%%) : ub& = UBOUND(arr~%%) + IF index% >= lb& AND index% <= ub& THEN + ' build new array up to index + FOR i& = lb& TO index% + CALL ARR_UBYTE.push(work_arr~%%(), arr~%%(i&)) + NEXT i& + ' insert new element + CALL ARR_UBYTE.push(work_arr~%%(), value~%%) + ' finish building array from index + 1 + FOR i& = index% + 1 TO ub& + CALL ARR_UBYTE.push(work_arr~%%(), arr~%%(i&)) + NEXT i& + CALL ARR_UBYTE.shift(work_arr~%%(), tmp~%%) + CALL ARR_UBYTE.copy(work_arr~%%(), arr~%%()) + END IF +END SUB + + +'' +' Removes element from a _UNSIGNED _BYTE array by element index +' +' @param _UNSIGNED _BYTE ARRAY arr~%%() array to work on +' @param INTEGER index% of element to remove +' +SUB ARR_UBYTE.remove(arr~%%(), index%) + DIM AS LONG lb, ub, i + DIM tmp AS _UNSIGNED _BYTE + DIM work_arr(0) AS _UNSIGNED _BYTE + lb& = LBOUND(arr~%%) : ub& = UBOUND(arr~%%) + IF index% >= lb& AND index% <= ub& THEN + ' build new array up to index + FOR i& = lb& TO index% - 1 + CALL ARR_UBYTE.push(work_arr~%%(), arr~%%(i&)) + NEXT i& + ' skip elements + FOR i& = index% + 1 TO ub& + CALL ARR_UBYTE.push(work_arr~%%(), arr~%%(i&)) + NEXT i& + CALL ARR_UBYTE.shift(work_arr~%%(), tmp~%%) + CALL ARR_UBYTE.copy(work_arr~%%(), arr~%%()) + END IF +END SUB + + +'' +' Filters a _UNSIGNED _BYTE array to only elements that have odd values +' +' @param _UNSIGNED _BYTE ARRAY source_arr~%%() array to work on +' @param _UNSIGNED _BYTE ARRAY dest_arr~%%() array to store in +' +SUB ARR_UBYTE.odd(source_arr~%%(), dest_arr~%%()) + DIM AS LONG lb, ub, i + DIM tmp AS _UNSIGNED _BYTE + lb& = LBOUND(source_arr~%%) : ub& = UBOUND(source_arr~%%) + DIM work_arr(0) AS _UNSIGNED _BYTE + FOR i& = lb& TO ub& + IF source_arr~%%(i&) MOD 2 <> 0 THEN + CALL ARR_UBYTE.push(work_arr~%%(), source_arr~%%(i&)) + END IF + NEXT i& + CALL ARR_UBYTE.shift(work_arr~%%(), tmp~%%) + CALL ARR_UBYTE.copy(work_arr~%%(), dest_arr~%%()) +END SUB + + +'' +' Filters a _UNSIGNED _BYTE array to only elements that have even values +' +' @param _UNSIGNED _BYTE ARRAY source_arr~%%() array to work on +' @param _UNSIGNED _BYTE ARRAY dest_arr~%%() array to store in +' +SUB ARR_UBYTE.even(source_arr~%%(), dest_arr~%%()) + DIM AS LONG lb, ub, i + DIM tmp AS _UNSIGNED _BYTE + lb& = LBOUND(source_arr~%%) : ub& = UBOUND(source_arr~%%) + DIM work_arr(0) AS _UNSIGNED _BYTE + FOR i& = lb& TO ub& + IF source_arr~%%(i&) MOD 2 = 0 THEN + CALL ARR_UBYTE.push(work_arr~%%(), source_arr~%%(i&)) + END IF + NEXT i& + CALL ARR_UBYTE.shift(work_arr~%%(), tmp~%%) + CALL ARR_UBYTE.copy(work_arr~%%(), dest_arr~%%()) +END SUB + + +'' +' Filters a _UNSIGNED _BYTE array to only elements that have values evenly divisible by divisor +' +' @param _UNSIGNED _BYTE ARRAY source_arr~%%() array to work on +' @param _UNSIGNED _BYTE ARRAY dest_arr~%%() array to store in +' @param _UNSIGNED _BYTE divisor~%% for modulo +' +SUB ARR_UBYTE.mod(source_arr~%%(), dest_arr~%%(), divisor~%%) + DIM AS LONG lb, ub, i + DIM tmp AS _UNSIGNED _BYTE + lb& = LBOUND(source_arr~%%) : ub& = UBOUND(source_arr~%%) + DIM work_arr(0) AS _UNSIGNED _BYTE + FOR i& = lb& TO ub& + IF source_arr~%%(i&) MOD divisor~%% = 0 THEN + CALL ARR_UBYTE.push(work_arr~%%(), source_arr~%%(i&)) + END IF + NEXT i& + CALL ARR_UBYTE.shift(work_arr~%%(), tmp~%%) + CALL ARR_UBYTE.copy(work_arr~%%(), dest_arr~%%()) +END SUB + + +'' +' Filters a _UNSIGNED _BYTE array to only elements between min and max +' +' @param _UNSIGNED _BYTE ARRAY source_arr~%%() array to work on +' @param _UNSIGNED _BYTE ARRAY dest_arr~%%() array to store in +' @param _UNSIGNED _BYTE min~%% to be greater than or equal to be returned +' @param _UNSIGNED _BYTE max~%% to be less than or equal to be returned +' +SUB ARR_UBYTE.between(source_arr~%%(), dest_arr~%%(), min~%%, max~%%) + DIM AS LONG lb, ub, i + DIM tmp AS _UNSIGNED _BYTE + lb& = LBOUND(source_arr~%%) : ub& = UBOUND(source_arr~%%) + DIM work_arr(0) AS _UNSIGNED _BYTE + FOR i& = lb& TO ub& + IF source_arr~%%(i&) >= min~%% _ + AND source_arr~%%(i&) <= max~%% THEN + CALL ARR_UBYTE.push(work_arr~%%(), source_arr~%%(i&)) + END IF + NEXT i& + CALL ARR_UBYTE.shift(work_arr~%%(), tmp~%%) + CALL ARR_UBYTE.copy(work_arr~%%(), dest_arr~%%()) +END SUB + + +'' +' Sorts _UNSIGNED _BYTE array in ascending order +' +' @param _UNSIGNED _BYTE ARRAY source_arr~%%() array to sort +' @param _UNSIGNED _BYTE ARRAY dest_arr~%%() array to store sorted in +' +SUB ARR_UBYTE.sort(source_arr~%%(), dest_arr~%%()) + DIM AS LONG lb, ub, count + lb& = LBOUND(source_arr~%%) : ub& = UBOUND(source_arr~%%) + count& = ub& - lb& + REDIM dest_arr(0 TO count&) AS _UNSIGNED _BYTE + CALL ARR_UBYTE.copy(source_arr~%%(), dest_arr~%%()) + CALL ARR_UBYTE.quicksort(dest_arr~%%(), lb&, ub&, 0) +END SUB + + +'' +' Sorts _UNSIGNED _BYTE array in descending order +' +' @param _UNSIGNED _BYTE ARRAY source_arr~%%() array to sort +' @param _UNSIGNED _BYTE ARRAY dest_arr~%%() array to store sorted in +' +SUB ARR_UBYTE.rsort(source_arr~%%(), dest_arr~%%()) + DIM AS LONG lb, ub, count + lb& = LBOUND(source_arr~%%) : ub& = UBOUND(source_arr~%%) + count& = ub& - lb& + REDIM dest_arr(0 TO count&) AS _UNSIGNED _BYTE + CALL ARR_UBYTE.copy(source_arr~%%(), dest_arr~%%()) + CALL ARR_UBYTE.quicksort(dest_arr~%%(), lb&, ub&, 1) +END SUB + + +'' +' Quicksort array with pivot algorithm by logiclrd +' +' @link https://www.tek-tips.com/faqs.cfm?fid=336 +' @param _UNSIGNED _BYTE ARRAY array~%%() to sort +' @param INTEGER start% of range to sort +' @param INTEGER finish% range of sort +' @param INTEGER order% to sort by (0 = asc / 1 = desc) +' +SUB ARR_UBYTE.quicksort(arr~%%(), start%, finish%, order%) + DIM i AS LONG + DIM pivot AS INTEGER + DIM pivotvalue AS _UNSIGNED _BYTE + + 'first, partition the array + pivot% = start% + pivotvalue~%% = arr~%%(pivot%) + FOR i& = start% + 1 TO finish% + IF order% = 0 THEN ' ascending order + IF arr~%%(i&) < pivotvalue~%% THEN + arr~%%(pivot%) = arr~%%(i&) + arr~%%(i&) = arr~%%(pivot% + 1) + arr~%%(pivot% + 1) = pivotvalue~%% + pivot% = pivot% + 1 + END IF + ELSEIF order% = 1 THEN ' descending order + IF arr~%%(i&) > pivotvalue~%% THEN + arr~%%(pivot%) = arr~%%(i&) + arr~%%(i&) = arr~%%(pivot% + 1) + arr~%%(pivot% + 1) = pivotvalue~%% + pivot% = pivot% + 1 + END IF + END IF + NEXT i& + + 'then, sort the subarrays to each side of the pivot + IF pivot% - start% >= 2 THEN + CALL ARR_UBYTE.quicksort(arr~%%(), start%, pivot% - 1, order%) + END IF + IF finish% - pivot% >= 2 THEN + CALL ARR_UBYTE.quicksort(arr~%%(), pivot% + 1, finish%, order%) + END IF +END SUB diff --git a/ARR/ARR_UINT.BAS b/ARR/ARR_UINT.BAS new file mode 100644 index 0000000..86ccf25 --- /dev/null +++ b/ARR/ARR_UINT.BAS @@ -0,0 +1,820 @@ +'$DYNAMIC +$LET GJ_LIB_ARR_UINT_BAS = 1 + +'' +' Slice an array from source to destination starting at index and count slices +' +' @param _UNSIGNED INTEGER() source_arr~% to slice from +' @param _UNSIGNED INTEGER() dest_arr~% to put slices into +' @param INTEGER start_idx% starting index to use as slice range +' @param INTEGER count% number of slices - if negative, backwards from index +' +SUB ARR_UINT.slice(source_arr~%(), dest_arr~%(), start_idx%, count%) + DIM AS LONG ub, lb, i, n + lb& = LBOUND(source_arr~%) : ub& = UBOUND(source_arr~%) + IF start_idx% < lb& OR start_idx% + count% > ub& THEN EXIT SUB ' out of range + IF ub& - lb& < count% THEN EXIT SUB ' too many and not enough + REDIM dest_arr(0 TO ABS(count%)) AS _UNSIGNED INTEGER + IF SGN(count%) = -1 THEN + IF ((start_idx% - 1) - ABS(count%)) < 0 THEN EXIT SUB ' out of range + n& = 0 + FOR i& = (start_idx% - 1) TO ((start_idx% - 1) - ABS(count%)) STEP -1 + dest_arr~%(n&) = source_arr~%(i&) + n& = n& + 1 + NEXT i& + ELSE + IF ((start_idx% + 1) + ABS(count%)) > (ub& - lb&) THEN EXIT SUB ' out of range + n& = 0 + FOR i& = start_idx% + 1 TO ((start_idx% + 1) + count%) STEP 1 + dest_arr~%(n&) = source_arr~%(i&) + n& = n& + 1 + NEXT i& + END IF +END SUB + + +'' +' Push a uint onto the end of a _UNSIGNED INTEGER array +' +' @param _UNSIGNED INTEGER arr~%() array to push into +' @param _UNSIGNED INTEGER value~% of byte to push +' +SUB ARR_UINT.push(arr~%(), value~%) + DIM AS LONG ub, lb + lb& = LBOUND(arr~%) : ub& = UBOUND(arr~%) + REDIM _PRESERVE arr(lb& TO (ub& + 1)) AS _UNSIGNED INTEGER + arr~%(ub& + 1) = value~% +END SUB + + +'' +' Pop a uint from the end of a _UNSIGNED INTEGER array +' +' @param _UNSIGNED INTEGER arr~%() array to pop from +' @param _UNSIGNED INTEGER var~% of uint to store popped uint +' +SUB ARR_UINT.pop(arr~%(), var~%) + DIM AS LONG ub, lb + lb& = LBOUND(arr~%) : ub& = UBOUND(arr~%) + var~% = arr~%(ub&) + REDIM _PRESERVE arr(lb& TO (ub& - 1)) AS _UNSIGNED INTEGER +END SUB + + +'' +' Pop a uint from the beginning of a _UNSIGNED INTEGER array +' +' @param _UNSIGNED INTEGER arr~%() array to pop from +' @param _UNSIGNED INTEGER var~% of uint to store popped uint +' +SUB ARR_UINT.shift(arr~%(), var~%) + DIM AS LONG ub, lb, i + lb& = LBOUND(arr~%) : ub& = UBOUND(arr~%) + var~% = arr~%(lb&) + FOR i& = lb& TO ub& - 1 + arr~%(i&) = arr~%(i& + 1) + NEXT i& + REDIM _PRESERVE arr(lb& + 1 TO ub&) AS _UNSIGNED INTEGER +END SUB + + +'' +' Copy an array of UINTs to another _UNSIGNED INTEGER array +' +' @param _UNSIGNED INTEGER ARRAY source_arr~%() source array to copy +' @param _UNSIGNED INTEGER ARRAY dest_arr~%() dest array to copy into +' +SUB ARR_UINT.copy(source_arr~%(), dest_arr~%()) + DIM AS LONG ub, lb, i + lb& = LBOUND(source_arr~%) : ub& = UBOUND(source_arr~%) + REDIM dest_arr(lb& TO ub&) AS _UNSIGNED INTEGER + FOR i& = lb& TO ub& + dest_arr~%(i&) = source_arr~%(i&) + NEXT i& +END SUB + + +'' +' Push a uint into the beginning of a _UNSIGNED INTEGER array +' +' @param _UNSIGNED INTEGER arr~%() array to push into +' @param _UNSIGNED INTEGER value~% of uint to push +' +SUB ARR_UINT.unshift(arr~%(), value~%) + DIM AS LONG ub, lb, i + lb& = LBOUND(arr~%) : ub& = UBOUND(arr~%) + DIM work(lb& TO ub&) AS _UNSIGNED INTEGER + CALL ARR_UINT.copy(arr~%(), work~%()) + REDIM _PRESERVE arr(lb& TO (ub& + 1)) AS _UNSIGNED INTEGER + FOR i& = lb& + 1 TO ub& + 1 + arr~%(i&) = work~%(i& - 1) + NEXT i& + arr~%(lb&) = value~% +END SUB + + +'' +' Joins an array of UINTs as a string +' +' @param _UNSIGNED INTEGER ARRAY arr~%() to get as a string +' @param STRING s$ to store stringified array in +' +SUB ARR_UINT.join(arr~%(), s$) + DIM AS LONG ub, lb, i + s$ = "" + lb& = LBOUND(arr~%) : ub& = UBOUND(arr~%) + FOR i& = lb& TO ub& + s$ = s$ + _TRIM$(STR$(arr~%(i&))) + ", " + NEXT i& + ' remove trailing comma + s$ = MID$(s$, 1, (LEN(s$)-LEN(", "))) +END SUB + + +'' +' Create a new _UNSIGNED INTEGER array using string of uints seperated by commas +' +' @param _UNSIGNED INTEGER ARRAY arr~%() to store the uints in +' @param STRING s$ string of comma separated uints +' +SUB ARR_UINT.new(arr~%(), s$) + DIM AS LONG i, count + DIM t AS STRING + count& = 0 + FOR i& = 1 TO LEN(s$) + IF ASC(s$, i&) = ASC(",") THEN count& = count& + 1 + NEXT i& + REDIM arr(0 TO count&) AS _UNSIGNED INTEGER + IF count& = 0 THEN + arr~%(0) = VAL(s$) + EXIT SUB + END IF + count& = 0 + FOR i& = 1 TO LEN(s$) + t$ = t$ + CHR$(ASC(s$, i&)) + IF ASC(s$, i&) = ASC(",") OR i& = LEN(s$) THEN + arr~%(count&) = VAL(t$) + count& = count& + 1 + t$ = "" + END IF + NEXT i& +END SUB + + +'' +' Return the visually longest element of a _UNSIGNED INTEGER array +' +' @param _UNSIGNED INTEGER ARRAY arr~%() to check in +' @return _UNSIGNED INTEGER value of visually longest element +' +FUNCTION ARR_UINT.longest~%(arr~%()) + DIM AS LONG lb, ub, i, res, lw + lb& = LBOUND(arr~%) : ub& = UBOUND(arr~%) + res& = 0 : lw& = 0 + FOR i& = lb& TO ub& + IF LEN(_TRIM$(STR$(arr~%(i&)))) > lw& THEN + lw& = LEN(_TRIM$(STR$(arr~%(i&)))) + res& = i& + END IF + NEXT i& + ARR_UINT.longest~% = arr~%(res&) +END FUNCTION + + +'' +' Perform some math on every element of a _UNSIGNED INTEGER array +' +' @param _UNSIGNED INTEGER ARRAY source_arr~%() to do math on +' @param _UNSIGNED INTEGER ARRAY dest_arr~%() to store results in +' @param STRING op$ one of: +' and or xor shl shr +' "+", "-", "*", "\", "&&", "||", "!!", "<<", ">>" +' @param _UNSIGNED INTEGER value~% to use for operand +' +SUB ARR_UINT.math(source_arr~%(), dest_arr~%(), op$, value~%) + DIM AS LONG lb, ub, i + lb& = LBOUND(source_arr~%) : ub& = UBOUND(source_arr~%) + REDIM dest_arr(lb& TO ub&) AS _UNSIGNED INTEGER + FOR i& = lb& TO ub& + SELECT CASE op$ + CASE "+": + dest_arr~%(i&) = source_arr~%(i&) + value~% + CASE "-": + dest_arr~%(i&) = source_arr~%(i&) - value~% + CASE "*": + dest_arr~%(i&) = source_arr~%(i&) * value~% + CASE "\": + IF value~% > 0 THEN + dest_arr~%(i&) = source_arr~%(i&) \ value~% + END IF + CASE "&&": + dest_arr~%(i&) = source_arr~%(i&) AND value~% + CASE "||": + dest_arr~%(i&) = source_arr~%(i&) OR value~% + CASE "!!": + dest_arr~%(i&) = source_arr~%(i&) XOR value~% + CASE "<<": + dest_arr~%(i&) = _SHL(source_arr~%(i&), value~%) + CASE ">>": + dest_arr~%(i&) = _SHR(source_arr~%(i&), value~%) + END SELECT + NEXT i& +END SUB + + +'' +' Return the minimum element value in _UNSIGNED INTEGER array +' +' @param _UNSIGNED INTEGER ARRAY arr~%() to check in +' @return _UNSIGNED INTEGER minimum value found +' +FUNCTION ARR_UINT.min~%(arr~%()) + DIM AS LONG lb, ub, i + DIM AS _UNSIGNED INTEGER s + lb& = LBOUND(arr~%) : ub& = UBOUND(arr~%) + s~% = 127 + FOR i& = lb& TO ub& + IF arr~%(i&) < s~% THEN + s~% = arr~%(i&) + END IF + NEXT i& + ARR_UINT.min~% = s~% +END FUNCTION + + +'' +' Return the maximum element value in _UNSIGNED INTEGER array +' +' @param _UNSIGNED INTEGER ARRAY arr~%() to check in +' @return _UNSIGNED INTEGER maximum value found +' +FUNCTION ARR_UINT.max~%(arr~%()) + DIM AS LONG lb, ub, i + DIM AS _UNSIGNED INTEGER s + lb& = LBOUND(arr~%) : ub& = UBOUND(arr~%) + s~% = 0 + FOR i& = lb& TO ub& + IF arr~%(i&) > s~% THEN + s~% = arr~%(i&) + END IF + NEXT i& + ARR_UINT.max~% = s~% +END FUNCTION + + +'' +' Return the visually shortest element of a _UNSIGNED INTEGER array +' +' @param _UNSIGNED INTEGER ARRAY arr~%() to check in +' @return _UNSIGNED INTEGER value of visually shortest element +' +FUNCTION ARR_UINT.shortest~%(arr~%()) + DIM AS LONG lb, ub, i, res, sw + lb& = LBOUND(arr~%) : ub& = UBOUND(arr~%) + res& = 0 : sw& = 0 + FOR i& = lb& TO ub& + IF LEN(_TRIM$(STR$(arr~%(i&)))) < sw& THEN + sw& = LEN(_TRIM$(STR$(arr~%(i&)))) + res& = i& + END IF + NEXT i& + ARR_UINT.shortest~% = arr~%(res&) +END FUNCTION + + +'' +' Return the first element of a _UNSIGNED INTEGER array +' +' @param _UNSIGNED INTEGER ARRAY arr~%() to check in +' @return _UNSIGNED INTEGER value of first element +' +FUNCTION ARR_UINT.first~%(arr~%()) + ARR_UINT.first~% = arr~%(LBOUND(arr~%)) +END FUNCTION + + +'' +' Return the last element of a _UNSIGNED INTEGER array +' +' @param _UNSIGNED INTEGER ARRAY arr~%() to check in +' @return _UNSIGNED INTEGER value of last element +' +FUNCTION ARR_UINT.last~%(arr~%()) + ARR_UINT.last~% = arr~%(UBOUND(arr~%)) +END FUNCTION + + +'' +' Return every nth array element of a _UNSIGNED INTEGER array +' +' @param _UNSIGNED INTEGER ARRAY source_arr~%() to get from +' @param _UNSIGNED INTEGER ARRAY dest_arr~%() to store in +' @param INTEGER nth% element +' +SUB ARR_UINT.nth(source_arr~%(), dest_arr~%(), nth%) + DIM AS LONG lb, ub, i, n + lb& = LBOUND(source_arr~%) : ub& = UBOUND(source_arr~%) + n& = (ub& - lb&) \ nth% + REDIM dest_arr(n&) AS _UNSIGNED INTEGER + n& = 0 + FOR i& = lb& to ub& + IF i& MOD nth% = 0 THEN + dest_arr~%(n&) = source_arr~%(i&) + n& = n& + 1 + END IF + NEXT i& +END SUB + + +'' +' Checks if value exists in _UNSIGNED INTEGER array +' +' @param _UNSIGNED INTEGER ARRAY arr~%() to check in +' @param _UNSIGNED INTEGER value~% value to check for +' @return INTEGER -1 if found or 0 if not found +' +FUNCTION ARR_UINT.in%(arr~%(), value~%) + DIM AS LONG lb, ub, i + lb& = LBOUND(arr~%) : ub& = UBOUND(arr~%) + FOR i& = lb& TO ub& + IF arr~%(i&) = value~% THEN + ARR_UINT.in% = -1 + EXIT FUNCTION + END IF + NEXT i& + ARR_UINT.in% = 0 +END FUNCTION + + +'' +' Checks if value exists in _UNSIGNED INTEGER array and returns index if found +' +' @param _UNSIGNED INTEGER ARRAY arr~%() to check in +' @param _UNSIGNED INTEGER value~% value to check for +' @return INTEGER index of element if found or -1 if not found +' +FUNCTION ARR_UINT.find%(arr~%(), value~%) + DIM AS LONG lb, ub, i + lb& = LBOUND(arr~%) : ub& = UBOUND(arr~%) + FOR i& = lb& TO ub& + IF arr~%(i&) = value~% THEN + ARR_UINT.find% = i& + EXIT FUNCTION + END IF + NEXT i& + ARR_UINT.find% = -1 +END FUNCTION + + +'' +' Return the number of elements in a _UNSIGNED INTEGER array +' +' @param _UNSIGNED INTEGER ARRAY arr~%() to count +' @return INTEGER number of elements +' +FUNCTION ARR_UINT.count&(arr~%()) + ARR_UINT.count& = UBOUND(arr~%) - LBOUND(arr~%) +END FUNCTION + + +'' +' Return the size of a _UNSIGNED INTEGER array +' +' @param _UNSIGNED INTEGER ARRAY arr~%() to get size of +' @return LONG size in bytes +' +FUNCTION ARR_UINT.size&(arr~%()) + ARR_UINT.size& = LEN(arr~%()) +END FUNCTION + + +'' +' Reverses the elements of a _UNSIGNED INTEGER array +' +' @param _UNSIGNED INTEGER ARRAY source_arr~%() to reverse +' @param _UNSIGNED INTEGER ARRAY dest_arr~%() to store reversed array in +' +SUB ARR_UINT.reverse(source_arr~%(), dest_arr~%()) + DIM AS LONG lb, ub, i, n + lb& = LBOUND(source_arr~%) : ub& = UBOUND(source_arr~%) + REDIM dest_arr(0 TO (ub& - lb&)) AS _UNSIGNED INTEGER + n& = 0 + FOR i& = ub& TO lb& STEP -1 + dest_arr~%(n&) = source_arr~%(i&) + n& = n& + 1 + NEXT i& +END SUB + + +'' +' Returns a random uint from a _UNSIGNED INTEGER array +' +' @param _UNSIGNED INTEGER ARRAY arr~%() array to get random element from +' @return _UNSIGNED INTEGER random element +' +FUNCTION ARR_UINT.random~%(arr~%()) + DIM AS LONG lb, ub + lb& = LBOUND(arr~%) : ub& = UBOUND(arr~%) + RANDOMIZE TIMER + ARR_UINT.random~% = arr~%(INT(RND * (ub& - lb&)) + 1) +END FUNCTION + + +'' +' Returns the sum of all elements in a _UNSIGNED INTEGER array +' +' @param _UNSIGNED INTEGER ARRAY arr~%() array to get some for +' @return LONG sum of all elements +' +FUNCTION ARR_UINT.sum&(arr~%()) + DIM AS LONG lb, ub, i + DIM sum AS LONG + lb& = LBOUND(arr~%) : ub& = UBOUND(arr~%) + FOR i& = lb& TO ub& + sum& = sum& + arr~%(i&) + NEXT i& + ARR_UINT.sum& = sum& +END FUNCTION + + +'' +' Returns the average value of elements in a _UNSIGNED INTEGER array +' +' @param _UNSIGNED INTEGER ARRAY arr~%() array to get average for +' @return LONG average of elements +' +FUNCTION ARR_UINT.avg&(arr~%()) + DIM AS LONG lb, ub, i + DIM sum AS LONG + lb& = LBOUND(arr~%) : ub& = UBOUND(arr~%) + FOR i& = lb& TO ub& + sum& = sum& + arr~%(i&) + NEXT i& + ARR_UINT.avg& = sum& / (ub& - lb&) +END FUNCTION + + +'' +' Shuffle the elements of a _UNSIGNED INTEGER array +' +' @param _UNSIGNED INTEGER ARRAY source_arr~%() to shuffle +' @param _UNSIGNED INTEGER ARRAY dest_arr~%() to store shuffled array in +' +SUB ARR_UINT.shuffle(source_arr~%(), dest_arr~%()) + DIM AS LONG lb, ub, i, count + lb& = LBOUND(source_arr~%) : ub& = UBOUND(source_arr~%) + count& = ub& - lb& + REDIM dest_arr(0 TO count&) AS _UNSIGNED INTEGER + CALL ARR_UINT.copy(source_arr~%(), dest_arr~%()) + RANDOMIZE TIMER + FOR i& = 0 TO count& + SWAP dest_arr~%(i&), dest_arr~%(lb& + RND * (ub& - lb&)) + NEXT i& +END SUB + + +'' +' Makes a _UNSIGNED INTEGER array contain only unique values +' +' @param _UNSIGNED INTEGER ARRAY source_arr~%() array to get uniques for +' @param _UNSIGNED INTEGER ARRAY dest_arr~%() array to store uniques in +' +SUB ARR_UINT.unique(source_arr~%(), dest_arr~%()) + DIM AS LONG lb, ub, i + DIM tmp AS _UNSIGNED INTEGER + lb& = LBOUND(source_arr~%) : ub& = UBOUND(source_arr~%) + DIM work_arr(0) AS _UNSIGNED INTEGER + FOR i& = lb& TO ub& + IF NOT ARR_UINT.in%(work_arr~%(), source_arr~%(i&)) THEN + CALL ARR_UINT.push(work_arr~%(), source_arr~%(i&)) + END IF + NEXT i& + CALL ARR_UINT.shift(work_arr~%(), tmp~%) + CALL ARR_UINT.copy(work_arr~%(), dest_arr~%()) +END SUB + + +'' +' Filters a _UNSIGNED INTEGER array to only elements greater than value +' +' @param _UNSIGNED INTEGER ARRAY source_arr~%() array to work on +' @param _UNSIGNED INTEGER ARRAY dest_arr~%() array to store in +' @param _UNSIGNED INTEGER value~% to be greater than to be returned +' +SUB ARR_UINT.gt(source_arr~%(), dest_arr~%(), value~%) + DIM AS LONG lb, ub, i + DIM tmp AS _UNSIGNED INTEGER + lb& = LBOUND(source_arr~%) : ub& = UBOUND(source_arr~%) + DIM work_arr(0) AS _UNSIGNED INTEGER + FOR i& = lb& TO ub& + IF source_arr~%(i&) > value~% THEN + CALL ARR_UINT.push(work_arr~%(), source_arr~%(i&)) + END IF + NEXT i& + CALL ARR_UINT.shift(work_arr~%(), tmp~%) + CALL ARR_UINT.copy(work_arr~%(), dest_arr~%()) +END SUB + + +'' +' Filters a _UNSIGNED INTEGER array to only elements greater than or equal to value +' +' @param _UNSIGNED INTEGER ARRAY source_arr~%() array to work on +' @param _UNSIGNED INTEGER ARRAY dest_arr~%() array to store in +' @param _UNSIGNED INTEGER value~% to be greater than or equal to be returned +' +SUB ARR_UINT.gte(source_arr~%(), dest_arr~%(), value~%) + DIM AS LONG lb, ub, i + DIM tmp AS _UNSIGNED INTEGER + lb& = LBOUND(source_arr~%) : ub& = UBOUND(source_arr~%) + DIM work_arr(0) AS _UNSIGNED INTEGER + FOR i& = lb& TO ub& + IF source_arr~%(i&) >= value~% THEN + CALL ARR_UINT.push(work_arr~%(), source_arr~%(i&)) + END IF + NEXT i& + CALL ARR_UINT.shift(work_arr~%(), tmp~%) + CALL ARR_UINT.copy(work_arr~%(), dest_arr~%()) +END SUB + + +'' +' Filters a _UNSIGNED INTEGER array to only elements less than value +' +' @param _UNSIGNED INTEGER ARRAY source_arr~%() array to work on +' @param _UNSIGNED INTEGER ARRAY dest_arr~%() array to store in +' @param _UNSIGNED INTEGER value~% to be less than to be returned +' +SUB ARR_UINT.lt(source_arr~%(), dest_arr~%(), value~%) + DIM AS LONG lb, ub, i + DIM tmp AS _UNSIGNED INTEGER + lb& = LBOUND(source_arr~%) : ub& = UBOUND(source_arr~%) + DIM work_arr(0) AS _UNSIGNED INTEGER + FOR i& = lb& TO ub& + IF source_arr~%(i&) < value~% THEN + CALL ARR_UINT.push(work_arr~%(), source_arr~%(i&)) + END IF + NEXT i& + CALL ARR_UINT.shift(work_arr~%(), tmp~%) + CALL ARR_UINT.copy(work_arr~%(), dest_arr~%()) +END SUB + + +'' +' Filters a _UNSIGNED INTEGER array to only elements less than or equal to value +' +' @param _UNSIGNED INTEGER ARRAY source_arr~%() array to work on +' @param _UNSIGNED INTEGER ARRAY dest_arr~%() array to store in +' @param _UNSIGNED INTEGER value~% to be less than or equal to be returned +' +SUB ARR_UINT.lte(source_arr~%(), dest_arr~%(), value~%) + DIM AS LONG lb, ub, i + DIM tmp AS _UNSIGNED INTEGER + lb& = LBOUND(source_arr~%) : ub& = UBOUND(source_arr~%) + DIM work_arr(0) AS _UNSIGNED INTEGER + FOR i& = lb& TO ub& + IF source_arr~%(i&) <= value~% THEN + CALL ARR_UINT.push(work_arr~%(), source_arr~%(i&)) + END IF + NEXT i& + CALL ARR_UINT.shift(work_arr~%(), tmp~%) + CALL ARR_UINT.copy(work_arr~%(), dest_arr~%()) +END SUB + + +'' +' Finds and replaces values across all elements in a _UNSIGNED INTEGER ARRAY +' +' @param _UNSIGNED INTEGER ARRAY arr~%() to check in +' @param _UNSIGNED INTEGER find~% value to find +' @param _UNSIGNED INTEGER replace~% value to replace with if found +' +SUB ARR_UINT.replace(arr~%(), find~%, replace~%) + DIM AS LONG lb, ub, i + lb& = LBOUND(arr~%) : ub& = UBOUND(arr~%) + FOR i& = lb& TO ub& + IF arr~%(i&) = find~% THEN + arr~%(i&) = replace~% + END IF + NEXT i& +END SUB + + +'' +' Inserts a new element into _UNSIGNED INTEGER array after index +' +' @param _UNSIGNED INTEGER ARRAY arr~%() array to work on +' @param _UNSIGNED INTEGER value~% to insert +' @param INTEGER index% of element to insert at +' +SUB ARR_UINT.insert(arr~%(), value~%, index%) + DIM AS LONG lb, ub, i + DIM tmp AS _UNSIGNED INTEGER + DIM work_arr(0) AS _UNSIGNED INTEGER + lb& = LBOUND(arr~%) : ub& = UBOUND(arr~%) + IF index% >= lb& AND index% <= ub& THEN + ' build new array up to index + FOR i& = lb& TO index% + CALL ARR_UINT.push(work_arr~%(), arr~%(i&)) + NEXT i& + ' insert new element + CALL ARR_UINT.push(work_arr~%(), value~%) + ' finish building array from index + 1 + FOR i& = index% + 1 TO ub& + CALL ARR_UINT.push(work_arr~%(), arr~%(i&)) + NEXT i& + CALL ARR_UINT.shift(work_arr~%(), tmp~%) + CALL ARR_UINT.copy(work_arr~%(), arr~%()) + END IF +END SUB + + +'' +' Removes element from a _UNSIGNED INTEGER array by element index +' +' @param _UNSIGNED INTEGER ARRAY arr~%() array to work on +' @param INTEGER index% of element to remove +' +SUB ARR_UINT.remove(arr~%(), index%) + DIM AS LONG lb, ub, i + DIM tmp AS _UNSIGNED INTEGER + DIM work_arr(0) AS _UNSIGNED INTEGER + lb& = LBOUND(arr~%) : ub& = UBOUND(arr~%) + IF index% >= lb& AND index% <= ub& THEN + ' build new array up to index + FOR i& = lb& TO index% - 1 + CALL ARR_UINT.push(work_arr~%(), arr~%(i&)) + NEXT i& + ' skip elements + FOR i& = index% + 1 TO ub& + CALL ARR_UINT.push(work_arr~%(), arr~%(i&)) + NEXT i& + CALL ARR_UINT.shift(work_arr~%(), tmp~%) + CALL ARR_UINT.copy(work_arr~%(), arr~%()) + END IF +END SUB + + +'' +' Filters a _UNSIGNED INTEGER array to only elements that have odd values +' +' @param _UNSIGNED INTEGER ARRAY source_arr~%() array to work on +' @param _UNSIGNED INTEGER ARRAY dest_arr~%() array to store in +' +SUB ARR_UINT.odd(source_arr~%(), dest_arr~%()) + DIM AS LONG lb, ub, i + DIM tmp AS _UNSIGNED INTEGER + lb& = LBOUND(source_arr~%) : ub& = UBOUND(source_arr~%) + DIM work_arr(0) AS _UNSIGNED INTEGER + FOR i& = lb& TO ub& + IF source_arr~%(i&) MOD 2 <> 0 THEN + CALL ARR_UINT.push(work_arr~%(), source_arr~%(i&)) + END IF + NEXT i& + CALL ARR_UINT.shift(work_arr~%(), tmp~%) + CALL ARR_UINT.copy(work_arr~%(), dest_arr~%()) +END SUB + + +'' +' Filters a _UNSIGNED INTEGER array to only elements that have even values +' +' @param _UNSIGNED INTEGER ARRAY source_arr~%() array to work on +' @param _UNSIGNED INTEGER ARRAY dest_arr~%() array to store in +' +SUB ARR_UINT.even(source_arr~%(), dest_arr~%()) + DIM AS LONG lb, ub, i + DIM tmp AS _UNSIGNED INTEGER + lb& = LBOUND(source_arr~%) : ub& = UBOUND(source_arr~%) + DIM work_arr(0) AS _UNSIGNED INTEGER + FOR i& = lb& TO ub& + IF source_arr~%(i&) MOD 2 = 0 THEN + CALL ARR_UINT.push(work_arr~%(), source_arr~%(i&)) + END IF + NEXT i& + CALL ARR_UINT.shift(work_arr~%(), tmp~%) + CALL ARR_UINT.copy(work_arr~%(), dest_arr~%()) +END SUB + + +'' +' Filters a _UNSIGNED INTEGER array to only elements that have values evenly divisible by divisor +' +' @param _UNSIGNED INTEGER ARRAY source_arr~%() array to work on +' @param _UNSIGNED INTEGER ARRAY dest_arr~%() array to store in +' @param _UNSIGNED INTEGER divisor~% for modulo +' +SUB ARR_UINT.mod(source_arr~%(), dest_arr~%(), divisor~%) + DIM AS LONG lb, ub, i + DIM tmp AS _UNSIGNED INTEGER + lb& = LBOUND(source_arr~%) : ub& = UBOUND(source_arr~%) + DIM work_arr(0) AS _UNSIGNED INTEGER + FOR i& = lb& TO ub& + IF source_arr~%(i&) MOD divisor~% = 0 THEN + CALL ARR_UINT.push(work_arr~%(), source_arr~%(i&)) + END IF + NEXT i& + CALL ARR_UINT.shift(work_arr~%(), tmp~%) + CALL ARR_UINT.copy(work_arr~%(), dest_arr~%()) +END SUB + + +'' +' Filters a _UNSIGNED INTEGER array to only elements between min and max +' +' @param _UNSIGNED INTEGER ARRAY source_arr~%() array to work on +' @param _UNSIGNED INTEGER ARRAY dest_arr~%() array to store in +' @param _UNSIGNED INTEGER min~% to be greater than or equal to be returned +' @param _UNSIGNED INTEGER max~% to be less than or equal to be returned +' +SUB ARR_UINT.between(source_arr~%(), dest_arr~%(), min~%, max~%) + DIM AS LONG lb, ub, i + DIM tmp AS _UNSIGNED INTEGER + lb& = LBOUND(source_arr~%) : ub& = UBOUND(source_arr~%) + DIM work_arr(0) AS _UNSIGNED INTEGER + FOR i& = lb& TO ub& + IF source_arr~%(i&) >= min~% _ + AND source_arr~%(i&) <= max~% THEN + CALL ARR_UINT.push(work_arr~%(), source_arr~%(i&)) + END IF + NEXT i& + CALL ARR_UINT.shift(work_arr~%(), tmp~%) + CALL ARR_UINT.copy(work_arr~%(), dest_arr~%()) +END SUB + + +'' +' Sorts _UNSIGNED INTEGER array in ascending order +' +' @param _UNSIGNED INTEGER ARRAY source_arr~%() array to sort +' @param _UNSIGNED INTEGER ARRAY dest_arr~%() array to store sorted in +' +SUB ARR_UINT.sort(source_arr~%(), dest_arr~%()) + DIM AS LONG lb, ub, count + lb& = LBOUND(source_arr~%) : ub& = UBOUND(source_arr~%) + count& = ub& - lb& + REDIM dest_arr(0 TO count&) AS _UNSIGNED INTEGER + CALL ARR_UINT.copy(source_arr~%(), dest_arr~%()) + CALL ARR_UINT.quicksort(dest_arr~%(), lb&, ub&, 0) +END SUB + + +'' +' Sorts _UNSIGNED INTEGER array in descending order +' +' @param _UNSIGNED INTEGER ARRAY source_arr~%() array to sort +' @param _UNSIGNED INTEGER ARRAY dest_arr~%() array to store sorted in +' +SUB ARR_UINT.rsort(source_arr~%(), dest_arr~%()) + DIM AS LONG lb, ub, count + lb& = LBOUND(source_arr~%) : ub& = UBOUND(source_arr~%) + count& = ub& - lb& + REDIM dest_arr(0 TO count&) AS _UNSIGNED INTEGER + CALL ARR_UINT.copy(source_arr~%(), dest_arr~%()) + CALL ARR_UINT.quicksort(dest_arr~%(), lb&, ub&, 1) +END SUB + + +'' +' Quicksort array with pivot algorithm by logiclrd +' +' @link https://www.tek-tips.com/faqs.cfm?fid=336 +' @param _UNSIGNED INTEGER ARRAY array~%() to sort +' @param INTEGER start% of range to sort +' @param INTEGER finish% range of sort +' @param INTEGER order% to sort by (0 = asc / 1 = desc) +' +SUB ARR_UINT.quicksort(arr~%(), start%, finish%, order%) + DIM i AS LONG + DIM pivot AS INTEGER + DIM pivotvalue AS _UNSIGNED INTEGER + + 'first, partition the array + pivot% = start% + pivotvalue~% = arr~%(pivot%) + FOR i& = start% + 1 TO finish% + IF order% = 0 THEN ' ascending order + IF arr~%(i&) < pivotvalue~% THEN + arr~%(pivot%) = arr~%(i&) + arr~%(i&) = arr~%(pivot% + 1) + arr~%(pivot% + 1) = pivotvalue~% + pivot% = pivot% + 1 + END IF + ELSEIF order% = 1 THEN ' descending order + IF arr~%(i&) > pivotvalue~% THEN + arr~%(pivot%) = arr~%(i&) + arr~%(i&) = arr~%(pivot% + 1) + arr~%(pivot% + 1) = pivotvalue~% + pivot% = pivot% + 1 + END IF + END IF + NEXT i& + + 'then, sort the subarrays to each side of the pivot + IF pivot% - start% >= 2 THEN + CALL ARR_UINT.quicksort(arr~%(), start%, pivot% - 1, order%) + END IF + IF finish% - pivot% >= 2 THEN + CALL ARR_UINT.quicksort(arr~%(), pivot% + 1, finish%, order%) + END IF +END SUB diff --git a/ARR/ARR_UINT64.BAS b/ARR/ARR_UINT64.BAS new file mode 100644 index 0000000..412f9bd --- /dev/null +++ b/ARR/ARR_UINT64.BAS @@ -0,0 +1,820 @@ +'$DYNAMIC +$LET GJ_LIB_ARR_UINT64_BAS = 1 + +'' +' Slice an array from source to destination starting at index and count slices +' +' @param _UNSIGNED _INTEGER64() source_arr~&& to slice from +' @param _UNSIGNED _INTEGER64() dest_arr~&& to put slices into +' @param INTEGER start_idx% starting index to use as slice range +' @param INTEGER count% number of slices - if negative, backwards from index +' +SUB ARR_UINT64.slice(source_arr~&&(), dest_arr~&&(), start_idx%, count%) + DIM AS LONG ub, lb, i, n + lb& = LBOUND(source_arr~&&) : ub& = UBOUND(source_arr~&&) + IF start_idx% < lb& OR start_idx% + count% > ub& THEN EXIT SUB ' out of range + IF ub& - lb& < count% THEN EXIT SUB ' too many and not enough + REDIM dest_arr(0 TO ABS(count%)) AS _UNSIGNED _INTEGER64 + IF SGN(count%) = -1 THEN + IF ((start_idx% - 1) - ABS(count%)) < 0 THEN EXIT SUB ' out of range + n& = 0 + FOR i& = (start_idx% - 1) TO ((start_idx% - 1) - ABS(count%)) STEP -1 + dest_arr~&&(n&) = source_arr~&&(i&) + n& = n& + 1 + NEXT i& + ELSE + IF ((start_idx% + 1) + ABS(count%)) > (ub& - lb&) THEN EXIT SUB ' out of range + n& = 0 + FOR i& = start_idx% + 1 TO ((start_idx% + 1) + count%) STEP 1 + dest_arr~&&(n&) = source_arr~&&(i&) + n& = n& + 1 + NEXT i& + END IF +END SUB + + +'' +' Push a uint64 onto the end of a _UNSIGNED _INTEGER64 array +' +' @param _UNSIGNED _INTEGER64 arr~&&() array to push into +' @param _UNSIGNED _INTEGER64 value~&& of byte to push +' +SUB ARR_UINT64.push(arr~&&(), value~&&) + DIM AS LONG ub, lb + lb& = LBOUND(arr~&&) : ub& = UBOUND(arr~&&) + REDIM _PRESERVE arr(lb& TO (ub& + 1)) AS _UNSIGNED _INTEGER64 + arr~&&(ub& + 1) = value~&& +END SUB + + +'' +' Pop a uint64 from the end of a _UNSIGNED _INTEGER64 array +' +' @param _UNSIGNED _INTEGER64 arr~&&() array to pop from +' @param _UNSIGNED _INTEGER64 var~&& of uint64 to store popped uint64 +' +SUB ARR_UINT64.pop(arr~&&(), var~&&) + DIM AS LONG ub, lb + lb& = LBOUND(arr~&&) : ub& = UBOUND(arr~&&) + var~&& = arr~&&(ub&) + REDIM _PRESERVE arr(lb& TO (ub& - 1)) AS _UNSIGNED _INTEGER64 +END SUB + + +'' +' Pop a uint64 from the beginning of a _UNSIGNED _INTEGER64 array +' +' @param _UNSIGNED _INTEGER64 arr~&&() array to pop from +' @param _UNSIGNED _INTEGER64 var~&& of uint64 to store popped uint64 +' +SUB ARR_UINT64.shift(arr~&&(), var~&&) + DIM AS LONG ub, lb, i + lb& = LBOUND(arr~&&) : ub& = UBOUND(arr~&&) + var~&& = arr~&&(lb&) + FOR i& = lb& TO ub& - 1 + arr~&&(i&) = arr~&&(i& + 1) + NEXT i& + REDIM _PRESERVE arr(lb& + 1 TO ub&) AS _UNSIGNED _INTEGER64 +END SUB + + +'' +' Copy an array of UINT64s to another _UNSIGNED _INTEGER64 array +' +' @param _UNSIGNED _INTEGER64 ARRAY source_arr~&&() source array to copy +' @param _UNSIGNED _INTEGER64 ARRAY dest_arr~&&() dest array to copy into +' +SUB ARR_UINT64.copy(source_arr~&&(), dest_arr~&&()) + DIM AS LONG ub, lb, i + lb& = LBOUND(source_arr~&&) : ub& = UBOUND(source_arr~&&) + REDIM dest_arr(lb& TO ub&) AS _UNSIGNED _INTEGER64 + FOR i& = lb& TO ub& + dest_arr~&&(i&) = source_arr~&&(i&) + NEXT i& +END SUB + + +'' +' Push a uint64 into the beginning of a _UNSIGNED _INTEGER64 array +' +' @param _UNSIGNED _INTEGER64 arr~&&() array to push into +' @param _UNSIGNED _INTEGER64 value~&& of uint64 to push +' +SUB ARR_UINT64.unshift(arr~&&(), value~&&) + DIM AS LONG ub, lb, i + lb& = LBOUND(arr~&&) : ub& = UBOUND(arr~&&) + DIM work(lb& TO ub&) AS _UNSIGNED _INTEGER64 + CALL ARR_UINT64.copy(arr~&&(), work~&&()) + REDIM _PRESERVE arr(lb& TO (ub& + 1)) AS _UNSIGNED _INTEGER64 + FOR i& = lb& + 1 TO ub& + 1 + arr~&&(i&) = work~&&(i& - 1) + NEXT i& + arr~&&(lb&) = value~&& +END SUB + + +'' +' Joins an array of UINT64s as a string +' +' @param _UNSIGNED _INTEGER64 ARRAY arr~&&() to get as a string +' @param STRING s$ to store stringified array in +' +SUB ARR_UINT64.join(arr~&&(), s$) + DIM AS LONG ub, lb, i + s$ = "" + lb& = LBOUND(arr~&&) : ub& = UBOUND(arr~&&) + FOR i& = lb& TO ub& + s$ = s$ + _TRIM$(STR$(arr~&&(i&))) + ", " + NEXT i& + ' remove trailing comma + s$ = MID$(s$, 1, (LEN(s$)-LEN(", "))) +END SUB + + +'' +' Create a new _UNSIGNED _INTEGER64 array using string of uint64s seperated by commas +' +' @param _UNSIGNED _INTEGER64 ARRAY arr~&&() to store the uint64s in +' @param STRING s$ string of comma separated uint64s +' +SUB ARR_UINT64.new(arr~&&(), s$) + DIM AS LONG i, count + DIM t AS STRING + count& = 0 + FOR i& = 1 TO LEN(s$) + IF ASC(s$, i&) = ASC(",") THEN count& = count& + 1 + NEXT i& + REDIM arr(0 TO count&) AS _UNSIGNED _INTEGER64 + IF count& = 0 THEN + arr~&&(0) = VAL(s$) + EXIT SUB + END IF + count& = 0 + FOR i& = 1 TO LEN(s$) + t$ = t$ + CHR$(ASC(s$, i&)) + IF ASC(s$, i&) = ASC(",") OR i& = LEN(s$) THEN + arr~&&(count&) = VAL(t$) + count& = count& + 1 + t$ = "" + END IF + NEXT i& +END SUB + + +'' +' Return the visually longest element of a _UNSIGNED _INTEGER64 array +' +' @param _UNSIGNED _INTEGER64 ARRAY arr~&&() to check in +' @return _UNSIGNED _INTEGER64 value of visually longest element +' +FUNCTION ARR_UINT64.longest~&&(arr~&&()) + DIM AS LONG lb, ub, i, res, lw + lb& = LBOUND(arr~&&) : ub& = UBOUND(arr~&&) + res& = 0 : lw& = 0 + FOR i& = lb& TO ub& + IF LEN(_TRIM$(STR$(arr~&&(i&)))) > lw& THEN + lw& = LEN(_TRIM$(STR$(arr~&&(i&)))) + res& = i& + END IF + NEXT i& + ARR_UINT64.longest~&& = arr~&&(res&) +END FUNCTION + + +'' +' Perform some math on every element of a _UNSIGNED _INTEGER64 array +' +' @param _UNSIGNED _INTEGER64 ARRAY source_arr~&&() to do math on +' @param _UNSIGNED _INTEGER64 ARRAY dest_arr~&&() to store results in +' @param STRING op$ one of: +' and or xor shl shr +' "+", "-", "*", "\", "&&", "||", "!!", "<<", ">>" +' @param _UNSIGNED _INTEGER64 value~&& to use for operand +' +SUB ARR_UINT64.math(source_arr~&&(), dest_arr~&&(), op$, value~&&) + DIM AS LONG lb, ub, i + lb& = LBOUND(source_arr~&&) : ub& = UBOUND(source_arr~&&) + REDIM dest_arr(lb& TO ub&) AS _UNSIGNED _INTEGER64 + FOR i& = lb& TO ub& + SELECT CASE op$ + CASE "+": + dest_arr~&&(i&) = source_arr~&&(i&) + value~&& + CASE "-": + dest_arr~&&(i&) = source_arr~&&(i&) - value~&& + CASE "*": + dest_arr~&&(i&) = source_arr~&&(i&) * value~&& + CASE "\": + IF value~&& > 0 THEN + dest_arr~&&(i&) = source_arr~&&(i&) \ value~&& + END IF + CASE "&&": + dest_arr~&&(i&) = source_arr~&&(i&) AND value~&& + CASE "||": + dest_arr~&&(i&) = source_arr~&&(i&) OR value~&& + CASE "!!": + dest_arr~&&(i&) = source_arr~&&(i&) XOR value~&& + CASE "<<": + dest_arr~&&(i&) = _SHL(source_arr~&&(i&), value~&&) + CASE ">>": + dest_arr~&&(i&) = _SHR(source_arr~&&(i&), value~&&) + END SELECT + NEXT i& +END SUB + + +'' +' Return the minimum element value in _UNSIGNED _INTEGER64 array +' +' @param _UNSIGNED _INTEGER64 ARRAY arr~&&() to check in +' @return _UNSIGNED _INTEGER64 minimum value found +' +FUNCTION ARR_UINT64.min~&&(arr~&&()) + DIM AS LONG lb, ub, i + DIM AS _UNSIGNED _INTEGER64 s + lb& = LBOUND(arr~&&) : ub& = UBOUND(arr~&&) + s~&& = 127 + FOR i& = lb& TO ub& + IF arr~&&(i&) < s~&& THEN + s~&& = arr~&&(i&) + END IF + NEXT i& + ARR_UINT64.min~&& = s~&& +END FUNCTION + + +'' +' Return the maximum element value in _UNSIGNED _INTEGER64 array +' +' @param _UNSIGNED _INTEGER64 ARRAY arr~&&() to check in +' @return _UNSIGNED _INTEGER64 maximum value found +' +FUNCTION ARR_UINT64.max~&&(arr~&&()) + DIM AS LONG lb, ub, i + DIM AS _UNSIGNED _INTEGER64 s + lb& = LBOUND(arr~&&) : ub& = UBOUND(arr~&&) + s~&& = 0 + FOR i& = lb& TO ub& + IF arr~&&(i&) > s~&& THEN + s~&& = arr~&&(i&) + END IF + NEXT i& + ARR_UINT64.max~&& = s~&& +END FUNCTION + + +'' +' Return the visually shortest element of a _UNSIGNED _INTEGER64 array +' +' @param _UNSIGNED _INTEGER64 ARRAY arr~&&() to check in +' @return _UNSIGNED _INTEGER64 value of visually shortest element +' +FUNCTION ARR_UINT64.shortest~&&(arr~&&()) + DIM AS LONG lb, ub, i, res, sw + lb& = LBOUND(arr~&&) : ub& = UBOUND(arr~&&) + res& = 0 : sw& = 0 + FOR i& = lb& TO ub& + IF LEN(_TRIM$(STR$(arr~&&(i&)))) < sw& THEN + sw& = LEN(_TRIM$(STR$(arr~&&(i&)))) + res& = i& + END IF + NEXT i& + ARR_UINT64.shortest~&& = arr~&&(res&) +END FUNCTION + + +'' +' Return the first element of a _UNSIGNED _INTEGER64 array +' +' @param _UNSIGNED _INTEGER64 ARRAY arr~&&() to check in +' @return _UNSIGNED _INTEGER64 value of first element +' +FUNCTION ARR_UINT64.first~&&(arr~&&()) + ARR_UINT64.first~&& = arr~&&(LBOUND(arr~&&)) +END FUNCTION + + +'' +' Return the last element of a _UNSIGNED _INTEGER64 array +' +' @param _UNSIGNED _INTEGER64 ARRAY arr~&&() to check in +' @return _UNSIGNED _INTEGER64 value of last element +' +FUNCTION ARR_UINT64.last~&&(arr~&&()) + ARR_UINT64.last~&& = arr~&&(UBOUND(arr~&&)) +END FUNCTION + + +'' +' Return every nth array element of a _UNSIGNED _INTEGER64 array +' +' @param _UNSIGNED _INTEGER64 ARRAY source_arr~&&() to get from +' @param _UNSIGNED _INTEGER64 ARRAY dest_arr~&&() to store in +' @param INTEGER nth% element +' +SUB ARR_UINT64.nth(source_arr~&&(), dest_arr~&&(), nth%) + DIM AS LONG lb, ub, i, n + lb& = LBOUND(source_arr~&&) : ub& = UBOUND(source_arr~&&) + n& = (ub& - lb&) \ nth% + REDIM dest_arr(n&) AS _UNSIGNED _INTEGER64 + n& = 0 + FOR i& = lb& to ub& + IF i& MOD nth% = 0 THEN + dest_arr~&&(n&) = source_arr~&&(i&) + n& = n& + 1 + END IF + NEXT i& +END SUB + + +'' +' Checks if value exists in _UNSIGNED _INTEGER64 array +' +' @param _UNSIGNED _INTEGER64 ARRAY arr~&&() to check in +' @param _UNSIGNED _INTEGER64 value~&& value to check for +' @return INTEGER -1 if found or 0 if not found +' +FUNCTION ARR_UINT64.in%(arr~&&(), value~&&) + DIM AS LONG lb, ub, i + lb& = LBOUND(arr~&&) : ub& = UBOUND(arr~&&) + FOR i& = lb& TO ub& + IF arr~&&(i&) = value~&& THEN + ARR_UINT64.in% = -1 + EXIT FUNCTION + END IF + NEXT i& + ARR_UINT64.in% = 0 +END FUNCTION + + +'' +' Checks if value exists in _UNSIGNED _INTEGER64 array and returns index if found +' +' @param _UNSIGNED _INTEGER64 ARRAY arr~&&() to check in +' @param _UNSIGNED _INTEGER64 value~&& value to check for +' @return INTEGER index of element if found or -1 if not found +' +FUNCTION ARR_UINT64.find%(arr~&&(), value~&&) + DIM AS LONG lb, ub, i + lb& = LBOUND(arr~&&) : ub& = UBOUND(arr~&&) + FOR i& = lb& TO ub& + IF arr~&&(i&) = value~&& THEN + ARR_UINT64.find% = i& + EXIT FUNCTION + END IF + NEXT i& + ARR_UINT64.find% = -1 +END FUNCTION + + +'' +' Return the number of elements in a _UNSIGNED _INTEGER64 array +' +' @param _UNSIGNED _INTEGER64 ARRAY arr~&&() to count +' @return INTEGER number of elements +' +FUNCTION ARR_UINT64.count&(arr~&&()) + ARR_UINT64.count& = UBOUND(arr~&&) - LBOUND(arr~&&) +END FUNCTION + + +'' +' Return the size of a _UNSIGNED _INTEGER64 array +' +' @param _UNSIGNED _INTEGER64 ARRAY arr~&&() to get size of +' @return LONG size in bytes +' +FUNCTION ARR_UINT64.size&(arr~&&()) + ARR_UINT64.size& = LEN(arr~&&()) +END FUNCTION + + +'' +' Reverses the elements of a _UNSIGNED _INTEGER64 array +' +' @param _UNSIGNED _INTEGER64 ARRAY source_arr~&&() to reverse +' @param _UNSIGNED _INTEGER64 ARRAY dest_arr~&&() to store reversed array in +' +SUB ARR_UINT64.reverse(source_arr~&&(), dest_arr~&&()) + DIM AS LONG lb, ub, i, n + lb& = LBOUND(source_arr~&&) : ub& = UBOUND(source_arr~&&) + REDIM dest_arr(0 TO (ub& - lb&)) AS _UNSIGNED _INTEGER64 + n& = 0 + FOR i& = ub& TO lb& STEP -1 + dest_arr~&&(n&) = source_arr~&&(i&) + n& = n& + 1 + NEXT i& +END SUB + + +'' +' Returns a random uint64 from a _UNSIGNED _INTEGER64 array +' +' @param _UNSIGNED _INTEGER64 ARRAY arr~&&() array to get random element from +' @return _UNSIGNED _INTEGER64 random element +' +FUNCTION ARR_UINT64.random~&&(arr~&&()) + DIM AS LONG lb, ub + lb& = LBOUND(arr~&&) : ub& = UBOUND(arr~&&) + RANDOMIZE TIMER + ARR_UINT64.random~&& = arr~&&(INT(RND * (ub& - lb&)) + 1) +END FUNCTION + + +'' +' Returns the sum of all elements in a _UNSIGNED _INTEGER64 array +' +' @param _UNSIGNED _INTEGER64 ARRAY arr~&&() array to get some for +' @return LONG sum of all elements +' +FUNCTION ARR_UINT64.sum&(arr~&&()) + DIM AS LONG lb, ub, i + DIM sum AS LONG + lb& = LBOUND(arr~&&) : ub& = UBOUND(arr~&&) + FOR i& = lb& TO ub& + sum& = sum& + arr~&&(i&) + NEXT i& + ARR_UINT64.sum& = sum& +END FUNCTION + + +'' +' Returns the average value of elements in a _UNSIGNED _INTEGER64 array +' +' @param _UNSIGNED _INTEGER64 ARRAY arr~&&() array to get average for +' @return LONG average of elements +' +FUNCTION ARR_UINT64.avg&(arr~&&()) + DIM AS LONG lb, ub, i + DIM sum AS LONG + lb& = LBOUND(arr~&&) : ub& = UBOUND(arr~&&) + FOR i& = lb& TO ub& + sum& = sum& + arr~&&(i&) + NEXT i& + ARR_UINT64.avg& = sum& / (ub& - lb&) +END FUNCTION + + +'' +' Shuffle the elements of a _UNSIGNED _INTEGER64 array +' +' @param _UNSIGNED _INTEGER64 ARRAY source_arr~&&() to shuffle +' @param _UNSIGNED _INTEGER64 ARRAY dest_arr~&&() to store shuffled array in +' +SUB ARR_UINT64.shuffle(source_arr~&&(), dest_arr~&&()) + DIM AS LONG lb, ub, i, count + lb& = LBOUND(source_arr~&&) : ub& = UBOUND(source_arr~&&) + count& = ub& - lb& + REDIM dest_arr(0 TO count&) AS _UNSIGNED _INTEGER64 + CALL ARR_UINT64.copy(source_arr~&&(), dest_arr~&&()) + RANDOMIZE TIMER + FOR i& = 0 TO count& + SWAP dest_arr~&&(i&), dest_arr~&&(lb& + RND * (ub& - lb&)) + NEXT i& +END SUB + + +'' +' Makes a _UNSIGNED _INTEGER64 array contain only unique values +' +' @param _UNSIGNED _INTEGER64 ARRAY source_arr~&&() array to get uniques for +' @param _UNSIGNED _INTEGER64 ARRAY dest_arr~&&() array to store uniques in +' +SUB ARR_UINT64.unique(source_arr~&&(), dest_arr~&&()) + DIM AS LONG lb, ub, i + DIM tmp AS _UNSIGNED _INTEGER64 + lb& = LBOUND(source_arr~&&) : ub& = UBOUND(source_arr~&&) + DIM work_arr(0) AS _UNSIGNED _INTEGER64 + FOR i& = lb& TO ub& + IF NOT ARR_UINT64.in%(work_arr~&&(), source_arr~&&(i&)) THEN + CALL ARR_UINT64.push(work_arr~&&(), source_arr~&&(i&)) + END IF + NEXT i& + CALL ARR_UINT64.shift(work_arr~&&(), tmp~&&) + CALL ARR_UINT64.copy(work_arr~&&(), dest_arr~&&()) +END SUB + + +'' +' Filters a _UNSIGNED _INTEGER64 array to only elements greater than value +' +' @param _UNSIGNED _INTEGER64 ARRAY source_arr~&&() array to work on +' @param _UNSIGNED _INTEGER64 ARRAY dest_arr~&&() array to store in +' @param _UNSIGNED _INTEGER64 value~&& to be greater than to be returned +' +SUB ARR_UINT64.gt(source_arr~&&(), dest_arr~&&(), value~&&) + DIM AS LONG lb, ub, i + DIM tmp AS _UNSIGNED _INTEGER64 + lb& = LBOUND(source_arr~&&) : ub& = UBOUND(source_arr~&&) + DIM work_arr(0) AS _UNSIGNED _INTEGER64 + FOR i& = lb& TO ub& + IF source_arr~&&(i&) > value~&& THEN + CALL ARR_UINT64.push(work_arr~&&(), source_arr~&&(i&)) + END IF + NEXT i& + CALL ARR_UINT64.shift(work_arr~&&(), tmp~&&) + CALL ARR_UINT64.copy(work_arr~&&(), dest_arr~&&()) +END SUB + + +'' +' Filters a _UNSIGNED _INTEGER64 array to only elements greater than or equal to value +' +' @param _UNSIGNED _INTEGER64 ARRAY source_arr~&&() array to work on +' @param _UNSIGNED _INTEGER64 ARRAY dest_arr~&&() array to store in +' @param _UNSIGNED _INTEGER64 value~&& to be greater than or equal to be returned +' +SUB ARR_UINT64.gte(source_arr~&&(), dest_arr~&&(), value~&&) + DIM AS LONG lb, ub, i + DIM tmp AS _UNSIGNED _INTEGER64 + lb& = LBOUND(source_arr~&&) : ub& = UBOUND(source_arr~&&) + DIM work_arr(0) AS _UNSIGNED _INTEGER64 + FOR i& = lb& TO ub& + IF source_arr~&&(i&) >= value~&& THEN + CALL ARR_UINT64.push(work_arr~&&(), source_arr~&&(i&)) + END IF + NEXT i& + CALL ARR_UINT64.shift(work_arr~&&(), tmp~&&) + CALL ARR_UINT64.copy(work_arr~&&(), dest_arr~&&()) +END SUB + + +'' +' Filters a _UNSIGNED _INTEGER64 array to only elements less than value +' +' @param _UNSIGNED _INTEGER64 ARRAY source_arr~&&() array to work on +' @param _UNSIGNED _INTEGER64 ARRAY dest_arr~&&() array to store in +' @param _UNSIGNED _INTEGER64 value~&& to be less than to be returned +' +SUB ARR_UINT64.lt(source_arr~&&(), dest_arr~&&(), value~&&) + DIM AS LONG lb, ub, i + DIM tmp AS _UNSIGNED _INTEGER64 + lb& = LBOUND(source_arr~&&) : ub& = UBOUND(source_arr~&&) + DIM work_arr(0) AS _UNSIGNED _INTEGER64 + FOR i& = lb& TO ub& + IF source_arr~&&(i&) < value~&& THEN + CALL ARR_UINT64.push(work_arr~&&(), source_arr~&&(i&)) + END IF + NEXT i& + CALL ARR_UINT64.shift(work_arr~&&(), tmp~&&) + CALL ARR_UINT64.copy(work_arr~&&(), dest_arr~&&()) +END SUB + + +'' +' Filters a _UNSIGNED _INTEGER64 array to only elements less than or equal to value +' +' @param _UNSIGNED _INTEGER64 ARRAY source_arr~&&() array to work on +' @param _UNSIGNED _INTEGER64 ARRAY dest_arr~&&() array to store in +' @param _UNSIGNED _INTEGER64 value~&& to be less than or equal to be returned +' +SUB ARR_UINT64.lte(source_arr~&&(), dest_arr~&&(), value~&&) + DIM AS LONG lb, ub, i + DIM tmp AS _UNSIGNED _INTEGER64 + lb& = LBOUND(source_arr~&&) : ub& = UBOUND(source_arr~&&) + DIM work_arr(0) AS _UNSIGNED _INTEGER64 + FOR i& = lb& TO ub& + IF source_arr~&&(i&) <= value~&& THEN + CALL ARR_UINT64.push(work_arr~&&(), source_arr~&&(i&)) + END IF + NEXT i& + CALL ARR_UINT64.shift(work_arr~&&(), tmp~&&) + CALL ARR_UINT64.copy(work_arr~&&(), dest_arr~&&()) +END SUB + + +'' +' Finds and replaces values across all elements in a _UNSIGNED _INTEGER64 ARRAY +' +' @param _UNSIGNED _INTEGER64 ARRAY arr~&&() to check in +' @param _UNSIGNED _INTEGER64 find~&& value to find +' @param _UNSIGNED _INTEGER64 replace~&& value to replace with if found +' +SUB ARR_UINT64.replace(arr~&&(), find~&&, replace~&&) + DIM AS LONG lb, ub, i + lb& = LBOUND(arr~&&) : ub& = UBOUND(arr~&&) + FOR i& = lb& TO ub& + IF arr~&&(i&) = find~&& THEN + arr~&&(i&) = replace~&& + END IF + NEXT i& +END SUB + + +'' +' Inserts a new element into _UNSIGNED _INTEGER64 array after index +' +' @param _UNSIGNED _INTEGER64 ARRAY arr~&&() array to work on +' @param _UNSIGNED _INTEGER64 value~&& to insert +' @param INTEGER index% of element to insert at +' +SUB ARR_UINT64.insert(arr~&&(), value~&&, index%) + DIM AS LONG lb, ub, i + DIM tmp AS _UNSIGNED _INTEGER64 + DIM work_arr(0) AS _UNSIGNED _INTEGER64 + lb& = LBOUND(arr~&&) : ub& = UBOUND(arr~&&) + IF index% >= lb& AND index% <= ub& THEN + ' build new array up to index + FOR i& = lb& TO index% + CALL ARR_UINT64.push(work_arr~&&(), arr~&&(i&)) + NEXT i& + ' insert new element + CALL ARR_UINT64.push(work_arr~&&(), value~&&) + ' finish building array from index + 1 + FOR i& = index% + 1 TO ub& + CALL ARR_UINT64.push(work_arr~&&(), arr~&&(i&)) + NEXT i& + CALL ARR_UINT64.shift(work_arr~&&(), tmp~&&) + CALL ARR_UINT64.copy(work_arr~&&(), arr~&&()) + END IF +END SUB + + +'' +' Removes element from a _UNSIGNED _INTEGER64 array by element index +' +' @param _UNSIGNED _INTEGER64 ARRAY arr~&&() array to work on +' @param INTEGER index% of element to remove +' +SUB ARR_UINT64.remove(arr~&&(), index%) + DIM AS LONG lb, ub, i + DIM tmp AS _UNSIGNED _INTEGER64 + DIM work_arr(0) AS _UNSIGNED _INTEGER64 + lb& = LBOUND(arr~&&) : ub& = UBOUND(arr~&&) + IF index% >= lb& AND index% <= ub& THEN + ' build new array up to index + FOR i& = lb& TO index% - 1 + CALL ARR_UINT64.push(work_arr~&&(), arr~&&(i&)) + NEXT i& + ' skip elements + FOR i& = index% + 1 TO ub& + CALL ARR_UINT64.push(work_arr~&&(), arr~&&(i&)) + NEXT i& + CALL ARR_UINT64.shift(work_arr~&&(), tmp~&&) + CALL ARR_UINT64.copy(work_arr~&&(), arr~&&()) + END IF +END SUB + + +'' +' Filters a _UNSIGNED _INTEGER64 array to only elements that have odd values +' +' @param _UNSIGNED _INTEGER64 ARRAY source_arr~&&() array to work on +' @param _UNSIGNED _INTEGER64 ARRAY dest_arr~&&() array to store in +' +SUB ARR_UINT64.odd(source_arr~&&(), dest_arr~&&()) + DIM AS LONG lb, ub, i + DIM tmp AS _UNSIGNED _INTEGER64 + lb& = LBOUND(source_arr~&&) : ub& = UBOUND(source_arr~&&) + DIM work_arr(0) AS _UNSIGNED _INTEGER64 + FOR i& = lb& TO ub& + IF source_arr~&&(i&) MOD 2 <> 0 THEN + CALL ARR_UINT64.push(work_arr~&&(), source_arr~&&(i&)) + END IF + NEXT i& + CALL ARR_UINT64.shift(work_arr~&&(), tmp~&&) + CALL ARR_UINT64.copy(work_arr~&&(), dest_arr~&&()) +END SUB + + +'' +' Filters a _UNSIGNED _INTEGER64 array to only elements that have even values +' +' @param _UNSIGNED _INTEGER64 ARRAY source_arr~&&() array to work on +' @param _UNSIGNED _INTEGER64 ARRAY dest_arr~&&() array to store in +' +SUB ARR_UINT64.even(source_arr~&&(), dest_arr~&&()) + DIM AS LONG lb, ub, i + DIM tmp AS _UNSIGNED _INTEGER64 + lb& = LBOUND(source_arr~&&) : ub& = UBOUND(source_arr~&&) + DIM work_arr(0) AS _UNSIGNED _INTEGER64 + FOR i& = lb& TO ub& + IF source_arr~&&(i&) MOD 2 = 0 THEN + CALL ARR_UINT64.push(work_arr~&&(), source_arr~&&(i&)) + END IF + NEXT i& + CALL ARR_UINT64.shift(work_arr~&&(), tmp~&&) + CALL ARR_UINT64.copy(work_arr~&&(), dest_arr~&&()) +END SUB + + +'' +' Filters a _UNSIGNED _INTEGER64 array to only elements that have values evenly divisible by divisor +' +' @param _UNSIGNED _INTEGER64 ARRAY source_arr~&&() array to work on +' @param _UNSIGNED _INTEGER64 ARRAY dest_arr~&&() array to store in +' @param _UNSIGNED _INTEGER64 divisor~&& for modulo +' +SUB ARR_UINT64.mod(source_arr~&&(), dest_arr~&&(), divisor~&&) + DIM AS LONG lb, ub, i + DIM tmp AS _UNSIGNED _INTEGER64 + lb& = LBOUND(source_arr~&&) : ub& = UBOUND(source_arr~&&) + DIM work_arr(0) AS _UNSIGNED _INTEGER64 + FOR i& = lb& TO ub& + IF source_arr~&&(i&) MOD divisor~&& = 0 THEN + CALL ARR_UINT64.push(work_arr~&&(), source_arr~&&(i&)) + END IF + NEXT i& + CALL ARR_UINT64.shift(work_arr~&&(), tmp~&&) + CALL ARR_UINT64.copy(work_arr~&&(), dest_arr~&&()) +END SUB + + +'' +' Filters a _UNSIGNED _INTEGER64 array to only elements between min and max +' +' @param _UNSIGNED _INTEGER64 ARRAY source_arr~&&() array to work on +' @param _UNSIGNED _INTEGER64 ARRAY dest_arr~&&() array to store in +' @param _UNSIGNED _INTEGER64 min~&& to be greater than or equal to be returned +' @param _UNSIGNED _INTEGER64 max~&& to be less than or equal to be returned +' +SUB ARR_UINT64.between(source_arr~&&(), dest_arr~&&(), min~&&, max~&&) + DIM AS LONG lb, ub, i + DIM tmp AS _UNSIGNED _INTEGER64 + lb& = LBOUND(source_arr~&&) : ub& = UBOUND(source_arr~&&) + DIM work_arr(0) AS _UNSIGNED _INTEGER64 + FOR i& = lb& TO ub& + IF source_arr~&&(i&) >= min~&& _ + AND source_arr~&&(i&) <= max~&& THEN + CALL ARR_UINT64.push(work_arr~&&(), source_arr~&&(i&)) + END IF + NEXT i& + CALL ARR_UINT64.shift(work_arr~&&(), tmp~&&) + CALL ARR_UINT64.copy(work_arr~&&(), dest_arr~&&()) +END SUB + + +'' +' Sorts _UNSIGNED _INTEGER64 array in ascending order +' +' @param _UNSIGNED _INTEGER64 ARRAY source_arr~&&() array to sort +' @param _UNSIGNED _INTEGER64 ARRAY dest_arr~&&() array to store sorted in +' +SUB ARR_UINT64.sort(source_arr~&&(), dest_arr~&&()) + DIM AS LONG lb, ub, count + lb& = LBOUND(source_arr~&&) : ub& = UBOUND(source_arr~&&) + count& = ub& - lb& + REDIM dest_arr(0 TO count&) AS _UNSIGNED _INTEGER64 + CALL ARR_UINT64.copy(source_arr~&&(), dest_arr~&&()) + CALL ARR_UINT64.quicksort(dest_arr~&&(), lb&, ub&, 0) +END SUB + + +'' +' Sorts _UNSIGNED _INTEGER64 array in descending order +' +' @param _UNSIGNED _INTEGER64 ARRAY source_arr~&&() array to sort +' @param _UNSIGNED _INTEGER64 ARRAY dest_arr~&&() array to store sorted in +' +SUB ARR_UINT64.rsort(source_arr~&&(), dest_arr~&&()) + DIM AS LONG lb, ub, count + lb& = LBOUND(source_arr~&&) : ub& = UBOUND(source_arr~&&) + count& = ub& - lb& + REDIM dest_arr(0 TO count&) AS _UNSIGNED _INTEGER64 + CALL ARR_UINT64.copy(source_arr~&&(), dest_arr~&&()) + CALL ARR_UINT64.quicksort(dest_arr~&&(), lb&, ub&, 1) +END SUB + + +'' +' Quicksort array with pivot algorithm by logiclrd +' +' @link https://www.tek-tips.com/faqs.cfm?fid=336 +' @param _UNSIGNED _INTEGER64 ARRAY array~&&() to sort +' @param INTEGER start% of range to sort +' @param INTEGER finish% range of sort +' @param INTEGER order% to sort by (0 = asc / 1 = desc) +' +SUB ARR_UINT64.quicksort(arr~&&(), start%, finish%, order%) + DIM i AS LONG + DIM pivot AS INTEGER + DIM pivotvalue AS _UNSIGNED _INTEGER64 + + 'first, partition the array + pivot% = start% + pivotvalue~&& = arr~&&(pivot%) + FOR i& = start% + 1 TO finish% + IF order% = 0 THEN ' ascending order + IF arr~&&(i&) < pivotvalue~&& THEN + arr~&&(pivot%) = arr~&&(i&) + arr~&&(i&) = arr~&&(pivot% + 1) + arr~&&(pivot% + 1) = pivotvalue~&& + pivot% = pivot% + 1 + END IF + ELSEIF order% = 1 THEN ' descending order + IF arr~&&(i&) > pivotvalue~&& THEN + arr~&&(pivot%) = arr~&&(i&) + arr~&&(i&) = arr~&&(pivot% + 1) + arr~&&(pivot% + 1) = pivotvalue~&& + pivot% = pivot% + 1 + END IF + END IF + NEXT i& + + 'then, sort the subarrays to each side of the pivot + IF pivot% - start% >= 2 THEN + CALL ARR_UINT64.quicksort(arr~&&(), start%, pivot% - 1, order%) + END IF + IF finish% - pivot% >= 2 THEN + CALL ARR_UINT64.quicksort(arr~&&(), pivot% + 1, finish%, order%) + END IF +END SUB diff --git a/ARR/ARR_ULONG.BAS b/ARR/ARR_ULONG.BAS new file mode 100644 index 0000000..07573dc --- /dev/null +++ b/ARR/ARR_ULONG.BAS @@ -0,0 +1,820 @@ +'$DYNAMIC +$LET GJ_LIB_ARR_ULONG_BAS = 1 + +'' +' Slice an array from source to destination starting at index and count slices +' +' @param _UNSIGNED LONG() source_arr~& to slice from +' @param _UNSIGNED LONG() dest_arr~& to put slices into +' @param INTEGER start_idx% starting index to use as slice range +' @param INTEGER count% number of slices - if negative, backwards from index +' +SUB ARR_ULONG.slice(source_arr~&(), dest_arr~&(), start_idx%, count%) + DIM AS LONG ub, lb, i, n + lb& = LBOUND(source_arr~&) : ub& = UBOUND(source_arr~&) + IF start_idx% < lb& OR start_idx% + count% > ub& THEN EXIT SUB ' out of range + IF ub& - lb& < count% THEN EXIT SUB ' too many and not enough + REDIM dest_arr(0 TO ABS(count%)) AS _UNSIGNED LONG + IF SGN(count%) = -1 THEN + IF ((start_idx% - 1) - ABS(count%)) < 0 THEN EXIT SUB ' out of range + n& = 0 + FOR i& = (start_idx% - 1) TO ((start_idx% - 1) - ABS(count%)) STEP -1 + dest_arr~&(n&) = source_arr~&(i&) + n& = n& + 1 + NEXT i& + ELSE + IF ((start_idx% + 1) + ABS(count%)) > (ub& - lb&) THEN EXIT SUB ' out of range + n& = 0 + FOR i& = start_idx% + 1 TO ((start_idx% + 1) + count%) STEP 1 + dest_arr~&(n&) = source_arr~&(i&) + n& = n& + 1 + NEXT i& + END IF +END SUB + + +'' +' Push a ulong onto the end of a _UNSIGNED LONG array +' +' @param _UNSIGNED LONG arr~&() array to push into +' @param _UNSIGNED LONG value~& of byte to push +' +SUB ARR_ULONG.push(arr~&(), value~&) + DIM AS LONG ub, lb + lb& = LBOUND(arr~&) : ub& = UBOUND(arr~&) + REDIM _PRESERVE arr(lb& TO (ub& + 1)) AS _UNSIGNED LONG + arr~&(ub& + 1) = value~& +END SUB + + +'' +' Pop a ulong from the end of a _UNSIGNED LONG array +' +' @param _UNSIGNED LONG arr~&() array to pop from +' @param _UNSIGNED LONG var~& of ulong to store popped ulong +' +SUB ARR_ULONG.pop(arr~&(), var~&) + DIM AS LONG ub, lb + lb& = LBOUND(arr~&) : ub& = UBOUND(arr~&) + var~& = arr~&(ub&) + REDIM _PRESERVE arr(lb& TO (ub& - 1)) AS _UNSIGNED LONG +END SUB + + +'' +' Pop a ulong from the beginning of a _UNSIGNED LONG array +' +' @param _UNSIGNED LONG arr~&() array to pop from +' @param _UNSIGNED LONG var~& of ulong to store popped ulong +' +SUB ARR_ULONG.shift(arr~&(), var~&) + DIM AS LONG ub, lb, i + lb& = LBOUND(arr~&) : ub& = UBOUND(arr~&) + var~& = arr~&(lb&) + FOR i& = lb& TO ub& - 1 + arr~&(i&) = arr~&(i& + 1) + NEXT i& + REDIM _PRESERVE arr(lb& + 1 TO ub&) AS _UNSIGNED LONG +END SUB + + +'' +' Copy an array of ULONGs to another _UNSIGNED LONG array +' +' @param _UNSIGNED LONG ARRAY source_arr~&() source array to copy +' @param _UNSIGNED LONG ARRAY dest_arr~&() dest array to copy into +' +SUB ARR_ULONG.copy(source_arr~&(), dest_arr~&()) + DIM AS LONG ub, lb, i + lb& = LBOUND(source_arr~&) : ub& = UBOUND(source_arr~&) + REDIM dest_arr(lb& TO ub&) AS _UNSIGNED LONG + FOR i& = lb& TO ub& + dest_arr~&(i&) = source_arr~&(i&) + NEXT i& +END SUB + + +'' +' Push a ulong into the beginning of a _UNSIGNED LONG array +' +' @param _UNSIGNED LONG arr~&() array to push into +' @param _UNSIGNED LONG value~& of ulong to push +' +SUB ARR_ULONG.unshift(arr~&(), value~&) + DIM AS LONG ub, lb, i + lb& = LBOUND(arr~&) : ub& = UBOUND(arr~&) + DIM work(lb& TO ub&) AS _UNSIGNED LONG + CALL ARR_ULONG.copy(arr~&(), work~&()) + REDIM _PRESERVE arr(lb& TO (ub& + 1)) AS _UNSIGNED LONG + FOR i& = lb& + 1 TO ub& + 1 + arr~&(i&) = work~&(i& - 1) + NEXT i& + arr~&(lb&) = value~& +END SUB + + +'' +' Joins an array of ULONGs as a string +' +' @param _UNSIGNED LONG ARRAY arr~&() to get as a string +' @param STRING s$ to store stringified array in +' +SUB ARR_ULONG.join(arr~&(), s$) + DIM AS LONG ub, lb, i + s$ = "" + lb& = LBOUND(arr~&) : ub& = UBOUND(arr~&) + FOR i& = lb& TO ub& + s$ = s$ + _TRIM$(STR$(arr~&(i&))) + ", " + NEXT i& + ' remove trailing comma + s$ = MID$(s$, 1, (LEN(s$)-LEN(", "))) +END SUB + + +'' +' Create a new _UNSIGNED LONG array using string of ulongs seperated by commas +' +' @param _UNSIGNED LONG ARRAY arr~&() to store the ulongs in +' @param STRING s$ string of comma separated ulongs +' +SUB ARR_ULONG.new(arr~&(), s$) + DIM AS LONG i, count + DIM t AS STRING + count& = 0 + FOR i& = 1 TO LEN(s$) + IF ASC(s$, i&) = ASC(",") THEN count& = count& + 1 + NEXT i& + REDIM arr(0 TO count&) AS _UNSIGNED LONG + IF count& = 0 THEN + arr~&(0) = VAL(s$) + EXIT SUB + END IF + count& = 0 + FOR i& = 1 TO LEN(s$) + t$ = t$ + CHR$(ASC(s$, i&)) + IF ASC(s$, i&) = ASC(",") OR i& = LEN(s$) THEN + arr~&(count&) = VAL(t$) + count& = count& + 1 + t$ = "" + END IF + NEXT i& +END SUB + + +'' +' Return the visually longest element of a _UNSIGNED LONG array +' +' @param _UNSIGNED LONG ARRAY arr~&() to check in +' @return _UNSIGNED LONG value of visually longest element +' +FUNCTION ARR_ULONG.longest~&(arr~&()) + DIM AS LONG lb, ub, i, res, lw + lb& = LBOUND(arr~&) : ub& = UBOUND(arr~&) + res& = 0 : lw& = 0 + FOR i& = lb& TO ub& + IF LEN(_TRIM$(STR$(arr~&(i&)))) > lw& THEN + lw& = LEN(_TRIM$(STR$(arr~&(i&)))) + res& = i& + END IF + NEXT i& + ARR_ULONG.longest~& = arr~&(res&) +END FUNCTION + + +'' +' Perform some math on every element of a _UNSIGNED LONG array +' +' @param _UNSIGNED LONG ARRAY source_arr~&() to do math on +' @param _UNSIGNED LONG ARRAY dest_arr~&() to store results in +' @param STRING op$ one of: +' and or xor shl shr +' "+", "-", "*", "\", "&&", "||", "!!", "<<", ">>" +' @param _UNSIGNED LONG value~& to use for operand +' +SUB ARR_ULONG.math(source_arr~&(), dest_arr~&(), op$, value~&) + DIM AS LONG lb, ub, i + lb& = LBOUND(source_arr~&) : ub& = UBOUND(source_arr~&) + REDIM dest_arr(lb& TO ub&) AS _UNSIGNED LONG + FOR i& = lb& TO ub& + SELECT CASE op$ + CASE "+": + dest_arr~&(i&) = source_arr~&(i&) + value~& + CASE "-": + dest_arr~&(i&) = source_arr~&(i&) - value~& + CASE "*": + dest_arr~&(i&) = source_arr~&(i&) * value~& + CASE "\": + IF value~& > 0 THEN + dest_arr~&(i&) = source_arr~&(i&) \ value~& + END IF + CASE "&&": + dest_arr~&(i&) = source_arr~&(i&) AND value~& + CASE "||": + dest_arr~&(i&) = source_arr~&(i&) OR value~& + CASE "!!": + dest_arr~&(i&) = source_arr~&(i&) XOR value~& + CASE "<<": + dest_arr~&(i&) = _SHL(source_arr~&(i&), value~&) + CASE ">>": + dest_arr~&(i&) = _SHR(source_arr~&(i&), value~&) + END SELECT + NEXT i& +END SUB + + +'' +' Return the minimum element value in _UNSIGNED LONG array +' +' @param _UNSIGNED LONG ARRAY arr~&() to check in +' @return _UNSIGNED LONG minimum value found +' +FUNCTION ARR_ULONG.min~&(arr~&()) + DIM AS LONG lb, ub, i + DIM AS _UNSIGNED LONG s + lb& = LBOUND(arr~&) : ub& = UBOUND(arr~&) + s~& = 127 + FOR i& = lb& TO ub& + IF arr~&(i&) < s~& THEN + s~& = arr~&(i&) + END IF + NEXT i& + ARR_ULONG.min~& = s~& +END FUNCTION + + +'' +' Return the maximum element value in _UNSIGNED LONG array +' +' @param _UNSIGNED LONG ARRAY arr~&() to check in +' @return _UNSIGNED LONG maximum value found +' +FUNCTION ARR_ULONG.max~&(arr~&()) + DIM AS LONG lb, ub, i + DIM AS _UNSIGNED LONG s + lb& = LBOUND(arr~&) : ub& = UBOUND(arr~&) + s~& = 0 + FOR i& = lb& TO ub& + IF arr~&(i&) > s~& THEN + s~& = arr~&(i&) + END IF + NEXT i& + ARR_ULONG.max~& = s~& +END FUNCTION + + +'' +' Return the visually shortest element of a _UNSIGNED LONG array +' +' @param _UNSIGNED LONG ARRAY arr~&() to check in +' @return _UNSIGNED LONG value of visually shortest element +' +FUNCTION ARR_ULONG.shortest~&(arr~&()) + DIM AS LONG lb, ub, i, res, sw + lb& = LBOUND(arr~&) : ub& = UBOUND(arr~&) + res& = 0 : sw& = 0 + FOR i& = lb& TO ub& + IF LEN(_TRIM$(STR$(arr~&(i&)))) < sw& THEN + sw& = LEN(_TRIM$(STR$(arr~&(i&)))) + res& = i& + END IF + NEXT i& + ARR_ULONG.shortest~& = arr~&(res&) +END FUNCTION + + +'' +' Return the first element of a _UNSIGNED LONG array +' +' @param _UNSIGNED LONG ARRAY arr~&() to check in +' @return _UNSIGNED LONG value of first element +' +FUNCTION ARR_ULONG.first~&(arr~&()) + ARR_ULONG.first~& = arr~&(LBOUND(arr~&)) +END FUNCTION + + +'' +' Return the last element of a _UNSIGNED LONG array +' +' @param _UNSIGNED LONG ARRAY arr~&() to check in +' @return _UNSIGNED LONG value of last element +' +FUNCTION ARR_ULONG.last~&(arr~&()) + ARR_ULONG.last~& = arr~&(UBOUND(arr~&)) +END FUNCTION + + +'' +' Return every nth array element of a _UNSIGNED LONG array +' +' @param _UNSIGNED LONG ARRAY source_arr~&() to get from +' @param _UNSIGNED LONG ARRAY dest_arr~&() to store in +' @param INTEGER nth% element +' +SUB ARR_ULONG.nth(source_arr~&(), dest_arr~&(), nth%) + DIM AS LONG lb, ub, i, n + lb& = LBOUND(source_arr~&) : ub& = UBOUND(source_arr~&) + n& = (ub& - lb&) \ nth% + REDIM dest_arr(n&) AS _UNSIGNED LONG + n& = 0 + FOR i& = lb& to ub& + IF i& MOD nth% = 0 THEN + dest_arr~&(n&) = source_arr~&(i&) + n& = n& + 1 + END IF + NEXT i& +END SUB + + +'' +' Checks if value exists in _UNSIGNED LONG array +' +' @param _UNSIGNED LONG ARRAY arr~&() to check in +' @param _UNSIGNED LONG value~& value to check for +' @return INTEGER -1 if found or 0 if not found +' +FUNCTION ARR_ULONG.in%(arr~&(), value~&) + DIM AS LONG lb, ub, i + lb& = LBOUND(arr~&) : ub& = UBOUND(arr~&) + FOR i& = lb& TO ub& + IF arr~&(i&) = value~& THEN + ARR_ULONG.in% = -1 + EXIT FUNCTION + END IF + NEXT i& + ARR_ULONG.in% = 0 +END FUNCTION + + +'' +' Checks if value exists in _UNSIGNED LONG array and returns index if found +' +' @param _UNSIGNED LONG ARRAY arr~&() to check in +' @param _UNSIGNED LONG value~& value to check for +' @return INTEGER index of element if found or -1 if not found +' +FUNCTION ARR_ULONG.find%(arr~&(), value~&) + DIM AS LONG lb, ub, i + lb& = LBOUND(arr~&) : ub& = UBOUND(arr~&) + FOR i& = lb& TO ub& + IF arr~&(i&) = value~& THEN + ARR_ULONG.find% = i& + EXIT FUNCTION + END IF + NEXT i& + ARR_ULONG.find% = -1 +END FUNCTION + + +'' +' Return the number of elements in a _UNSIGNED LONG array +' +' @param _UNSIGNED LONG ARRAY arr~&() to count +' @return INTEGER number of elements +' +FUNCTION ARR_ULONG.count&(arr~&()) + ARR_ULONG.count& = UBOUND(arr~&) - LBOUND(arr~&) +END FUNCTION + + +'' +' Return the size of a _UNSIGNED LONG array +' +' @param _UNSIGNED LONG ARRAY arr~&() to get size of +' @return LONG size in bytes +' +FUNCTION ARR_ULONG.size&(arr~&()) + ARR_ULONG.size& = LEN(arr~&()) +END FUNCTION + + +'' +' Reverses the elements of a _UNSIGNED LONG array +' +' @param _UNSIGNED LONG ARRAY source_arr~&() to reverse +' @param _UNSIGNED LONG ARRAY dest_arr~&() to store reversed array in +' +SUB ARR_ULONG.reverse(source_arr~&(), dest_arr~&()) + DIM AS LONG lb, ub, i, n + lb& = LBOUND(source_arr~&) : ub& = UBOUND(source_arr~&) + REDIM dest_arr(0 TO (ub& - lb&)) AS _UNSIGNED LONG + n& = 0 + FOR i& = ub& TO lb& STEP -1 + dest_arr~&(n&) = source_arr~&(i&) + n& = n& + 1 + NEXT i& +END SUB + + +'' +' Returns a random ulong from a _UNSIGNED LONG array +' +' @param _UNSIGNED LONG ARRAY arr~&() array to get random element from +' @return _UNSIGNED LONG random element +' +FUNCTION ARR_ULONG.random~&(arr~&()) + DIM AS LONG lb, ub + lb& = LBOUND(arr~&) : ub& = UBOUND(arr~&) + RANDOMIZE TIMER + ARR_ULONG.random~& = arr~&(INT(RND * (ub& - lb&)) + 1) +END FUNCTION + + +'' +' Returns the sum of all elements in a _UNSIGNED LONG array +' +' @param _UNSIGNED LONG ARRAY arr~&() array to get some for +' @return LONG sum of all elements +' +FUNCTION ARR_ULONG.sum&(arr~&()) + DIM AS LONG lb, ub, i + DIM sum AS LONG + lb& = LBOUND(arr~&) : ub& = UBOUND(arr~&) + FOR i& = lb& TO ub& + sum& = sum& + arr~&(i&) + NEXT i& + ARR_ULONG.sum& = sum& +END FUNCTION + + +'' +' Returns the average value of elements in a _UNSIGNED LONG array +' +' @param _UNSIGNED LONG ARRAY arr~&() array to get average for +' @return LONG average of elements +' +FUNCTION ARR_ULONG.avg&(arr~&()) + DIM AS LONG lb, ub, i + DIM sum AS LONG + lb& = LBOUND(arr~&) : ub& = UBOUND(arr~&) + FOR i& = lb& TO ub& + sum& = sum& + arr~&(i&) + NEXT i& + ARR_ULONG.avg& = sum& / (ub& - lb&) +END FUNCTION + + +'' +' Shuffle the elements of a _UNSIGNED LONG array +' +' @param _UNSIGNED LONG ARRAY source_arr~&() to shuffle +' @param _UNSIGNED LONG ARRAY dest_arr~&() to store shuffled array in +' +SUB ARR_ULONG.shuffle(source_arr~&(), dest_arr~&()) + DIM AS LONG lb, ub, i, count + lb& = LBOUND(source_arr~&) : ub& = UBOUND(source_arr~&) + count& = ub& - lb& + REDIM dest_arr(0 TO count&) AS _UNSIGNED LONG + CALL ARR_ULONG.copy(source_arr~&(), dest_arr~&()) + RANDOMIZE TIMER + FOR i& = 0 TO count& + SWAP dest_arr~&(i&), dest_arr~&(lb& + RND * (ub& - lb&)) + NEXT i& +END SUB + + +'' +' Makes a _UNSIGNED LONG array contain only unique values +' +' @param _UNSIGNED LONG ARRAY source_arr~&() array to get uniques for +' @param _UNSIGNED LONG ARRAY dest_arr~&() array to store uniques in +' +SUB ARR_ULONG.unique(source_arr~&(), dest_arr~&()) + DIM AS LONG lb, ub, i + DIM tmp AS _UNSIGNED LONG + lb& = LBOUND(source_arr~&) : ub& = UBOUND(source_arr~&) + DIM work_arr(0) AS _UNSIGNED LONG + FOR i& = lb& TO ub& + IF NOT ARR_ULONG.in%(work_arr~&(), source_arr~&(i&)) THEN + CALL ARR_ULONG.push(work_arr~&(), source_arr~&(i&)) + END IF + NEXT i& + CALL ARR_ULONG.shift(work_arr~&(), tmp~&) + CALL ARR_ULONG.copy(work_arr~&(), dest_arr~&()) +END SUB + + +'' +' Filters a _UNSIGNED LONG array to only elements greater than value +' +' @param _UNSIGNED LONG ARRAY source_arr~&() array to work on +' @param _UNSIGNED LONG ARRAY dest_arr~&() array to store in +' @param _UNSIGNED LONG value~& to be greater than to be returned +' +SUB ARR_ULONG.gt(source_arr~&(), dest_arr~&(), value~&) + DIM AS LONG lb, ub, i + DIM tmp AS _UNSIGNED LONG + lb& = LBOUND(source_arr~&) : ub& = UBOUND(source_arr~&) + DIM work_arr(0) AS _UNSIGNED LONG + FOR i& = lb& TO ub& + IF source_arr~&(i&) > value~& THEN + CALL ARR_ULONG.push(work_arr~&(), source_arr~&(i&)) + END IF + NEXT i& + CALL ARR_ULONG.shift(work_arr~&(), tmp~&) + CALL ARR_ULONG.copy(work_arr~&(), dest_arr~&()) +END SUB + + +'' +' Filters a _UNSIGNED LONG array to only elements greater than or equal to value +' +' @param _UNSIGNED LONG ARRAY source_arr~&() array to work on +' @param _UNSIGNED LONG ARRAY dest_arr~&() array to store in +' @param _UNSIGNED LONG value~& to be greater than or equal to be returned +' +SUB ARR_ULONG.gte(source_arr~&(), dest_arr~&(), value~&) + DIM AS LONG lb, ub, i + DIM tmp AS _UNSIGNED LONG + lb& = LBOUND(source_arr~&) : ub& = UBOUND(source_arr~&) + DIM work_arr(0) AS _UNSIGNED LONG + FOR i& = lb& TO ub& + IF source_arr~&(i&) >= value~& THEN + CALL ARR_ULONG.push(work_arr~&(), source_arr~&(i&)) + END IF + NEXT i& + CALL ARR_ULONG.shift(work_arr~&(), tmp~&) + CALL ARR_ULONG.copy(work_arr~&(), dest_arr~&()) +END SUB + + +'' +' Filters a _UNSIGNED LONG array to only elements less than value +' +' @param _UNSIGNED LONG ARRAY source_arr~&() array to work on +' @param _UNSIGNED LONG ARRAY dest_arr~&() array to store in +' @param _UNSIGNED LONG value~& to be less than to be returned +' +SUB ARR_ULONG.lt(source_arr~&(), dest_arr~&(), value~&) + DIM AS LONG lb, ub, i + DIM tmp AS _UNSIGNED LONG + lb& = LBOUND(source_arr~&) : ub& = UBOUND(source_arr~&) + DIM work_arr(0) AS _UNSIGNED LONG + FOR i& = lb& TO ub& + IF source_arr~&(i&) < value~& THEN + CALL ARR_ULONG.push(work_arr~&(), source_arr~&(i&)) + END IF + NEXT i& + CALL ARR_ULONG.shift(work_arr~&(), tmp~&) + CALL ARR_ULONG.copy(work_arr~&(), dest_arr~&()) +END SUB + + +'' +' Filters a _UNSIGNED LONG array to only elements less than or equal to value +' +' @param _UNSIGNED LONG ARRAY source_arr~&() array to work on +' @param _UNSIGNED LONG ARRAY dest_arr~&() array to store in +' @param _UNSIGNED LONG value~& to be less than or equal to be returned +' +SUB ARR_ULONG.lte(source_arr~&(), dest_arr~&(), value~&) + DIM AS LONG lb, ub, i + DIM tmp AS _UNSIGNED LONG + lb& = LBOUND(source_arr~&) : ub& = UBOUND(source_arr~&) + DIM work_arr(0) AS _UNSIGNED LONG + FOR i& = lb& TO ub& + IF source_arr~&(i&) <= value~& THEN + CALL ARR_ULONG.push(work_arr~&(), source_arr~&(i&)) + END IF + NEXT i& + CALL ARR_ULONG.shift(work_arr~&(), tmp~&) + CALL ARR_ULONG.copy(work_arr~&(), dest_arr~&()) +END SUB + + +'' +' Finds and replaces values across all elements in a _UNSIGNED LONG ARRAY +' +' @param _UNSIGNED LONG ARRAY arr~&() to check in +' @param _UNSIGNED LONG find~& value to find +' @param _UNSIGNED LONG replace~& value to replace with if found +' +SUB ARR_ULONG.replace(arr~&(), find~&, replace~&) + DIM AS LONG lb, ub, i + lb& = LBOUND(arr~&) : ub& = UBOUND(arr~&) + FOR i& = lb& TO ub& + IF arr~&(i&) = find~& THEN + arr~&(i&) = replace~& + END IF + NEXT i& +END SUB + + +'' +' Inserts a new element into _UNSIGNED LONG array after index +' +' @param _UNSIGNED LONG ARRAY arr~&() array to work on +' @param _UNSIGNED LONG value~& to insert +' @param INTEGER index% of element to insert at +' +SUB ARR_ULONG.insert(arr~&(), value~&, index%) + DIM AS LONG lb, ub, i + DIM tmp AS _UNSIGNED LONG + DIM work_arr(0) AS _UNSIGNED LONG + lb& = LBOUND(arr~&) : ub& = UBOUND(arr~&) + IF index% >= lb& AND index% <= ub& THEN + ' build new array up to index + FOR i& = lb& TO index% + CALL ARR_ULONG.push(work_arr~&(), arr~&(i&)) + NEXT i& + ' insert new element + CALL ARR_ULONG.push(work_arr~&(), value~&) + ' finish building array from index + 1 + FOR i& = index% + 1 TO ub& + CALL ARR_ULONG.push(work_arr~&(), arr~&(i&)) + NEXT i& + CALL ARR_ULONG.shift(work_arr~&(), tmp~&) + CALL ARR_ULONG.copy(work_arr~&(), arr~&()) + END IF +END SUB + + +'' +' Removes element from a _UNSIGNED LONG array by element index +' +' @param _UNSIGNED LONG ARRAY arr~&() array to work on +' @param INTEGER index% of element to remove +' +SUB ARR_ULONG.remove(arr~&(), index%) + DIM AS LONG lb, ub, i + DIM tmp AS _UNSIGNED LONG + DIM work_arr(0) AS _UNSIGNED LONG + lb& = LBOUND(arr~&) : ub& = UBOUND(arr~&) + IF index% >= lb& AND index% <= ub& THEN + ' build new array up to index + FOR i& = lb& TO index% - 1 + CALL ARR_ULONG.push(work_arr~&(), arr~&(i&)) + NEXT i& + ' skip elements + FOR i& = index% + 1 TO ub& + CALL ARR_ULONG.push(work_arr~&(), arr~&(i&)) + NEXT i& + CALL ARR_ULONG.shift(work_arr~&(), tmp~&) + CALL ARR_ULONG.copy(work_arr~&(), arr~&()) + END IF +END SUB + + +'' +' Filters a _UNSIGNED LONG array to only elements that have odd values +' +' @param _UNSIGNED LONG ARRAY source_arr~&() array to work on +' @param _UNSIGNED LONG ARRAY dest_arr~&() array to store in +' +SUB ARR_ULONG.odd(source_arr~&(), dest_arr~&()) + DIM AS LONG lb, ub, i + DIM tmp AS _UNSIGNED LONG + lb& = LBOUND(source_arr~&) : ub& = UBOUND(source_arr~&) + DIM work_arr(0) AS _UNSIGNED LONG + FOR i& = lb& TO ub& + IF source_arr~&(i&) MOD 2 <> 0 THEN + CALL ARR_ULONG.push(work_arr~&(), source_arr~&(i&)) + END IF + NEXT i& + CALL ARR_ULONG.shift(work_arr~&(), tmp~&) + CALL ARR_ULONG.copy(work_arr~&(), dest_arr~&()) +END SUB + + +'' +' Filters a _UNSIGNED LONG array to only elements that have even values +' +' @param _UNSIGNED LONG ARRAY source_arr~&() array to work on +' @param _UNSIGNED LONG ARRAY dest_arr~&() array to store in +' +SUB ARR_ULONG.even(source_arr~&(), dest_arr~&()) + DIM AS LONG lb, ub, i + DIM tmp AS _UNSIGNED LONG + lb& = LBOUND(source_arr~&) : ub& = UBOUND(source_arr~&) + DIM work_arr(0) AS _UNSIGNED LONG + FOR i& = lb& TO ub& + IF source_arr~&(i&) MOD 2 = 0 THEN + CALL ARR_ULONG.push(work_arr~&(), source_arr~&(i&)) + END IF + NEXT i& + CALL ARR_ULONG.shift(work_arr~&(), tmp~&) + CALL ARR_ULONG.copy(work_arr~&(), dest_arr~&()) +END SUB + + +'' +' Filters a _UNSIGNED LONG array to only elements that have values evenly divisible by divisor +' +' @param _UNSIGNED LONG ARRAY source_arr~&() array to work on +' @param _UNSIGNED LONG ARRAY dest_arr~&() array to store in +' @param _UNSIGNED LONG divisor~& for modulo +' +SUB ARR_ULONG.mod(source_arr~&(), dest_arr~&(), divisor~&) + DIM AS LONG lb, ub, i + DIM tmp AS _UNSIGNED LONG + lb& = LBOUND(source_arr~&) : ub& = UBOUND(source_arr~&) + DIM work_arr(0) AS _UNSIGNED LONG + FOR i& = lb& TO ub& + IF source_arr~&(i&) MOD divisor~& = 0 THEN + CALL ARR_ULONG.push(work_arr~&(), source_arr~&(i&)) + END IF + NEXT i& + CALL ARR_ULONG.shift(work_arr~&(), tmp~&) + CALL ARR_ULONG.copy(work_arr~&(), dest_arr~&()) +END SUB + + +'' +' Filters a _UNSIGNED LONG array to only elements between min and max +' +' @param _UNSIGNED LONG ARRAY source_arr~&() array to work on +' @param _UNSIGNED LONG ARRAY dest_arr~&() array to store in +' @param _UNSIGNED LONG min~& to be greater than or equal to be returned +' @param _UNSIGNED LONG max~& to be less than or equal to be returned +' +SUB ARR_ULONG.between(source_arr~&(), dest_arr~&(), min~&, max~&) + DIM AS LONG lb, ub, i + DIM tmp AS _UNSIGNED LONG + lb& = LBOUND(source_arr~&) : ub& = UBOUND(source_arr~&) + DIM work_arr(0) AS _UNSIGNED LONG + FOR i& = lb& TO ub& + IF source_arr~&(i&) >= min~& _ + AND source_arr~&(i&) <= max~& THEN + CALL ARR_ULONG.push(work_arr~&(), source_arr~&(i&)) + END IF + NEXT i& + CALL ARR_ULONG.shift(work_arr~&(), tmp~&) + CALL ARR_ULONG.copy(work_arr~&(), dest_arr~&()) +END SUB + + +'' +' Sorts _UNSIGNED LONG array in ascending order +' +' @param _UNSIGNED LONG ARRAY source_arr~&() array to sort +' @param _UNSIGNED LONG ARRAY dest_arr~&() array to store sorted in +' +SUB ARR_ULONG.sort(source_arr~&(), dest_arr~&()) + DIM AS LONG lb, ub, count + lb& = LBOUND(source_arr~&) : ub& = UBOUND(source_arr~&) + count& = ub& - lb& + REDIM dest_arr(0 TO count&) AS _UNSIGNED LONG + CALL ARR_ULONG.copy(source_arr~&(), dest_arr~&()) + CALL ARR_ULONG.quicksort(dest_arr~&(), lb&, ub&, 0) +END SUB + + +'' +' Sorts _UNSIGNED LONG array in descending order +' +' @param _UNSIGNED LONG ARRAY source_arr~&() array to sort +' @param _UNSIGNED LONG ARRAY dest_arr~&() array to store sorted in +' +SUB ARR_ULONG.rsort(source_arr~&(), dest_arr~&()) + DIM AS LONG lb, ub, count + lb& = LBOUND(source_arr~&) : ub& = UBOUND(source_arr~&) + count& = ub& - lb& + REDIM dest_arr(0 TO count&) AS _UNSIGNED LONG + CALL ARR_ULONG.copy(source_arr~&(), dest_arr~&()) + CALL ARR_ULONG.quicksort(dest_arr~&(), lb&, ub&, 1) +END SUB + + +'' +' Quicksort array with pivot algorithm by logiclrd +' +' @link https://www.tek-tips.com/faqs.cfm?fid=336 +' @param _UNSIGNED LONG ARRAY array~&() to sort +' @param INTEGER start% of range to sort +' @param INTEGER finish% range of sort +' @param INTEGER order% to sort by (0 = asc / 1 = desc) +' +SUB ARR_ULONG.quicksort(arr~&(), start%, finish%, order%) + DIM i AS LONG + DIM pivot AS INTEGER + DIM pivotvalue AS _UNSIGNED LONG + + 'first, partition the array + pivot% = start% + pivotvalue~& = arr~&(pivot%) + FOR i& = start% + 1 TO finish% + IF order% = 0 THEN ' ascending order + IF arr~&(i&) < pivotvalue~& THEN + arr~&(pivot%) = arr~&(i&) + arr~&(i&) = arr~&(pivot% + 1) + arr~&(pivot% + 1) = pivotvalue~& + pivot% = pivot% + 1 + END IF + ELSEIF order% = 1 THEN ' descending order + IF arr~&(i&) > pivotvalue~& THEN + arr~&(pivot%) = arr~&(i&) + arr~&(i&) = arr~&(pivot% + 1) + arr~&(pivot% + 1) = pivotvalue~& + pivot% = pivot% + 1 + END IF + END IF + NEXT i& + + 'then, sort the subarrays to each side of the pivot + IF pivot% - start% >= 2 THEN + CALL ARR_ULONG.quicksort(arr~&(), start%, pivot% - 1, order%) + END IF + IF finish% - pivot% >= 2 THEN + CALL ARR_ULONG.quicksort(arr~&(), pivot% + 1, finish%, order%) + END IF +END SUB diff --git a/ARR/README.md b/ARR/README.md new file mode 100644 index 0000000..3eb0cb4 --- /dev/null +++ b/ARR/README.md @@ -0,0 +1,120 @@ +# [QB64_GJ_LIB](../README.md) +## GRYMMJACK'S ARRay Library + +A bunch of very handy stuff for arrays spanning all QB64 types. + +### USAGE for ARRay Library (separately) +```basic + +' ...your code here... + +'Insert at bottom of file: +'$INCLUDE:'path_to_GJ_LIB/ARR/ARR_STR.BAS' at the bottom of file +``` + +> Replace `ARR_STR.BAS` with any types you want to use, 1 line for each type. + + +## WHAT'S IN THE LIBRARY + +### TYPES SUPPORTED: +| TYPE | FILE TO INCLUDE | +|------|-----------------| +| `_BYTE` | [ARR_BYTE.BAS](ARR_BYTE.BAS) | +| `_UNSIGNED _BYTE` | [ARR_UBYTE.BAS](ARR_UBYTE.BAS) | +| `INTEGER` | [ARR_INT.BAS](ARR_INT.BAS) | +| `_UNSIGNED INTEGER` | [ARR_UINT.BAS](ARR_UINT.BAS) | +| `_INTEGER64` | [ARR_INT64.BAS](ARR_INT64.BAS) | +| `_UNSIGNED _INTEGER64` | [ARR_UINT64.BAS](ARR_UINT64.BAS) | +| `LONG` | [ARR_LONG.BAS](ARR_LONG.BAS) | +| `_UNSIGNED LONG` | [ARR_ULONG.BAS](ARR_ULONG.BAS) | +| `SINGLE` | [ARR_SNG.BAS](ARR_SNG.BAS) | +| `DOUBLE` | [ARR_DBL.BAS](ARR_DBL.BAS) | +| `_FLOAT` | [ARR_FLT.BAS](ARR_FLT.BAS) | +| `STRING` | [ARR_STR.BAS](ARR_STR.BAS) | + +> Every numeric type contains the following SUBs/FUNCTIONs +> e.g. `ARR_INT.slice` for the slice SUB for INTEGER type. + +### SUBS AND FUNCTIONS FOR NUMERIC TYPES: +| SUB / FUNCTION | NOTES | +|----------------|-------| +| `.slice` | Slice an array from source to destination starting at index and count slices | +| `.push` | Push a element onto the end of the array | +| `.pop` | Pop a element off the end of the array | +| `.shift` | Pop a element off the beginning of the array | +| `.unshift` | Push a element on the beginning of the array | +| `.copy` | Copy an array | +| `.join` | Return array contents as comma delimited string | +| `.new` | Create new array using comma delimited string | +| `.longest` | Return the longest element of an array | +| `.shortest` | Return the shortest element of an array | +| `.math` | Do math on every element of an array | +| `.min` | Return minimum element of an array | +| `.max` | Return maximum element of an array | +| `.first` | Return 1st element of an array | +| `.last` | Return last element of an array | +| `.nth` | Return every nth element of an array | +| `.in` | Determine if a value exists in an array | +| `.find` | Find a value in an array and return it's index | +| `.count` | Return the number of elements in an array | +| `.size` | Return the size in bytes of all elements in an array | +| `.reverse` | Reverse the index of elements in an array | +| `.random` | Return a random element from the array | +| `.sum` | Return the sum of all elements in an array | +| `.avg` | Return the average of all elements in an array | +| `.shuffle` | Randomize the indexes of all elements in an array | +| `.unique` | Return unique elements in an array | +| `.gt` | Return elements greater than (>) value in an array | +| `.gte` | Return elements greater than or equal (>=) value in an array | +| `.lt` | Return elements less than (<>=) value in an array | +| `.lte` | Return elements less than or equal (<>=) value in an array | +| `.replace` | Replace elements in array with replacement value | +| `.insert` | Insert element in an array at index | +| `.remove` | Remove element in an array at index | +| `.odd` | Return odd numbered indexed elements in an array | +| `.even` | Return even numbered indexed elements in an array | +| `.mod` | Return evenly divisible by n numbered indexed elements in an array | +| `.between` | Return elements between a start and end index in an array | +| `.sort` | Sort elements of an array in ascending order | +| `.rsort` | Sort elements of an array in desscending order | + +### SUBS AND FUNCTIONS FOR STRING TYPE: +| SUB / FUNCTION | NOTES | +|----------------|-------| +| `.slice` | Slice an array from source to destination starting at index and count slices | +| `.push` | Push a element onto the end of the array | +| `.pop` | Pop a element off the end of the array | +| `.shift` | Pop a element off the beginning of the array | +| `.unshift` | Push a element on the beginning of the array | +| `.copy` | Copy an array | +| `.join` | Return array contents as comma delimited string | +| `.new` | Create new array using comma delimited string | +| `.longest` | Return the longest element of an array | +| `.shortest` | Return the shortest element of an array | +| `.first` | Return 1st element of an array | +| `.last` | Return last element of an array | +| `.nth` | Return every nth element of an array | +| `.in` | Determine if a value exists in an array | +| `.find` | Find a value in an array and return it's index | +| `.count` | Return the number of elements in an array | +| `.size` | Return the size in bytes of all elements in an array | +| `.reverse` | Reverse the index of elements in an array | +| `.random` | Return a random element from the array | +| `.shuffle` | Randomize the indexes of all elements in an array | +| `.unique` | Return unique elements in an array | +| `.replace` | Replace elements in array with replacement value | +| `.insert` | Insert element in an array at index | +| `.remove` | Remove element in an array at index | +| `.odd` | Return odd numbered indexed elements in an array | +| `.even` | Return even numbered indexed elements in an array | +| `.mod` | Return evenly divisible by n numbered indexed elements in an array | +| `.between` | Return elements between a start and end index in an array | +| `.sort` | Sort elements of an array in ascending order | +| `.rsort` | Sort elements of an array in desscending order | + + +### TO DO +- [ ] .union +- [ ] .intersection +- [ ] .difference \ No newline at end of file diff --git a/ARR/generate.sh b/ARR/generate.sh new file mode 100755 index 0000000..1f648b1 --- /dev/null +++ b/ARR/generate.sh @@ -0,0 +1,100 @@ +#!/usr/bin/env bash + +# generate was used on MacOS - so gnu-sed is needed on MacOS: +# brew install gnu-sed +# on linux, just replace $SED with sed +SED=gsed + +TYPES=( + BYTE + UBYTE + INT + UINT + LONG + ULONG + INT64 + UINT64 + SNG + DBL + FLT +) +for t in ${TYPES[@]}; do + cp ARR_TEMPLATE.BAS "ARR_${t}.BAS" +done + +# BYTE +$SED -i 's/{Q}/_BYTE/g' ARR_BYTE.BAS +$SED -i 's/{q}/_byte/g' ARR_BYTE.BAS +$SED -i 's/{UT}/BYTE/g' ARR_BYTE.BAS +$SED -i 's/{LT}/byte/g' ARR_BYTE.BAS +$SED -i 's/{SY}/%%/g' ARR_BYTE.BAS + +# UBYTE +$SED -i 's/{Q}/_UNSIGNED _BYTE/g' ARR_UBYTE.BAS +$SED -i 's/{q}/_unsigned _byte/g' ARR_UBYTE.BAS +$SED -i 's/{UT}/UBYTE/g' ARR_UBYTE.BAS +$SED -i 's/{LT}/ubyte/g' ARR_UBYTE.BAS +$SED -i 's/{SY}/~%%/g' ARR_UBYTE.BAS + +# INT +$SED -i 's/{Q}/INTEGER/g' ARR_INT.BAS +$SED -i 's/{q}/INTEGER/g' ARR_INT.BAS +$SED -i 's/{UT}/INT/g' ARR_INT.BAS +$SED -i 's/{LT}/int/g' ARR_INT.BAS +$SED -i 's/{SY}/%/g' ARR_INT.BAS + +# UINT +$SED -i 's/{Q}/_UNSIGNED INTEGER/g' ARR_UINT.BAS +$SED -i 's/{q}/_unsigned INTEGER/g' ARR_UINT.BAS +$SED -i 's/{UT}/UINT/g' ARR_UINT.BAS +$SED -i 's/{LT}/uint/g' ARR_UINT.BAS +$SED -i 's/{SY}/~%/g' ARR_UINT.BAS + +# LONG +$SED -i 's/{Q}/LONG/g' ARR_LONG.BAS +$SED -i 's/{q}/long/g' ARR_LONG.BAS +$SED -i 's/{UT}/LONG/g' ARR_LONG.BAS +$SED -i 's/{LT}/long/g' ARR_LONG.BAS +$SED -i 's/{SY}/\&/g' ARR_LONG.BAS + +# ULONG +$SED -i 's/{Q}/_UNSIGNED LONG/g' ARR_ULONG.BAS +$SED -i 's/{q}/_unsigned long/g' ARR_ULONG.BAS +$SED -i 's/{UT}/ULONG/g' ARR_ULONG.BAS +$SED -i 's/{LT}/ulong/g' ARR_ULONG.BAS +$SED -i 's/{SY}/~\&/g' ARR_ULONG.BAS + +# INT64 +$SED -i 's/{Q}/_INTEGER64/g' ARR_INT64.BAS +$SED -i 's/{q}/_integer64/g' ARR_INT64.BAS +$SED -i 's/{UT}/INT64/g' ARR_INT64.BAS +$SED -i 's/{LT}/int64/g' ARR_INT64.BAS +$SED -i 's/{SY}/\&\&/g' ARR_INT64.BAS + +# UINT64 +$SED -i 's/{Q}/_UNSIGNED _INTEGER64/g' ARR_UINT64.BAS +$SED -i 's/{q}/_unsigned _integer64/g' ARR_UINT64.BAS +$SED -i 's/{UT}/UINT64/g' ARR_UINT64.BAS +$SED -i 's/{LT}/uint64/g' ARR_UINT64.BAS +$SED -i 's/{SY}/~\&\&/g' ARR_UINT64.BAS + +# SNG +$SED -i 's/{Q}/SINGLE/g' ARR_SNG.BAS +$SED -i 's/{q}/single/g' ARR_SNG.BAS +$SED -i 's/{UT}/SNG/g' ARR_SNG.BAS +$SED -i 's/{LT}/sng/g' ARR_SNG.BAS +$SED -i 's/{SY}/!/g' ARR_SNG.BAS + +# DBL +$SED -i 's/{Q}/DOUBLE/g' ARR_DBL.BAS +$SED -i 's/{q}/double/g' ARR_DBL.BAS +$SED -i 's/{UT}/DBL/g' ARR_DBL.BAS +$SED -i 's/{LT}/dbl/g' ARR_DBL.BAS +$SED -i 's/{SY}/#/g' ARR_DBL.BAS + +# FLT +$SED -i 's/{Q}/_FLOAT/g' ARR_FLT.BAS +$SED -i 's/{q}/_float/g' ARR_FLT.BAS +$SED -i 's/{UT}/FLT/g' ARR_FLT.BAS +$SED -i 's/{LT}/flt/g' ARR_FLT.BAS +$SED -i 's/{SY}/##/g' ARR_FLT.BAS diff --git a/CONSOLE/CONSOLE.BAS b/CONSOLE/CONSOLE.BAS new file mode 100644 index 0000000..74d2cda --- /dev/null +++ b/CONSOLE/CONSOLE.BAS @@ -0,0 +1,36 @@ +'' +' QB64_GJ_LIB +' GRYMMJACK'S CONSOLE LIB +' +' CONSOLE object with debugging example +' +' USAGE: +' Insert '$INCLUDE:'path_to_GJ_LIB/CONSOLE/CONSOLE.BI' at the top of file +' Insert '$INCLUDE:'path_to_GJ_LIB/CONSOLE/CONSOLE.BM' at the bottom of file +' +' @author Rick Christy +' +$IF GJ_LIB_UNIFIED_TESTING = DEFINED AND GJ_LIB_INC_BI = UNDEFINED THEN +'$INCLUDE:'../_GJ_LIB.BI' +$END IF + +_TITLE "QB64_GJ_LIB CONSOLE OBJECT EXAMPLE" + +PRINT "This example outputs only this message to the program window." +PRINT "To see the purpose and output look at the terminal window." + +'$INCLUDE:'CONSOLE.BI' +console.log "This is a console.log message." + +'If you are used to using console.log() you might want to use CALL to use ()'s +CALL console.log("This is another console.log message.") + +console.info "This is a console.info message." +console.warn "This is a console.warn message." +console.error "This is a console.error message." + +'console.banner and console.box take a color code argument to draw in that color +console.banner "This is a console.banner message.", 13 +console.box "This is a console.box message.", 5 + +'$INCLUDE:'CONSOLE.BM' diff --git a/CONSOLE/CONSOLE.BI b/CONSOLE/CONSOLE.BI new file mode 100644 index 0000000..26b9ac4 --- /dev/null +++ b/CONSOLE/CONSOLE.BI @@ -0,0 +1,61 @@ +'' +' QB64_GJ_LIB +' GRYMMJACK'S CONSOLE LIB +' +' CONSOLE object with debugging. +' +' USAGE: +' Insert '$INCLUDE:'path_to_GJ_LIB/CONSOLE/CONSOLE.BI' at the top of file +' Insert '$INCLUDE:'path_to_GJ_LIB/CONSOLE/CONSOLE.BM' at the bottom of file +' +' @depends STRINGS/STRINGS.BI +' @author Rick Christy +' +$LET GJ_LIB_CONSOLE_INC_BI = 1 +$IF DEBUGGING = UNDEFINED THEN + $LET DEBUGGING = TRUE +$END IF +$IF DEBUGGING = TRUE THEN + $CONSOLE + $ASSERTS:CONSOLE + _CONSOLE ON +$END IF + +'Maps ANSI colors to CGA colors +DIM SHARED ANSI_COLOR(0 TO 15) AS INTEGER +ANSI_COLOR%(0) = 0 +ANSI_COLOR%(1) = 4 +ANSI_COLOR%(2) = 2 +ANSI_COLOR%(3) = 6 +ANSI_COLOR%(4) = 1 +ANSI_COLOR%(5) = 5 +ANSI_COLOR%(6) = 3 +ANSI_COLOR%(7) = 7 +ANSI_COLOR%(8) = 0 +ANSI_COLOR%(9) = 4 +ANSI_COLOR%(10) = 2 +ANSI_COLOR%(11) = 6 +ANSI_COLOR%(12) = 1 +ANSI_COLOR%(13) = 5 +ANSI_COLOR%(14) = 3 +ANSI_COLOR%(15) = 7 +CONST BLACK = 0% +CONST BLUE = 1% +CONST GREEN = 2% +CONST CYAN = 3% +CONST RED = 4% +CONST MAGENTA = 5% +CONST YELLOW = 6% +CONST WHITE = 7% +CONST BRIGHT_BLACK = 8% +CONST BRIGHT_BLUE = 9% +CONST BRIGHT_GREEN = 10% +CONST BRIGHT_CYAN = 11% +CONST BRIGHT_RED = 12% +CONST BRIGHT_MAGENTA = 13% +CONST BRIGHT_YELLOW = 14% +CONST BRIGHT_WHITE = 15% + +$IF GJ_LIB_STRINGS_INC_BI = UNDEFINED THEN +'$INCLUDE:'../STRINGS/STRINGS.BI' +$END IF diff --git a/CONSOLE/CONSOLE.BM b/CONSOLE/CONSOLE.BM new file mode 100644 index 0000000..069413d --- /dev/null +++ b/CONSOLE/CONSOLE.BM @@ -0,0 +1,119 @@ +'' +' QB64_GJ_LIB +' GRYMMJACK'S CONSOLE LIB +' +' CONSOLE object with debugging. +' +' USAGE: +' Insert '$INCLUDE:'path_to_GJ_LIB/CONSOLE/CONSOLE.BI' at the top of file +' Insert '$INCLUDE:'path_to_GJ_LIB/CONSOLE/CONSOLE.BM' at the bottom of file +' +' @author Rick Christy +' +$LET GJ_LIB_CONSOLE_INC_BM = 1 + +'' +' Log a boxed message to console if DEBUGGING +' @param STRING msg message to send +' @param INTEGER kolor% color to use +' +SUB console.box(msg$, kolor%) + $IF DEBUGGING = TRUE THEN + DIM AS STRING e, color_code, lines, intensity + DIM AS INTEGER afg + intensity$ = "0" + e$ = CHR$(27) + afg% = ANSI_COLOR%(kolor%) + color_code$ = e$ + "[" + IF kolor% > 7 THEN intensity$ = "1" + color_code$ = color_code$ + intensity$ + ";3" + color_code$ = color_code$ + _TRIM$(STR$(afg%)) + "m" + lines$ = "+" + STRING$(LEN(msg$)+2, "-") + "+" + _ECHO color_code$ + lines$ + _ECHO "| " + msg$ + " |" + _ECHO lines$ + e$ + "[0m" + $END IF + msg$ = "" +END SUB + + +'' +' Log a banner to console if DEBUGGING +' @param STRING msg message to send +' @param INTEGER kolor% color to use +' +SUB console.banner(msg$, kolor%) + $IF DEBUGGING = TRUE THEN + DIM AS STRING e, color_code, intensity + DIM AS INTEGER afg + intensity$ = "0" + e$ = CHR$(27) + afg% = ANSI_COLOR(kolor%) + msg$ = STR.replace$(msg$, "\n", CHR$(10), -1) + msg$ = STR.replace$(msg$, "\t", CHR$(9), -1) + color_code$ = e$ + "[" + IF kolor% > 7 THEN intensity$ = "1" + color_code$ = color_code$ + intensity$ + ";3" + color_code$ = color_code$ + _TRIM$(STR$(afg%)) + "m" + _ECHO color_code$ + msg$ + e$ + "[0m" + $END IF + msg$ = "" +END SUB + + +'' +' Log to console if DEBUGGING +' @param STRING msg message to send +' +SUB console.log(msg$) + $IF DEBUGGING = TRUE THEN + _ECHO msg$ + $END IF + msg$ = "" +END SUB + + +'' +' Log to console as info if DEBUGGING +' @param STRING msg message to send +' +SUB console.info(msg$) + $IF DEBUGGING = TRUE THEN + DIM AS STRING e + e$ = CHR$(27) + _ECHO e$ + "[1;36m" + msg$ + e$ + "[0m" + $END IF + msg$ = "" +END SUB + + +'' +' Log to console as warning if DEBUGGING +' @param STRING msg message to send +' +SUB console.warn(msg$) + $IF DEBUGGING = TRUE THEN + DIM AS STRING e + e$ = CHR$(27) + _ECHO e$ + "[1;33m" + msg$ + e$ + "[0m" + $END IF + msg$ = "" +END SUB + + +'' +' Log to console as error if DEBUGGING +' @param STRING msg message to send +' +SUB console.error(msg$) + $IF DEBUGGING = TRUE THEN + DIM AS STRING e + e$ = CHR$(27) + _ECHO e$ + "[1;31m" + msg$ + e$ + "[0m" + $END IF + msg$ = "" +END SUB + +$IF GJ_LIB_STRINGS_INC_BM = UNDEFINED THEN +'$INCLUDE:'../STRINGS/STRINGS.BM' +$END IF diff --git a/CONSOLE/CONSOLE.png b/CONSOLE/CONSOLE.png new file mode 100644 index 0000000..7c68cc7 Binary files /dev/null and b/CONSOLE/CONSOLE.png differ diff --git a/CONSOLE/README.md b/CONSOLE/README.md new file mode 100644 index 0000000..20b3094 --- /dev/null +++ b/CONSOLE/README.md @@ -0,0 +1,51 @@ +# [QB64_GJ_LIB](../README.md) +## GRYMMJACK'S CONSOLE LIBRARY + +> CONSOLE object with debugging. + +The idea of this library and is to provide a elegant and simple way to include +console output conditionally by setting one `$LET DEBUGGING = TRUE` in your code +and if you do not have this set, just including the library will enable it by +default. This will let you simply include the library and get busy using it. + +So what does it do? Well it provides QB64PE with functions similar to that of +a web developer is spoiled by with a console object to use. + +In this case, we simply send strings to the console terminal of various colors +with ANSI codes. + +I've extended the original idea of the web console.log a bit with a banner, and +a box utility which is handy for seeing output separated from the rest. + +See the example and screenshot for more information. + + +## WHAT'S IN THE LIBRARY +| SUB / FUNCTION | NOTES | +|----------------|-------| +| console.log | Log to console if DEBUGGING | +| console.info | Log to console as info if DEBUGGING | +| console.warn | Log to console as warning if DEBUGGING | +| console.error | Log to console as error if DEBUGGING | +| console.banner | Log a banner to console if DEBUGGING | +| console.box | Log a boxed message to console if DEBUGGING | + + + +### USAGE for CONSOLE LIB (separately) +```basic +'Insert at top of code: +'$INCLUDE:'path_to_GJ_LIB/CONSOLE/CONSOLE.BM' + +'...your code here... + +'Insert at bottom of code: +'$INCLUDE:'path_to_GJ_LIB/CONSOLE/CONSOLE.BM' +``` + + + +### EXAMPLE +> Screenshot of output from [CONSOLE.BAS](CONSOLE.BAS) + +![](CONSOLE.png) \ No newline at end of file diff --git a/DICT/DICT.BAS b/DICT/DICT.BAS index 6d73e43..9de2a93 100644 --- a/DICT/DICT.BAS +++ b/DICT/DICT.BAS @@ -17,6 +17,8 @@ $IF GJ_LIB_DUMP_INC_BI = UNDEFINED THEN $END IF '$DYNAMIC +OPTION _EXPLICIT +OPTION _EXPLICITARRAY _TITLE "QB64_GJ_LIB Dict OBJECT TESTS" @@ -24,11 +26,13 @@ _TITLE "QB64_GJ_LIB Dict OBJECT TESTS" SCREEN _NEWIMAGE(1024, 768, 32) _SCREENMOVE _MIDDLE +DIM index AS INTEGER +DIM AS STRING mykey, myval DIM keys$(5) DIM vals$(5) DIM SHARED keys2$(5) DIM SHARED vals2$(5) -DIM SHARED myDict(5) AS Dict +DIM SHARED myDict(5) AS DICTIONARY keys$(0) = "bilbo" keys$(1) = "frodo" @@ -44,28 +48,25 @@ vals$(4) = "bebbins" vals$(5) = "zobbins" ' dict_fill myDict() -dict_populate myDict(), keys$(), vals$() +CALL DICT.populate(myDict(), keys$(), vals$()) -PRINT dump_dict$(myDict(), "myDict") +PRINT DUMP.dict$(myDict(), "myDict") +index% = DICT.get_index_by_key(myDict(), "barbo") +PRINT "index = " + _TRIM$(STR$(index%)) +mykey$ = DICT.get_key_by_index(myDict(), index%) +PRINT "mykey = " + mykey$ +myval$ = DICT.get_val_by_index(myDict(), index%) +PRINT "myval (from index) = " + myval$ +PRINT "myval (from mykey) = " + mykey$ -index% = dict_get_index_by_key(myDict(), "barbo") -index$ = _TRIM$(STR$(index%)) -PRINT "index = " + index$ -key$ = dict_get_key_by_index(myDict(), index%) -PRINT "key = " + key$ -val$ = dict_get_val_by_index(myDict(), index%) -PRINT "val (from index) = " + val$ -PRINT "val (from key) = " + key$ +CALL DICT.get_keys(myDict(), keys2$()) +CALL DICT.get_vals(myDict(), vals2$()) +PRINT DUMP.string_array(keys2$(), "keys from dict_get_keys") +PRINT DUMP.string_array(vals2$(), "vals from dict_get_vals") -dict_get_keys myDict(), keys2$() -dict_get_vals myDict(), vals2$() - -PRINT dump_string_array(keys2$(), "keys from dict_get_keys") -PRINT dump_string_array(vals2$(), "vals from dict_get_vals") - -dict_swap_keys_for_vals myDict() -PRINT dump_dict(myDict(), "myDict swapped") +CALL DICT.swap_keys_for_vals(myDict()) +PRINT DUMP.dict(myDict(), "myDict swapped") diff --git a/DICT/DICT.BI b/DICT/DICT.BI index fd9f9b6..3019b5b 100644 --- a/DICT/DICT.BI +++ b/DICT/DICT.BI @@ -15,8 +15,8 @@ $LET GJ_LIB_DICT_INC_BI = 1 -' Dict type consists of keys and values and is intended for array use -TYPE Dict +' DICTIONARY type consists of keys and values and is intended for array use +TYPE DICTIONARY key AS STRING val AS STRING END TYPE diff --git a/DICT/DICT.BM b/DICT/DICT.BM index 700c901..bc9f303 100644 --- a/DICT/DICT.BM +++ b/DICT/DICT.BM @@ -1,6 +1,6 @@ '' ' QB64_GJ_LIB -' GRYMMJACK'S DICT Object (part of _GJ_LIB) +' GRYMMJACK'S DICTIONARY Object (part of _GJ_LIB) ' ' Simulates a dictionary object as found in other languages. ' @@ -8,17 +8,6 @@ ' Insert '$INCLUDE:'path_to_GJ_LIB/DICT/DICT.BI' at the top of file ' Insert '$INCLUDE:'path_to_GJ_LIB/DICT/DICT.BM' at the bottom of file ' -' SUB / FUNCTION NOTES -' dict_populate Populates a dict with arrays of keys and values -' dict_fill Fills a dict with serialized keys and values -' dict_get_index_by_key Gets a dict array index by key -' dict_get_key_by_index Gets a dict items key by its array index -' dict_get_val_by_index Gets a dict items value by its array index -' dict_get_val_by_key Gets a dict items value by its key -' dict_get_keys Get all dict object keys as an array of strings -' dict_get_vals Get all dict object values as an array of strings -' dict_swap_keys_for_vals Swaps a dict objects keys for its values -' ' @author Rick Christy ' @uses DICT.BI ' @@ -28,22 +17,21 @@ $IF GJ_LIB_DICT_INC_BI = UNDEFINED THEN $END IF - '' ' Populates a dictionary with arrays of keys and values ' -' @param dict() Dict object to populate -' @param keys$() String array of keys to use for dict keys -' @param vals$() String array of vals to use for dict vals -' @return Nothing, but the dict() passed is populated by keys and values +' @param DICTIONARY d() object to populate +' @param STRING ARRAY keys$() keys to use for dict keys +' @param STRING ARRAY vals$() vals to use for dict vals +' @return Nothing, but the d() passed is populated by keys and values ' -SUB dict_populate (dict() AS Dict, keys$(), vals$()) +SUB DICT.populate(d() AS DICTIONARY, keys$(), vals$()) DIM AS INTEGER uk, uv, i - uk = UBOUND(keys$) : uv = UBOUND(vals$) - IF uk <> uv THEN EXIT SUB - FOR i = 0 TO uk - dict(i).key = keys$(i) - dict(i).val = vals$(i) + uk% = UBOUND(keys$) : uv% = UBOUND(vals$) + IF uk% <> uv% THEN EXIT SUB + FOR i% = 0 TO uk% + d(i%).key$ = keys$(i) + d(i%).val$ = vals$(i) NEXT i END SUB @@ -51,15 +39,15 @@ END SUB '' ' Fills a dictionary with serialized keys and values ' -' @param dict() Dict object to fill -' @return Nothing, but the dict() passed in is filled +' @param DICTIONARY d() object to fill +' @return Nothing, but the d() passed in is filled ' - SUB dict_fill (dict() AS Dict) - DIM AS INTEGER u, l, i - u = UBOUND(dict) : l = LBOUND(dict) - FOR i = l TO u - dict(i).key = "key" + _TRIM$(STR$(i)) - dict(i).val = _TRIM$(STR$(i)) +SUB DICT.fill(d() AS DICTIONARY) + DIM AS INTEGER ub, lb, i + ub% = UBOUND(d) : lb% = LBOUND(d) + FOR i% = lb% TO ub% + d(i%).key$ = "key" + _TRIM$(STR$(i%)) + d(i%).val$ = _TRIM$(STR$(i%)) NEXT i END SUB @@ -67,35 +55,35 @@ END SUB '' ' Gets a dictionary array index by key ' -' @param dict() Dictionary to look in -' @param key$ String key to find the index for -' @return INTEGER array index if found or -1 if not found +' @param DICTIONARY d() to look in +' @param STRING ARRAY key$ to find the index for +' @return INTEGER array index if found or 0 if not found ' -FUNCTION dict_get_index_by_key% (dict() AS Dict, key$) - DIM AS INTEGER u, l, i - u = UBOUND(dict) : l = LBOUND(dict) - FOR i = l TO u - IF dict(i).key = key$ THEN - dict_get_index_by_key% = i +FUNCTION DICT.get_index_by_key%(d() AS DICTIONARY, key$) + DIM AS INTEGER ub, lb, i + ub% = UBOUND(d) : lb% = LBOUND(d) + FOR i% = lb% TO ub% + IF d(i%).key$ = key$ THEN + DICT.get_index_by_key% = i% EXIT FUNCTION END IF - NEXT i - dict_get_index_by_key% = -1 + NEXT i% + DICT.get_index_by_key% = 0 END FUNCTION '' ' Gets a dictionary items key by its array index ' -' @param dict() Dictionary to look in -' @param index% Index to lookup the key for +' @param DICTIONARY d() to look in +' @param INTEGER index% to lookup the key for ' @return STRING key of item at index ' -FUNCTION dict_get_key_by_index$ (dict() AS Dict, index%) - DIM AS INTEGER u, l - u = UBOUND(dict) : l = LBOUND(dict) - IF index% >= l AND index% <= u THEN - dict_get_key_by_index$ = dict(index%).key +FUNCTION DICT.get_key_by_index$(d() AS DICTIONARY, index%) + DIM AS INTEGER ub, lb + ub% = UBOUND(d) : lb% = LBOUND(d) + IF index% >= lb% AND index% <= ub% THEN + DICT.get_key_by_index$ = d(index%).key$ END IF END FUNCTION @@ -103,15 +91,15 @@ END FUNCTION '' ' Gets a dictionary items value by its array index ' -' @param dict() Dictionary to look in -' @param index% Index to lookup the value for +' @param DICTIONARY d() to look in +' @param INTEGER index% to lookup the value for ' @return STRING value of item at index ' -FUNCTION dict_get_val_by_index$ (dict() AS Dict, index%) - DIM AS INTEGER u, l - u = UBOUND(dict) : l = LBOUND(dict) - IF index% >= l AND index% <= u THEN - dict_get_val_by_index$ = dict(index%).val +FUNCTION DICT.get_val_by_index$(d() AS DICTIONARY, index%) + DIM AS INTEGER ub, lb + ub% = UBOUND(d) : lb% = LBOUND(d) + IF index% >= lb% AND index% <= ub% THEN + DICT.get_val_by_index$ = d(index%).val$ END IF END FUNCTION @@ -119,70 +107,70 @@ END FUNCTION '' ' Gets a dictionary items value by its key ' -' @param dict() Dictionary to look in -' @param key$ Key to get the value for +' @param DICTIONARY d() to look in +' @param STRING key$ to get the value for ' @return STRING value of dictionary item by key ' -FUNCTION dict_get_val_by_key$ (dict() AS Dict, key$) - DIM AS INTEGER u, l, i - u = UBOUND(dict) : l = LBOUND(dict) - FOR i = l TO u - IF dict(i).key = key$ THEN - dict_get_val_by_key$ = dict(i).val +FUNCTION DICT.get_val_by_key$(d() AS DICTIONARY, key$) + DIM AS INTEGER ub, lb, i + ub% = UBOUND(d) : lb% = LBOUND(d) + FOR i% = lb% TO ub% + IF d(i%).key$ = key$ THEN + DICT.get_val_by_key$ = d(i%).val$ EXIT FUNCTION END IF - NEXT i + NEXT i% END FUNCTION '' ' Get all dictionary object keys as an array of strings ' -' @param dict() Dictionary to look in -' @param keys$() String array to store dict object keys into +' @param DICTIONARY d() to look in +' @param STRING ARRAY keys$() to store dict object keys into ' @return Nothing, but the keys$() array is populated ' -SUB dict_get_keys (dict() AS Dict, keys$()) - DIM AS INTEGER u, l, i, c - u = UBOUND(dict) : l = LBOUND(dict) : c = u - l - REDIM keys$(c) - FOR i = l TO u - keys$(i) = dict(i).key - NEXT i +SUB DICT.get_keys(d() AS DICTIONARY, keys$()) + DIM AS INTEGER ub, lb, i, c + ub = UBOUND(d) : lb = LBOUND(d) : c% = ub% - lb% + REDIM keys$(c%) + FOR i% = lb% TO ub% + keys$(i%) = d(i%).key$ + NEXT i% END SUB '' ' Get all dictionary object values as an array of strings ' -' @param dict() Dictionary to look in -' @param vals$() String array to store dict object vals into +' @param DICTIONARY d() to look in +' @param STRING ARRAY vals$() to store dict object vals into ' @return Nothing, but the vals$() array is populated ' -SUB dict_get_vals (dict() AS Dict, vals$()) - DIM AS INTEGER u, l, i, c - u = UBOUND(dict) : l = LBOUND(dict) : c = u - l - REDIM vals$(c) - FOR i = l TO u - vals$(i) = dict(i).val - NEXT i +SUB DICT.get_vals(d() AS DICTIONARY, vals$()) + DIM AS INTEGER ub, lb, i, c + ub = UBOUND(d) : lb = LBOUND(d) : c% = ub% - lb% + REDIM vals$(c%) + FOR i% = lb% TO ub% + vals$(i%) = d(i%).val$ + NEXT i% END SUB '' ' Swaps a dictionary objects keys for its values ' -' @param dict() Dictionary to operate on +' @param DICTIONARY d() to operate on ' @return Nothing, but the dict() passed in is operated on directly ' -SUB dict_swap_keys_for_vals (dict() AS Dict) - DIM AS INTEGER u, l, i, c - u = UBOUND(dict) : l = LBOUND(dict) : c = u - l - DIM res(c) AS Dict - FOR i = l TO u - res(i).key = dict(i).val - res(i).val = dict(i).key - SWAP dict(i).key, res(i).key - SWAP dict(i).val, res(i).val - NEXT i +SUB DICT.swap_keys_for_vals(d() AS DICTIONARY) + DIM AS INTEGER ub, lb, i, c + ub = UBOUND(d) : lb = LBOUND(d) : c% = ub% - lb% + DIM res(c%) AS DICTIONARY + FOR i% = lb% TO ub% + res(i%).key$ = d(i%).val$ + res(i%).val$ = d(i%).key$ + SWAP d(i%).key$, res(i%).key$ + SWAP d(i%).val$, res(i%).val$ + NEXT i% END SUB diff --git a/DICT/README.md b/DICT/README.md index 6d1be2c..36373ec 100644 --- a/DICT/README.md +++ b/DICT/README.md @@ -19,17 +19,35 @@ Simulates a dictionary object as found in other languages. ## WHAT'S IN THE LIBRARY | SUB / FUNCTION | NOTES | |----------------|-------| -| dict_populate | Populates a dict with arrays of keys and values -| dict_fill | Fills a dict with serialized keys and values -| dict_get_index_by_key | Gets a dict array index by key -| dict_get_key_by_index | Gets a dict items key by its array index -| dict_get_val_by_index | Gets a dict items value by its array index -| dict_get_val_by_key | Gets a dict items value by its key -| dict_get_keys | Get all dict object keys as an array of strings -| dict_get_vals | Get all dict object values as an array of strings -| dict_swap_keys_for_vals | Swaps a dict objects keys for its values +| DICT.populate | Populates a dict with arrays of keys and values +| DICT.fill | Fills a dict with serialized keys and values +| DICT.get_index_by_key | Gets a dict array index by key +| DICT.get_key_by_index | Gets a dict items key by its array index +| DICT.get_val_by_index | Gets a dict items value by its array index +| DICT.get_val_by_key | Gets a dict items value by its key +| DICT.get_keys | Get all dict object keys as an array of strings +| DICT.get_vals | Get all dict object values as an array of strings +| DICT.swap_keys_for_vals | Swaps a dict objects keys for its values ### EXAMPLE > Screenshot of output from [DICT.BAS](DICT.BAS) -![Example output from [DICT.BAS](DICT.BAS)](DICT.png) \ No newline at end of file +![Example output from [DICT.BAS](DICT.BAS)](DICT.png) + +### TO DO: +- [ ] DICT.keys (wrapper for DICT.get_keys) +- [ ] DICT.vals (wrapper for DICT.get_vals) +- [ ] DICT.clear +- [ ] DICT.new(d(), "foo=bar,baz=bop,fip=fap,end=done") +- [ ] DICT.add(d(), "foo=bar,baz=bop,fip=fap") +- [ ] DICT.templatize_str (like \` \` in js {key} replaced with {val}) +- [ ] DICT.insert(d(), key$, val$) +- [ ] DICT.remove(d(), key$) +- [ ] DICT.key_sort +- [ ] DICT.val_sort +- [ ] DICT.from_json +- [ ] DICT.to_json +- [ ] DICT.from_yaml +- [ ] DICT.to_yaml +- [ ] DICT.from_xml +- [ ] DICT.to_xml \ No newline at end of file diff --git a/DUMP/DUMP.BAS b/DUMP/DUMP.BAS index 9062863..50423dc 100644 --- a/DUMP/DUMP.BAS +++ b/DUMP/DUMP.BAS @@ -17,6 +17,8 @@ $IF GJ_LIB_DUMP_INC_BI = UNDEFINED THEN $END IF '$DYNAMIC +OPTION _EXPLICIT +OPTION _EXPLICITARRAY _TITLE "QB64_GJ_LIB DUMP LIB TESTS" @@ -57,7 +59,7 @@ dblArray#(1) = 2863864865866868088.2863864865866868088 dblArray#(2) = 3864865866868088286.3864865866868088286 dblArray#(3) = 4865866868088286386.4865866868088286386 -DIM SHARED myDict(5) AS Dict +DIM SHARED myDict(5) AS DICTIONARY DIM keys$(5) DIM vals$(5) keys$(0) = "bilbo" @@ -72,15 +74,15 @@ vals$(2) = "biggins" vals$(3) = "boggins" vals$(4) = "bebbins" vals$(5) = "zobbins" -dict_populate myDict(), keys$(), vals$() +CALL DICT.populate(myDict(), keys$(), vals$()) -PRINT dump_string$(s$, "s") -PRINT dump_string_array$(strArray$(), "strArray") -PRINT dump_integer_array$(intArray%(), "intArray") -PRINT dump_single_array$(singArray!(), "singArray") -PRINT dump_long_array$(longArray&(), "longArray") -PRINT dump_double_array$(dblArray#(), "dblArray") -PRINT dump_dict$(myDict(), "myDict"); +PRINT DUMP.string$(s$, "s") +PRINT DUMP.string_array$(strArray$(), "strArray") +PRINT DUMP.integer_array$(intArray%(), "intArray") +PRINT DUMP.single_array$(singArray!(), "singArray") +PRINT DUMP.long_array$(longArray&(), "longArray") +PRINT DUMP.double_array$(dblArray#(), "dblArray") +PRINT DUMP.dict$(myDict(), "myDict"); diff --git a/DUMP/DUMP.BI b/DUMP/DUMP.BI index e6c751a..1c9fd08 100644 --- a/DUMP/DUMP.BI +++ b/DUMP/DUMP.BI @@ -15,9 +15,12 @@ $IF GJ_LIB_DICT_INC_BI = UNDEFINED THEN '$INCLUDE:'../DICT/DICT.BI' $END IF +$IF GJ_LIB_NL = UNDEFINED THEN +$LET GJ_LIB_NL = 1 DIM SHARED GJ_LIB_NL AS STRING $IF WIN THEN GJ_LIB_NL$ = CHR$(13) $ELSE GJ_LIB_NL$ = CHR$(10) $END IF +$END IF diff --git a/DUMP/DUMP.BM b/DUMP/DUMP.BM index 0d2a19f..e6eab65 100644 --- a/DUMP/DUMP.BM +++ b/DUMP/DUMP.BM @@ -33,7 +33,7 @@ ' ' DIM words$(2) ' words$(0) = "foo" : words$(1) = "bar" : words$(2) = "baz" -' PRINT dump_string_array(words$(), "words") +' PRINT DUMP.string_array(words$(), "words") ' ' OUTPUT: ' @@ -50,20 +50,20 @@ ' you want while staying in the spirit of DUMP LIB. ' ' FUNCTION NOTES -' dump_bit_array$ Returns string with a dump of an array of bits -' dump_unsigned_bit_array$ Returns string with a dump of an array of unsigned bits -' dump_byte_array$ Returns string with a dump of an array of bytes -' dump_unsigned_byte_array$ Returns string with a dump of an array of unsigned bytes -' dump_unsigned_integer$ Returns string with a dump of an array of unsigned integers -' dump_unsigned_byte_array_as_hex$ Returns string with a dump of an array of unsigned bytes as hex -' dump_unsigned_byte_array_as_ascii$ Returns string with a dump of an array of unsigned bytes as hex and ASCII -' dump_string$ Includes handy output of the strings length. -' dump_string_array$ Works on 1 dimensional arrays only (right now). -' dump_integer_array$ Works on 1 dimensional arrays only (right now). -' dump_single_array$ Works on 1 dimensional arrays only (right now). -' dump_long_array$ Works on 1 dimensional arrays only (right now). -' dump_double_array$ Works on 1 dimensional arrays only (right now). -' dump_dict$ Dump a dictionary object and it's contents. +' DUMP.bit_array$ Returns string with a dump of an array of bits +' DUMP.unsigned_bit_array$ Returns string with a dump of an array of unsigned bits +' DUMP.byte_array$ Returns string with a dump of an array of bytes +' DUMP.unsigned_byte_array$ Returns string with a dump of an array of unsigned bytes +' DUMP.unsigned_integer$ Returns string with a dump of an array of unsigned integers +' DUMP.unsigned_byte_array_as_hex$ Returns string with a dump of an array of unsigned bytes as hex +' DUMP.unsigned_byte_array_as_ascii$ Returns string with a dump of an array of unsigned bytes as hex and ASCII +' DUMP.string$ Includes handy output of the strings length. +' DUMP.string_array$ Works on 1 dimensional arrays only (right now). +' DUMP.integer_array$ Works on 1 dimensional arrays only (right now). +' DUMP.single_array$ Works on 1 dimensional arrays only (right now). +' DUMP.long_array$ Works on 1 dimensional arrays only (right now). +' DUMP.double_array$ Works on 1 dimensional arrays only (right now). +' DUMP.dict$ Dump a dictionary object and it's contents. ' ' @author Rick Christy ' @depends DUMP.BI @@ -77,199 +77,201 @@ $LET GJ_LIB_DUMP_INC_BM = 1 '' ' Returns string with a dump of a string ' -' @param s$ String to dump -' @param label$ Label to give the dump block +' @param STRING s$ to dump +' @param STRING label$ to give the dump block ' @return STRING dump block ' -FUNCTION dump_string$ (s$, label$) +FUNCTION DUMP.string$(s$, label$) DIM AS STRING l, r l$ = _TRIM$(STR$(LEN(s$))) r$ = GJ_LIB_NL$ + "STRING: " + label$ + "$ {" + GJ_LIB_NL$ r$ = r$ + " " + CHR$(34) + s$ + CHR$(34) + " [" + l$ + "]" + GJ_LIB_NL$ r$ = r$ + "} " - dump_string$ = r$ + DUMP.string$ = r$ END FUNCTION '' ' Returns string with a dump of an array of bits ' -' @param arr$() Array of bits to dump -' @param label$ Label to give the dump block +' @param _BIT ARRAY arr`() of bits to dump +' @param STRING label$ to give the dump block ' @return STRING dump block ' -FUNCTION dump_bit_array$ (arr`(), label$) +FUNCTION DUMP.bit_array$(arr`(), label$) DIM AS INTEGER lb, ub, i DIM AS STRING t, r - lb = LBOUND(arr`) : ub = UBOUND(arr`) : t$ = _TRIM$(STR$(ub - lb)) + lb% = LBOUND(arr`) : ub% = UBOUND(arr`) : t$ = _TRIM$(STR$(ub% - lb%)) r$ = GJ_LIB_NL$ + "STRING ARRAY: " + label$ + "`(" + t$ + ") {" + GJ_LIB_NL$ - FOR i = lb TO ub - r$ = r$ + " (" + _TRIM$(STR$(i)) + "): " + STR$(arr`(i)) + FOR i% = lb% TO ub% + r$ = r$ + " (" + _TRIM$(STR$(i%)) + "): " + STR$(arr`(i%)) r$ = r$ + " [" + t$ + "]" + GJ_LIB_NL$ - NEXT i + NEXT i% r$ = r$ + "}" - dump_bit_array$ = r$ + DUMP.bit_array$ = r$ END FUNCTION '' ' Returns string with a dump of an array of unsigned bits ' -' @param arr$() Array of unsigned bits to dump -' @param label$ Label to give the dump block +' @param _UNSIGNED _BIT arr~`() of unsigned bits to dump +' @param STRING label$ to give the dump block ' @return STRING dump block ' -FUNCTION dump_unsigned_bit_array$ (arr~`(), label$) +FUNCTION DUMP.unsigned_bit_array$(arr~`(), label$) DIM AS INTEGER lb, ub, i DIM AS STRING t, r - lb = LBOUND(arr~`) : ub = UBOUND(arr~`) : t$ = _TRIM$(STR$(ub - lb)) + lb% = LBOUND(arr~`) : ub% = UBOUND(arr~`) : t$ = _TRIM$(STR$(ub% - lb%)) r$ = GJ_LIB_NL$ + "STRING ARRAY: " + label$ + "~`(" + t$ + ") {" + GJ_LIB_NL$ - FOR i = lb TO ub - r$ = r$ + " (" + _TRIM$(STR$(i)) + "): " + STR$(arr~`(i)) + FOR i% = lb% TO ub% + r$ = r$ + " (" + _TRIM$(STR$(i%)) + "): " + STR$(arr~`(i%)) r$ = r$ + " [" + t$ + "]" + GJ_LIB_NL$ - NEXT i + NEXT i% r$ = r$ + "}" - dump_unsigned_bit_array$ = r$ + DUMP.unsigned_bit_array$ = r$ END FUNCTION '' ' Returns string with a dump of an array of strings ' -' @param arr$() Array of strings to dump -' @param label$ Label to give the dump block +' @param STRING ARRAY arr$() of strings to dump +' @param STRING label$ to give the dump block ' @return STRING dump block ' -FUNCTION dump_string_array$ (arr$(), label$) +FUNCTION DUMP.string_array$(arr$(), label$) DIM AS INTEGER lb, ub, i DIM AS STRING t, r - lb = LBOUND(arr$) : ub = UBOUND(arr$) : t$ = _TRIM$(STR$(ub - lb)) + lb% = LBOUND(arr$) : ub% = UBOUND(arr$) : t$ = _TRIM$(STR$(ub% - lb%)) r$ = GJ_LIB_NL$ + "STRING ARRAY: " + label$ + "$(" + t$ + ") {" + GJ_LIB_NL$ - FOR i = lb TO ub - r$ = r$ + " (" + _TRIM$(STR$(i)) + "): " + CHR$(34) + arr$(i) + CHR$(34) + FOR i% = lb% TO ub% + r$ = r$ + " (" + _TRIM$(STR$(i%)) + "): " + CHR$(34) + arr$(i%) + CHR$(34) r$ = r$ + " [" + t$ + "]" + GJ_LIB_NL$ - NEXT i + NEXT i% r$ = r$ + "}" - dump_string_array$ = r$ + DUMP.string_array$ = r$ END FUNCTION '' ' Returns string with a dump of an array of integers ' -' @param arr%() Array of integers to dump -' @param label$ Label to give the dump block +' @param INTEGER ARRAY arr%() of integers to dump +' @param STRING label$ to give the dump block ' @return STRING dump block ' -FUNCTION dump_integer_array$ (arr%(), label$) +FUNCTION DUMP.integer_array$(arr%(), label$) DIM AS INTEGER lb, ub, i DIM AS STRING t, r, num - lb = LBOUND(arr%) : ub = UBOUND(arr%) : t$ = _TRIM$(STR$(ub - lb)) + lb% = LBOUND(arr%) : ub% = UBOUND(arr%) : t$ = _TRIM$(STR$(ub% - lb%)) r$ = GJ_LIB_NL$ + "INTEGER ARRAY: " + label$ + "%(" + t$ + ") {" + GJ_LIB_NL$ - FOR i = lb TO ub - num$ = _TRIM$(STR$(arr%(i))) - r$ = r$ + " (" + _TRIM$(STR$(i)) + "): " + num$ + GJ_LIB_NL$ - NEXT i + FOR i% = lb% TO ub% + num$ = _TRIM$(STR$(arr%(i%))) + r$ = r$ + " (" + _TRIM$(STR$(i%)) + "): " + num$ + GJ_LIB_NL$ + NEXT i% r$ = r$ + "}" - dump_integer_array$ = r$ + DUMP.integer_array$ = r$ END FUNCTION '' ' Returns string with a dump of an array of unsigned integers ' -' @param arr%() Array of unsigned integers to dump -' @param label$ Label to give the dump block +' @param _UNSIGNED INTEGER arr%() of unsigned integers to dump +' @param STRING label$ to give the dump block ' @return STRING dump block ' -FUNCTION dump_unsigned_integer_array$ (arr~%(), label$) +FUNCTION DUMP.unsigned_integer_array$(arr~%(), label$) DIM AS INTEGER lb, ub, i DIM AS STRING r, t, num - lb = LBOUND(arr~%) : ub = UBOUND(arr~%) : t$ = _TRIM$(STR$(ub - lb)) + lb% = LBOUND(arr~%) : ub% = UBOUND(arr~%) : t$ = _TRIM$(STR$(ub% - lb%)) r$ = GJ_LIB_NL$ + "INTEGER ARRAY: " + label$ + "~%(" + t$ + ") {" + GJ_LIB_NL$ - FOR i = lb TO ub - num$ = _TRIM$(STR$(arr~%(i))) - r$ = r$ + " (" + _TRIM$(STR$(i)) + "): " + num$ + GJ_LIB_NL$ - NEXT i + FOR i% = lb% TO ub% + num$ = _TRIM$(STR$(arr~%(i%))) + r$ = r$ + " (" + _TRIM$(STR$(i%)) + "): " + num$ + GJ_LIB_NL$ + NEXT i% r$ = r$ + "}" - dump_unsigned_integer_array$ = r$ + DUMP.unsigned_integer_array$ = r$ END FUNCTION '' ' Returns a string containing a dump of an array of singles ' -' @param arr!() Array of singles to dump -' @param label$ Label to give the dump block +' @param SINGLE ARRAY arr!() of singles to dump +' @param STRING label$ to give the dump block ' @return STRING dump block ' -FUNCTION dump_single_array$ (arr!(), label$) +FUNCTION DUMP.single_array$(arr!(), label$) DIM AS INTEGER lb, ub, i DIM AS STRING r, t, num - lb = LBOUND(arr!) : ub = UBOUND(arr!) : t$ = _TRIM$(STR$(ub - lb)) + lb% = LBOUND(arr!) : ub% = UBOUND(arr!) : t$ = _TRIM$(STR$(ub% - lb%)) r$ = GJ_LIB_NL$ + "SINGLE ARRAY: " + label$ + "!(" + t$ + ") {" + GJ_LIB_NL$ - FOR i = lb TO ub - num$ = _TRIM$(STR$(arr!(i))) - r$ = r$ + " (" + _TRIM$(STR$(i)) + "): " + num$ + GJ_LIB_NL$ - NEXT i + FOR i% = lb% TO ub% + num$ = _TRIM$(STR$(arr!(i%))) + r$ = r$ + " (" + _TRIM$(STR$(i%)) + "): " + num$ + GJ_LIB_NL$ + NEXT i% r$ = r$ + "}" - dump_single_array$ = r$ + DUMP.single_array$ = r$ END FUNCTION '' ' Dumps an array of longs ' -' @param arr&() Array of longs to dump -' @param label$ Label to give the dump block +' @param LONG ARRAY arr&() of longs to dump +' @param STRING label$ to give the dump block +' @return STRING dump block ' -FUNCTION dump_long_array$ (arr&(), label$) +FUNCTION DUMP.long_array$(arr&(), label$) DIM AS INTEGER lb, ub, i DIM AS STRING r, t, num - lb = LBOUND(arr&) : ub = UBOUND(arr&) : t$ = _TRIM$(STR$(ub - lb)) + lb% = LBOUND(arr&) : ub% = UBOUND(arr&) : t$ = _TRIM$(STR$(ub% - lb%)) r$ = GJ_LIB_NL$ + "LONG ARRAY: " + label$ + "&(" + t$ + ") {" + GJ_LIB_NL$ - FOR i = lb TO ub - num$ = _TRIM$(STR$(arr&(i))) - r$ = r$ + " (" + _TRIM$(STR$(i)) + "): " + num$ + GJ_LIB_NL$ - NEXT i + FOR i% = lb% TO ub% + num$ = _TRIM$(STR$(arr&(i%))) + r$ = r$ + " (" + _TRIM$(STR$(i%)) + "): " + num$ + GJ_LIB_NL$ + NEXT i% r$ = r$ + "}" - dump_long_array$ = r$ + DUMP.long_array$ = r$ END FUNCTION '' ' Dumps an array of doubles ' -' @param arr#() Array of doubles to dump -' @param label$ Label to give the dump block +' @param DOUBLE ARRAY arr#() of doubles to dump +' @param STRING label$ to give the dump block +' @return STRING dump block ' -FUNCTION dump_double_array$ (arr#(), label$) +FUNCTION DUMP.double_array$(arr#(), label$) DIM AS INTEGER lb, ub, i DIM AS STRING r, t, num - lb = LBOUND(arr#) : ub = UBOUND(arr#) : t$ = _TRIM$(STR$(ub - lb)) + lb% = LBOUND(arr#) : ub% = UBOUND(arr#) : t$ = _TRIM$(STR$(ub% - lb%)) r$ = GJ_LIB_NL$ + "DOUBLE ARRAY: " + label$ + "#(" + t$ + ") {" + GJ_LIB_NL$ - FOR i = lb TO ub - num$ = _TRIM$(STR$(arr#(i))) - r$ = r$ + " (" + _TRIM$(STR$(i)) + "): " + num$ + GJ_LIB_NL$ - NEXT i + FOR i% = lb% TO ub% + num$ = _TRIM$(STR$(arr#(i%))) + r$ = r$ + " (" + _TRIM$(STR$(i%)) + "): " + num$ + GJ_LIB_NL$ + NEXT i% r$ = r$ + "}" - dump_double_array$ = r$ + DUMP.double_array$ = r$ END FUNCTION '' ' Returns string with a dump of an array of bytes ' -' @param arr%%() Array of bytes to dump -' @param label$ Label to give the dump block +' @param _BYTE ARRAY arr%%() of bytes to dump +' @param STRING label$ to give the dump block ' @return STRING dump block ' -FUNCTION dump_byte_array$ (arr%%(), label$) +FUNCTION DUMP.byte_array$(arr%%(), label$) DIM AS INTEGER lb, ub, i DIM AS STRING r, t, num, si - lb = LBOUND(arr%%) : ub = UBOUND(arr%%) : t$ = _TRIM$(STR$(ub - lb)) + lb% = LBOUND(arr%%) : ub% = UBOUND(arr%%) : t$ = _TRIM$(STR$(ub% - lb%)) r$ = GJ_LIB_NL$ + "BYTE ARRAY: " + label$ + "%%(" + t$ + ") {" - FOR i% = lb TO ub + FOR i% = lb% TO ub% si$ = _TRIM$(STR$(i%)) IF LEN(si$) = 1 THEN si$ = "0" + si$ num$ = _TRIM$(STR$(arr%%(i%))) @@ -295,23 +297,23 @@ FUNCTION dump_byte_array$ (arr%%(), label$) r$ = r$ + num$ + " " NEXT i% r$ = r$ + GJ_LIB_NL$ + "}" - dump_byte_array$ = r$ + DUMP.byte_array$ = r$ END FUNCTION '' ' Returns string with a dump of an array of unsigned bytes ' -' @param arr~%%() Array of unsigned bytes to dump -' @param label$ Label to give the dump block +' @param _UNSIGNED _BYTE ARRAY arr~%%() of unsigned bytes to dump +' @param STRING label$ to give the dump block ' @return STRING dump block ' -FUNCTION dump_unsigned_byte_array$ (arr~%%(), label$) +FUNCTION DUMP.unsigned_byte_array$(arr~%%(), label$) DIM AS INTEGER lb, ub, i DIM AS STRING r, t, num, si - lb = LBOUND(arr~%%) : ub = UBOUND(arr~%%) : t$ = _TRIM$(STR$(ub - lb)) + lb% = LBOUND(arr~%%) : ub% = UBOUND(arr~%%) : t$ = _TRIM$(STR$(ub% - lb%)) r$ = GJ_LIB_NL$ + "UNSIGNED BYTE ARRAY: " + label$ + "~%%(" + t$ + ") {" - FOR i% = lb TO ub + FOR i% = lb% TO ub% si$ = _TRIM$(STR$(i%)) IF LEN(si$) = 1 THEN si$ = "0" + si$ num$ = _TRIM$(STR$(arr~%%(i%))) @@ -327,23 +329,23 @@ FUNCTION dump_unsigned_byte_array$ (arr~%%(), label$) r$ = r$ + num$ + " " NEXT i% r$ = r$ + GJ_LIB_NL$ + "}" - dump_unsigned_byte_array$ = r$ + DUMP.unsigned_byte_array$ = r$ END FUNCTION '' ' Returns string with a dump of an array of unsigned bytes as hex ' -' @param arr~%%() Array of unsigned bytes to dump -' @param label$ Label to give the dump block +' @param _UNSIGNED _BYTE ARRAY arr~%%() of unsigned bytes to dump +' @param STRING label$ to give the dump block ' @return STRING dump block ' -FUNCTION dump_unsigned_byte_array_as_hex$ (arr~%%(), label$) +FUNCTION DUMP.unsigned_byte_array_as_hex$(arr~%%(), label$) DIM AS INTEGER lb, ub, i DIM AS STRING r, t, num, si, h - lb = LBOUND(arr~%%) : ub = UBOUND(arr~%%) : t$ = _TRIM$(STR$(ub - lb)) + lb% = LBOUND(arr~%%) : ub% = UBOUND(arr~%%) : t$ = _TRIM$(STR$(ub% - lb%)) r$ = GJ_LIB_NL$ + "UNSIGNED BYTE ARRAY: " + label$ + "~%%(" + t$ + ") {" - FOR i% = lb TO ub + FOR i% = lb% TO ub% si$ = _TRIM$(STR$(i%)) IF LEN(si$) = 1 THEN si$ = "0" + si$ h$ = HEX$(arr~%%(i%)) @@ -356,25 +358,25 @@ FUNCTION dump_unsigned_byte_array_as_hex$ (arr~%%(), label$) r$ = r$ + h$ + " " NEXT i% r$ = r$ + GJ_LIB_NL$ + "}" - dump_unsigned_byte_array_as_hex$ = r$ + DUMP.unsigned_byte_array_as_hex$ = r$ END FUNCTION '' ' Returns string with a dump of an array of unsigned bytes as ascii ' -' @param arr~%%() Array of unsigned bytes to dump -' @param label$ Label to give the dump block +' @param _UNSIGNED _BYTE ARRAY arr~%%() of unsigned bytes to dump +' @param STRING label$ to give the dump block ' @return STRING dump block ' -FUNCTION dump_unsigned_byte_array_as_ascii$ (arr~%%(), label$) +FUNCTION DUMP.unsigned_byte_array_as_ascii$(arr~%%(), label$) DIM AS INTEGER lb, ub, i DIM AS STRING r, t, num, si, h, c - lb = LBOUND(arr~%%) : ub = UBOUND(arr~%%) : t$ = _TRIM$(STR$(ub - lb)) + lb% = LBOUND(arr~%%) : ub% = UBOUND(arr~%%) : t$ = _TRIM$(STR$(ub% - lb%)) r$ = GJ_LIB_NL$ + "UNSIGNED BYTE ARRAY: " + label$ + "~%%(" + t$ + ") {" - FOR i% = lb TO ub + FOR i% = lb% TO ub% si$ = _TRIM$(STR$(i%)) - DO WHILE LEN(si$) < LEN(STR$(ub))-1 + DO WHILE LEN(si$) < LEN(STR$(ub%))-1 si$ = "0" + si$ LOOP h$ = HEX$(arr~%%(i%)) @@ -387,9 +389,9 @@ FUNCTION dump_unsigned_byte_array_as_ascii$ (arr~%%(), label$) r$ = r$ + h$ + " " NEXT i% r$ = r$ + GJ_LIB_NL$ + "ASCII:" - FOR i% = lb TO ub + FOR i% = lb% TO ub% si$ = _TRIM$(STR$(i%)) - DO WHILE LEN(si$) < LEN(STR$(ub))-1 + DO WHILE LEN(si$) < LEN(STR$(ub%))-1 si$ = "0" + si$ LOOP IF arr~%%(i%) < 33 OR arr~%%(i%) > 254 THEN @@ -404,28 +406,29 @@ FUNCTION dump_unsigned_byte_array_as_ascii$ (arr~%%(), label$) r$ = r$ + c$ + " " NEXT i% r$ = r$ + GJ_LIB_NL$ + "}" - dump_unsigned_byte_array_as_ascii$ = r$ + DUMP.unsigned_byte_array_as_ascii$ = r$ END FUNCTION '' ' Dumps a dictionary object and its contents ' -' @param dict$() Dictionary object to dump -' @param label$ Label to give the dump block +' @param DICTIONARY d() object to dump +' @param STRING label$ to give the dump block +' @return STRING dump block ' -FUNCTION dump_dict$ (dict() AS Dict, label$) +FUNCTION DUMP.dict$(d() AS DICTIONARY, label$) DIM AS INTEGER lb, ub, i DIM AS STRING r, t, q, skey, sval - lb = LBOUND(dict) : ub = UBOUND(dict) : t$ = _TRIM$(STR$(ub - lb)) + lb% = LBOUND(d) : ub% = UBOUND(d) : t$ = _TRIM$(STR$(ub% - lb%)) r$ = GJ_LIB_NL$ + "DICT: " + label$ + "(" + t$ + ") {" + GJ_LIB_NL$ q$ = CHR$(34) - FOR i = lb TO ub - skey$ = dict(i).key - sval$ = dict(i).val - r$ = r$ + " (" + _TRIM$(STR$(i)) + ") " + q$ + skey$ + q$ + FOR i% = lb% TO ub% + skey$ = d(i%).key$ + sval$ = d(i%).val$ + r$ = r$ + " (" + _TRIM$(STR$(i%)) + ") " + q$ + skey$ + q$ r$ = r$ + ": " + q$ + sval$ + q$ + GJ_LIB_NL$ - NEXT i + NEXT i% r$ = r$ + "}" - dump_dict$ = r$ + DUMP.dict$ = r$ END FUNCTION diff --git a/DUMP/README.md b/DUMP/README.md index eeb318a..c7d9be4 100644 --- a/DUMP/README.md +++ b/DUMP/README.md @@ -9,20 +9,20 @@ Inspired by PHP [print_r()](https://www.php.net/manual/en/function.print-r.php) ## WHAT'S IN THE LIBRARY | FUNCTION | NOTES | |----------|-------| -| dump_bit_array$ | Returns string with a dump of an array of bits | -| dump_unsigned_bit_array$ | Returns string with a dump of an array of unsigned bits | -| dump_byte_array$ | Returns string with a dump of an array of bytes | -| dump_unsigned_byte_array$ | Returns string with a dump of an array of unsigned bytes | -| dump_unsigned_integer$ | Returns string with a dump of an array of unsigned integers | -| dump_unsigned_byte_array_as_hex$ | Returns string with a dump of an array of unsigned bytes as hex | -| dump_unsigned_byte_array_as_ascii$ | Returns string with a dump of an array of unsigned bytes as hex and ASCII | -| dump_string$ | Includes handy output of the strings length. | -| dump_string_array$ | Works on 1 dimensional arrays only (right now). | -| dump_integer_array$ | Works on 1 dimensional arrays only (right now). | -| dump_single_array$ | Works on 1 dimensional arrays only (right now). | -| dump_long_array$ | Works on 1 dimensional arrays only (right now). | -| dump_double_array$ | Works on 1 dimensional arrays only (right now). | -| dump_dict$ | Dump a dictionary object and it's contents. | +| DUMP.bit_array$ | Returns string with a dump of an array of bits | +| DUMP.unsigned_bit_array$ | Returns string with a dump of an array of unsigned bits | +| DUMP.byte_array$ | Returns string with a dump of an array of bytes | +| DUMP.unsigned_byte_array$ | Returns string with a dump of an array of unsigned bytes | +| DUMP.unsigned_integer$ | Returns string with a dump of an array of unsigned integers | +| DUMP.unsigned_byte_array_as_hex$ | Returns string with a dump of an array of unsigned bytes as hex | +| DUMP.unsigned_byte_array_as_ascii$ | Returns string with a dump of an array of unsigned bytes as hex and ASCII | +| DUMP.string$ | Includes handy output of the strings length. | +| DUMP.string_array$ | Works on 1 dimensional arrays only (right now). | +| DUMP.integer_array$ | Works on 1 dimensional arrays only (right now). | +| DUMP.single_array$ | Works on 1 dimensional arrays only (right now). | +| DUMP.long_array$ | Works on 1 dimensional arrays only (right now). | +| DUMP.double_array$ | Works on 1 dimensional arrays only (right now). | +| DUMP.dict$ | Dump a dictionary object and it's contents. | @@ -49,7 +49,7 @@ _Here is an example of a dump of a string array:_ ```basic DIM words$(2) words$(0) = "foo" : words$(1) = "bar" : words$(2) = "baz" -PRINT dump_string_array(words$(), "words") +PRINT DUMP.string_array(words$(), "words") ``` _output:_ diff --git a/FLATTEN.BAS b/FLATTEN.BAS new file mode 100644 index 0000000..05b7e27 --- /dev/null +++ b/FLATTEN.BAS @@ -0,0 +1,80 @@ +'' +' MergeFile +' +' Takes a source target basic file and flattens all the includes to be in a +' single file. This lets you see what the compiler is compiling if you have +' multiple file includes. It works across directories, too. +' +' HOW TO USE: +' ----------- +' Change target$ to your source basic file you want to flatten into one file. +' Change outfile$ to the file path of the flattened/merged file. +' Change indent to a number you prefer. This helps debug stuff in code folding. +' +' @author Steve McNeill (every thing) +' @author Rick Christy (idea) +' @see https://qb64phoenix.com/forum/showthread.php?tid=1335&pid=12085#pid12085 +' + +$Debug +$If WIN Then + Const Slash$ = "/" +$Else + const Slash$ = "/" +$End If + +' CHANGE THIS STUFF +const indent = 8 +target$ = "_OPTION_TEST.bas" +outfile$ = "_OPTION_TEST_FLATTENED.BAS" + +' ----------------------------------------------------------------------------- +' MAIN PROGRAM +' ----------------------------------------------------------------------------- +If target$ = "" Then + Print "Give me a QB64 program to unravel => "; + Input target$ + Print "Give me a name to save the new file under => "; + Input outfile$ +End If + +Open outfile$ For Output As #1 +MergeFile target$ +Dim SHARED stack AS INTEGER +stack% = 0 + +Sub MergeFile (whatfile$) + f = FreeFile + CurrentDir$ = _CWD$ + i = _InStrRev(whatfile$, Slash$) + newdir$ = Left$(whatfile$, i) + If i > 0 Then + ChDir newdir$ + whatfile$ = Mid$(whatfile$, i + 1) + End If + Print whatfile$ + Open whatfile$ For Binary As #f + If LOF(f) Then + Do + Line Input #f, temp$ + If Left$(UCase$(_Trim$(temp$)), 11) = "'$INCLUDE:'" Then + temp$ = _Trim$(temp$) + file$ = Mid$(temp$, 12) + file$ = Left$(file$, Len(file$) - 1) + stack% = stack% +1 + MergeFile file$ + Else + Print #1, STRING$(stack% * indent, " ") + temp$ + End If + Loop Until EOF(f) + End If + ChDir CurrentDir$ + ' See below comment + IF LOF(f) = 0 THEN killempty = 1 ELSE killempty = 0 + Close #f + ' In severe cases of nesting (the catalyst for this MergeFile program LOL) + ' the program can get confused and output 0 byte files in the same dir it + ' ran from. This is a kludge to get rid of those after it runs. + if killempty = 1 THEN KILL whatfile$ + stack% = stack% - 1 +End Sub diff --git a/INPUT/LIGHTBAR.BAS b/INPUT/LIGHTBAR.BAS index 9102d20..d0d5537 100644 --- a/INPUT/LIGHTBAR.BAS +++ b/INPUT/LIGHTBAR.BAS @@ -21,13 +21,12 @@ _BLINK OFF ' Allow high intensity background colors > 7 _CONTROLCHR OFF ' Allow printing of any characters including control chars DIM menu AS LIGHTBAR ' Create a LIGHTBAR menu -DIM opts(13) AS STRING ' Menu contains 13 options (0 indexed array) DIM options(13) AS LIGHTBAR_OPTION ' Menu options storage ' choice variable will contain the choice the user made using either the ' hot key directly, or by using the home, end, or arrow keys to select ' and confirm with ENTER. This var will be -1 if the user hit ESC to abort. -DIM choice AS INTEGER +DIM choice AS INTEGER ' Configure the LIGHTBAR menu menu.opt_bg_color% = 0 : menu.opt_fg_color% = 12 ' Unselected option colors @@ -58,38 +57,38 @@ menu.snd_abrt_vol! = 0.5 ' Populate the LIGHTBAR options - NOTE: Vertical options are padded with spaces ' If you want longer bars, add more spaces or characters that bar is made of IF menu.opt_vertical% = 1 THEN ' Vertical LIGHTBAR menu - opts$(0) = " |P|izza " ' | = delimeter, so |P| for Pizza - opts$(1) = " |R|ibs " ' Notice that Ribs, ... - opts$(2) = " |H|ot Wings " ' Hot Wings, ... - opts$(3) = " |S|alad " ' Salad, ... - opts$(4) = " |B|readsticks " ' ... - opts$(5) = " |A|pple Pie " ' ... - opts$(6) = " |C|innamon Sticks " ' ... - opts$(7) = " |K|rispy Kreme Donuts " ' ... - opts$(8) = " |D|eluxe Pepperoni Bread " ' ... - opts$(9) = " |E|ggplant Parmesan " ' ... - opts$(10) = " |F|ettucinni Alfredo Bowl " ' ... - opts$(11) = " |J|uice for the Bambinos " ' ... - opts$(12) = " |W|ine for Padre + Madre " ' All of the same length - opts$(13) = " |Q|uit " ' However, each can have diff bar length like this. + options(0).txt$ = " |P|izza " ' | = delimeter, so |P| for Pizza + options(1).txt$ = " |R|ibs " ' Notice that Ribs, ... + options(2).txt$ = " |H|ot Wings " ' Hot Wings, ... + options(3).txt$ = " |S|alad " ' Salad, ... + options(4).txt$ = " |B|readsticks " ' ... + options(5).txt$ = " |A|pple Pie " ' ... + options(6).txt$ = " |C|innamon Sticks " ' ... + options(7).txt$ = " |K|rispy Kreme Donuts " ' ... + options(8).txt$ = " |D|eluxe Pepperoni Bread " ' ... + options(9).txt$ = " |E|ggplant Parmesan " ' ... + options(10).txt$ = " |F|ettucinni Alfredo Bowl " ' ... + options(11).txt$ = " |J|uice for the Bambinos " ' ... + options(12).txt$ = " |W|ine for Padre + Madre " ' All of the same length + options(13).txt$ = " |Q|uit " ' However, each can have diff bar length like this. END IF ' Populate the LIGHTBAR options - NOTE: Horizontal options aren't padded. IF menu.opt_vertical% = 0 THEN ' Horizontal LIGHTBAR menu - opts$(0) = " |P|izza " - opts$(1) = " |R|ibs " - opts$(2) = " |H|ot Wings " - opts$(3) = " |S|alad " - opts$(4) = " |B|readsticks " - opts$(5) = " |A|pple Pie " - opts$(6) = " |C|innamon Sticks " - opts$(7) = " |K|rispy Kreme Donut " - opts$(8) = " |D|eluxe Pepperoni Bread " - opts$(9) = " |E|ggplant Parmesan " - opts$(10) = " |F|ettucinni Alfredo Bowl " - opts$(11) = " |J|uice for the Bambinos " - opts$(12) = " |W|ine for Padre + Madre " - opts$(13) = " |Q|uit " + options(0).txt$ = " |P|izza " + options(1).txt$ = " |R|ibs " + options(2).txt$ = " |H|ot Wings " + options(3).txt$ = " |S|alad " + options(4).txt$ = " |B|readsticks " + options(5).txt$ = " |A|pple Pie " + options(6).txt$ = " |C|innamon Sticks " + options(7).txt$ = " |K|rispy Kreme Donut " + options(8).txt$ = " |D|eluxe Pepperoni Bread " + options(9).txt$ = " |E|ggplant Parmesan " + options(10).txt$ = " |F|ettucinni Alfredo Bowl " + options(11).txt$ = " |J|uice for the Bambinos " + options(12).txt$ = " |W|ine for Padre + Madre " + options(13).txt$ = " |Q|uit " END IF ' Draw some basic goofy screen under which we will have a LIGHTBAR menu @@ -106,7 +105,7 @@ COLOR 9, 0 : PRINT " PRESS ESC to abort!" PRINT ' Draw the LIGHTBAR menu, and store the result chosen in choice% -choice% = LIGHTBAR%(menu, opts$(), options()) +choice% = LIGHTBAR%(menu, options()) ' If user did not press ESC to abort show which option they chose. IF choice% <> -1 THEN @@ -114,7 +113,7 @@ IF choice% <> -1 THEN COLOR 11, 0 : PRINT "You chose option "; COLOR 14, 0 : PRINT UCASE$(_TRIM$(STR$(choice%))); COLOR 11, 0 : PRINT ": "; - COLOR 12, 0 : PRINT _TRIM$(opts$(choice%)) + COLOR 12, 0 : PRINT _TRIM$(options(choice%).txt$) IF choice% = 0 THEN COLOR 10, 0 : PRINT "An excellent choice! It is also my favorite!" END IF diff --git a/INPUT/LIGHTBAR.BI b/INPUT/LIGHTBAR.BI index d84bcbd..5a608f3 100644 --- a/INPUT/LIGHTBAR.BI +++ b/INPUT/LIGHTBAR.BI @@ -39,6 +39,7 @@ END TYPE ' UDT for option data TYPE LIGHTBAR_OPTION + txt AS STRING ' Option raw string row AS INTEGER ' Option row col AS INTEGER ' Option column lft AS STRING ' Option left side text diff --git a/INPUT/LIGHTBAR.BM b/INPUT/LIGHTBAR.BM index 044ed4d..4e9c8ac 100644 --- a/INPUT/LIGHTBAR.BM +++ b/INPUT/LIGHTBAR.BM @@ -12,27 +12,26 @@ $LET GJ_LIB_INPUT_LIGHTBAR_INC_BM = 1 '' -' Render a LIGHTBAR menu using options$ +' Render a LIGHTBAR menu ' @param LIGHTBAR menu UDT -' @param STRING opts$ array of menu options ' @param LIGHTBAR_OPTIONS o array to hold LIGHTBAR_OPTIONS ' @return integer choice made (-1 if abort with ESC) ' -FUNCTION LIGHTBAR%(menu AS LIGHTBAR, opt$(), o() AS LIGHTBAR_OPTION) - DIM AS INTEGER row, col, orig_fg, orig_bg, sel - DIM AS INTEGER lb, ub +FUNCTION LIGHTBAR%(menu AS LIGHTBAR, o() AS LIGHTBAR_OPTION) + DIM AS INTEGER row, col,sel, lb, ub + DIM AS INTEGER orig_fg, orig_bg ' Get lower and upper bounds of options array - lb% = LBOUND(opt$) : ub% = UBOUND(opt$) + lb% = LBOUND(o) : ub% = UBOUND(o) ' Capture initial state for cursor and colors - row% = CSRLIN : col% = POS(0) ' Store initial cursor position + row% = CSRLIN : col% = POS(0) ' Store initial cursor position orig_fg% = SCREEN(row%, col%, 1) AND 15 ' Store initial foreground color orig_bg% = SCREEN(row%, col%, 1) \ 16 ' Store initial background color - LIGHTBAR.get_options menu, opt$(), o(), row%, col%, menu.opt_selected% - LIGHTBAR.draw menu, opt$(), o(), menu.opt_selected% - sel% = LIGHTBAR.get_choice%(menu, opt$(), o()) + LIGHTBAR.get_options menu, o(), row%, col%, menu.opt_selected% + LIGHTBAR.draw menu, o(), menu.opt_selected% + sel% = LIGHTBAR.get_choice%(menu, o()) ' Restore original colors COLOR orig_fg%, orig_bg% @@ -51,33 +50,32 @@ END FUNCTION '' ' Get LIGHTBAR options as an array ' @param LIGHTBAR menu -' @param STRING options$ array of string options ' @param LIGHTBAR_OPTIONS array of LIGHTBAR_OPTIONS ' @param INTEGER row% the original row cursor was on ' @param INTEGER col% the original column cursor was on ' @param INTEGER sel% the selected menu item ' -SUB LIGHTBAR.get_options(menu AS LIGHTBAR, options$(), o() AS LIGHTBAR_OPTION, row%, col%, sel%) +SUB LIGHTBAR.get_options(menu AS LIGHTBAR, o() AS LIGHTBAR_OPTION, row%, col%, sel%) DIM AS INTEGER i, lb, ub, key_pos_s, key_pos_e DIM AS INTEGER cur_row, cur_col, w ' Get lower and upper bounds of options array - lb% = LBOUND(options$) : ub% = UBOUND(options$) + lb% = LBOUND(o) : ub% = UBOUND(o) w% = menu.max_width% ' Get the max width for horiz menu cur_row% = row% : cur_col% = col% ' Init current row and current col FOR i% = lb% to ub% ' Extract hot key start and end positions - key_pos_s% = INSTR(0, options$(i%), menu.delimeter$) - key_pos_e% = INSTR(key_pos_s%, options$(i%), menu.delimeter$) + key_pos_s% = INSTR(0, o(i%).txt, menu.delimeter$) + key_pos_e% = INSTR(key_pos_s%, o(i%).txt, menu.delimeter$) ' Extract left and right part of option without key or delimeter - o(i%).lft$ = MID$(options$(i%), 0, key_pos_s%) - o(i%).rgt$ = MID$(options$(i%), key_pos_s% + 3) + o(i%).lft$ = MID$(o(i%).txt, 0, key_pos_s%) + o(i%).rgt$ = MID$(o(i%).txt, key_pos_s% + 3) ' Capture hot key into arrays - o(i%).key$ = MID$(options$(i%), key_pos_s% + 1, 1) + o(i%).key$ = MID$(o(i%).txt, key_pos_s% + 1, 1) ' Capture visible option length o(i%).len% = LEN(o(i%).lft$ + o(i%).key$ + o(i%).rgt$) @@ -109,15 +107,15 @@ END SUB '' ' Draws LIGHTBAR menu ' @param LIGHTBAR menu -' @param STRING options$ array of string options ' @param LIGHTBAR_OPTIONS array of LIGHTBAR_OPTIONS ' @param INTEGER sel% which option is selected ' -SUB LIGHTBAR.draw(menu AS LIGHTBAR, options$(), o() AS LIGHTBAR_OPTION, sel%) - DIM AS INTEGER i, fg, bg, kf, kb, lb, ub +SUB LIGHTBAR.draw(menu AS LIGHTBAR, o() AS LIGHTBAR_OPTION, sel%) + DIM AS INTEGER i, lb, ub + DIM AS INTEGER fg, bg, kf, kb ' Get lower and upper bounds of options array - lb% = LBOUND(options$) : ub% = UBOUND(options$) + lb% = LBOUND(o) : ub% = UBOUND(o) FOR i% = lb% TO ub% ' Walk the array of menu options LOCATE o(i%).row%, o(i%).col% ' Position the option @@ -139,16 +137,15 @@ END SUB '' ' Get choice from user in LIGHTBAR menu ' @param LIGHTBAR menu -' @param STRING options$ array of string options ' @param LIGHTBAR_OPTIONS array of LIGHTBAR_OPTIONS ' @return INTEGER choice user made (-1 = aborted) ' -FUNCTION LIGHTBAR.get_choice%(menu AS LIGHTBAR, options$(), o() AS LIGHTBAR_OPTION) +FUNCTION LIGHTBAR.get_choice%(menu AS LIGHTBAR, o() AS LIGHTBAR_OPTION) DIM k AS STRING DIM AS INTEGER i, key_code, do_move, lb, ub, sel ' Get lower and upper bounds of options array - lb% = LBOUND(options$) : ub% = UBOUND(options$) + lb% = LBOUND(o) : ub% = UBOUND(o) ' Define key constants CONST KEY_ESC = 27 @@ -199,9 +196,9 @@ FUNCTION LIGHTBAR.get_choice%(menu AS LIGHTBAR, options$(), o() AS LIGHTBAR_OPTI SELECT CASE do_move% CASE 1: ' move LIGHTBAR.sound menu.use_sounds%, menu.snd_move_frq!, menu.snd_move_dur!, menu.snd_move_vol! - LIGHTBAR.draw menu, options$(), o(), sel% + LIGHTBAR.draw menu, o(), sel% CASE 2: ' pick - LIGHTBAR.draw menu, options$(), o(), sel% + LIGHTBAR.draw menu, o(), sel% CASE 3: ' abort LIGHTBAR.sound menu.use_sounds%, menu.snd_abrt_frq!, menu.snd_abrt_dur!, menu.snd_abrt_vol! LIGHTBAR.get_choice% = -1 diff --git a/INPUT/LIGHTBAR32.BAS b/INPUT/LIGHTBAR32.BAS index 1e62bb8..45e61ca 100644 --- a/INPUT/LIGHTBAR32.BAS +++ b/INPUT/LIGHTBAR32.BAS @@ -17,14 +17,13 @@ $END IF _TITLE "QB64_GJ_LIB - INPUT LIB - LIGHTBAR32 DEMO" DIM CANVAS AS LONG -CANVAS& = _NEWIMAGE(132 * 8, 50*16, 32) +CANVAS& = _NEWIMAGE(132 * _FONTWIDTH, 50 * _FONTHEIGHT, 32) SCREEN CANVAS& _BLINK OFF ' Allow high intensity background colors > 7 _CONTROLCHR OFF ' Allow printing of any characters including control chars DIM menu AS LIGHTBAR32 ' Create a LIGHTBAR menu -DIM opts(13) AS STRING ' Menu contains 13 options (0 indexed array) DIM options(13) AS LIGHTBAR32_OPTION ' Menu options storage ' choice variable will contain the choice the user made using either the @@ -43,7 +42,7 @@ menu.opt_selected% = 0 menu.opt_vertical% = 1 ' Delimiter can be any single char but must be same char on both sides of key ' OK: "|F|oo", Not OK: "[F]oo" -menu.delimeter$ = "|" +menu.delimeter$ = "|" ' Set max width to screen width menu.max_width% = _WIDTH(0) ' Setup sounds @@ -61,38 +60,38 @@ menu.snd_abrt_vol! = 0.5 ' Populate the LIGHTBAR options - NOTE: Vertical options are padded with spaces ' If you want longer bars, add more spaces or characters that bar is made of IF menu.opt_vertical% = 1 THEN ' Vertical LIGHTBAR menu - opts$(0) = " |P|izza " ' | = delimeter, so |P| for Pizza - opts$(1) = " |R|ibs " ' Notice that Ribs, ... - opts$(2) = " |H|ot Wings " ' Hot Wings, ... - opts$(3) = " |S|alad " ' Salad, ... - opts$(4) = " |B|readsticks " ' ... - opts$(5) = " |A|pple Pie " ' ... - opts$(6) = " |C|innamon Sticks " ' ... - opts$(7) = " |K|rispy Kreme Donuts " ' ... - opts$(8) = " |D|eluxe Pepperoni Bread " ' ... - opts$(9) = " |E|ggplant Parmesan " ' ... - opts$(10) = " |F|ettucinni Alfredo Bowl " ' ... - opts$(11) = " |J|uice for the Bambinos " ' ... - opts$(12) = " |W|ine for Padre + Madre " ' All of the same length - opts$(13) = " |Q|uit " ' However, each can have diff bar length like this. + options(0).txt$ = " |P|izza " ' | = delimeter, so |P| for Pizza + options(1).txt$ = " |R|ibs " ' Notice that Ribs, ... + options(2).txt$ = " |H|ot Wings " ' Hot Wings, ... + options(3).txt$ = " |S|alad " ' Salad, ... + options(4).txt$ = " |B|readsticks " ' ... + options(5).txt$ = " |A|pple Pie " ' ... + options(6).txt$ = " |C|innamon Sticks " ' ... + options(7).txt$ = " |K|rispy Kreme Donuts " ' ... + options(8).txt$ = " |D|eluxe Pepperoni Bread " ' ... + options(9).txt$ = " |E|ggplant Parmesan " ' ... + options(10).txt$ = " |F|ettucinni Alfredo Bowl " ' ... + options(11).txt$ = " |J|uice for the Bambinos " ' ... + options(12).txt$ = " |W|ine for Padre + Madre " ' All of the same length + options(13).txt$ = " |Q|uit " ' However, each can have diff bar length like this. END IF ' Populate the LIGHTBAR options - NOTE: Horizontal options aren't padded. IF menu.opt_vertical% = 0 THEN ' Horizontal LIGHTBAR menu - opts$(0) = " |P|izza " - opts$(1) = " |R|ibs " - opts$(2) = " |H|ot Wings " - opts$(3) = " |S|alad " - opts$(4) = " |B|readsticks " - opts$(5) = " |A|pple Pie " - opts$(6) = " |C|innamon Sticks " - opts$(7) = " |K|rispy Kreme Donut " - opts$(8) = " |D|eluxe Pepperoni Bread " - opts$(9) = " |E|ggplant Parmesan " - opts$(10) = " |F|ettucinni Alfredo Bowl " - opts$(11) = " |J|uice for the Bambinos " - opts$(12) = " |W|ine for Padre + Madre " - opts$(13) = " |Q|uit " + options(0).txt$ = " |P|izza " + options(1).txt$ = " |R|ibs " + options(2).txt$ = " |H|ot Wings " + options(3).txt$ = " |S|alad " + options(4).txt$ = " |B|readsticks " + options(5).txt$ = " |A|pple Pie " + options(6).txt$ = " |C|innamon Sticks " + options(7).txt$ = " |K|rispy Kreme Donut " + options(8).txt$ = " |D|eluxe Pepperoni Bread " + options(9).txt$ = " |E|ggplant Parmesan " + options(10).txt$ = " |F|ettucinni Alfredo Bowl " + options(11).txt$ = " |J|uice for the Bambinos " + options(12).txt$ = " |W|ine for Padre + Madre " + options(13).txt$ = " |Q|uit " END IF ' Draw some basic goofy screen under which we will have a LIGHTBAR32 menu @@ -109,7 +108,7 @@ COLOR LB_EGA(9), LB_EGA(0) : PRINT " PRESS ESC to abort!" PRINT ' Draw the LIGHTBAR32 menu, and store the result chosen in choice% -choice% = LIGHTBAR32%(menu, opts$(), options()) +choice% = LIGHTBAR32%(menu, options()) ' If user did not press ESC to abort show which option they chose. IF choice% <> -1 THEN @@ -117,7 +116,7 @@ IF choice% <> -1 THEN COLOR LB_EGA(11), LB_EGA(0) : PRINT "You chose option "; COLOR LB_EGA(14), LB_EGA(0) : PRINT UCASE$(_TRIM$(STR$(choice%))); COLOR LB_EGA(11), LB_EGA(0) : PRINT ": "; - COLOR LB_EGA(12), LB_EGA(0) : PRINT _TRIM$(opts$(choice%)) + COLOR LB_EGA(12), LB_EGA(0) : PRINT _TRIM$(options(choice%).txt$) IF choice% = 0 THEN COLOR LB_EGA(10), LB_EGA(0) : PRINT "An excellent choice! It is also my favorite!" END IF diff --git a/INPUT/LIGHTBAR32.BI b/INPUT/LIGHTBAR32.BI index f8d504b..3881e03 100644 --- a/INPUT/LIGHTBAR32.BI +++ b/INPUT/LIGHTBAR32.BI @@ -39,6 +39,7 @@ END TYPE ' UDT for option data TYPE LIGHTBAR32_OPTION + txt AS STRING ' Option raw string row AS INTEGER ' Option row col AS INTEGER ' Option column lft AS STRING ' Option left side text diff --git a/INPUT/LIGHTBAR32.BM b/INPUT/LIGHTBAR32.BM index f6e8f86..1ba5411 100644 --- a/INPUT/LIGHTBAR32.BM +++ b/INPUT/LIGHTBAR32.BM @@ -12,27 +12,26 @@ $LET GJ_LIB_INPUT_LIGHTBAR32_INC_BM = 1 '' -' Render a LIGHTBA32R menu using options$ +' Render a LIGHTBA32R menu ' @param LIGHTBAR32 menu UDT -' @param STRING opts$ array of menu options ' @param LIGHTBAR_OPTIONS o array to hold LIGHTBAR_OPTIONS ' @return integer choice made (-1 if abort with ESC) ' -FUNCTION LIGHTBAR32%(menu AS LIGHTBAR32, opt$(), o() AS LIGHTBAR32_OPTION) +FUNCTION LIGHTBAR32%(menu AS LIGHTBAR32, o() AS LIGHTBAR32_OPTION) DIM AS INTEGER row, col, sel, lb, ub DIM AS _UNSIGNED LONG orig_fg, orig_bg ' Get lower and upper bounds of options array - lb% = LBOUND(opt$) : ub% = UBOUND(opt$) + lb% = LBOUND(o) : ub% = UBOUND(o) ' Capture initial state for cursor and colors - row% = CSRLIN : col% = POS(0) ' Store initial cursor position + row% = CSRLIN : col% = POS(0) ' Store initial cursor position orig_fg~& = LB_EGA(7) ' Store initial foreground color orig_bg~& = LB_EGA(0) ' Store initial background color - LIGHTBAR32.get_options menu, opt$(), o(), row%, col%, menu.opt_selected% - LIGHTBAR32.draw menu, opt$(), o(), menu.opt_selected% - sel% = LIGHTBAR32.get_choice%(menu, opt$(), o()) + LIGHTBAR32.get_options menu, o(), row%, col%, menu.opt_selected% + LIGHTBAR32.draw menu, o(), menu.opt_selected% + sel% = LIGHTBAR32.get_choice%(menu, o()) ' Restore original colors COLOR orig_fg~&, orig_bg~& @@ -51,33 +50,32 @@ END FUNCTION '' ' Get LIGHTBAR32 options as an array ' @param LIGHTBAR32 menu -' @param STRING options$ array of string options ' @param LIGHTBAR_OPTIONS array of LIGHTBAR_OPTIONS ' @param INTEGER row% the original row cursor was on ' @param INTEGER col% the original column cursor was on ' @param INTEGER sel% the selected menu item ' -SUB LIGHTBAR32.get_options(menu AS LIGHTBAR32, options$(), o() AS LIGHTBAR32_OPTION, row%, col%, sel%) +SUB LIGHTBAR32.get_options(menu AS LIGHTBAR32, o() AS LIGHTBAR32_OPTION, row%, col%, sel%) DIM AS INTEGER i, lb, ub, key_pos_s, key_pos_e DIM AS INTEGER cur_row, cur_col, w ' Get lower and upper bounds of options array - lb% = LBOUND(options$) : ub% = UBOUND(options$) + lb% = LBOUND(o) : ub% = UBOUND(o) w% = menu.max_width% ' Get the max width for horiz menu cur_row% = row% : cur_col% = col% ' Init current row and current col FOR i% = lb% to ub% ' Extract hot key start and end positions - key_pos_s% = INSTR(0, options$(i%), menu.delimeter$) - key_pos_e% = INSTR(key_pos_s%, options$(i%), menu.delimeter$) + key_pos_s% = INSTR(0, o(i%).txt, menu.delimeter$) + key_pos_e% = INSTR(key_pos_s%, o(i%).txt, menu.delimeter$) ' Extract left and right part of option without key or delimeter - o(i%).lft$ = MID$(options$(i%), 0, key_pos_s%) - o(i%).rgt$ = MID$(options$(i%), key_pos_s% + 3) + o(i%).lft$ = MID$(o(i%).txt, 0, key_pos_s%) + o(i%).rgt$ = MID$(o(i%).txt, key_pos_s% + 3) ' Capture hot key into arrays - o(i%).key$ = MID$(options$(i%), key_pos_s% + 1, 1) + o(i%).key$ = MID$(o(i%).txt, key_pos_s% + 1, 1) ' Capture visible option length o(i%).len% = LEN(o(i%).lft$ + o(i%).key$ + o(i%).rgt$) @@ -109,16 +107,15 @@ END SUB '' ' Draws LIGHTBAR32 menu ' @param LIGHTBAR32 menu -' @param STRING options$ array of string options ' @param LIGHTBAR_OPTIONS array of LIGHTBAR_OPTIONS ' @param INTEGER sel% which option is selected ' -SUB LIGHTBAR32.draw(menu AS LIGHTBAR32, options$(), o() AS LIGHTBAR32_OPTION, sel%) +SUB LIGHTBAR32.draw(menu AS LIGHTBAR32, o() AS LIGHTBAR32_OPTION, sel%) DIM AS INTEGER i, lb, ub DIM AS _UNSIGNED LONG fg, bg, kf, kb ' Get lower and upper bounds of options array - lb% = LBOUND(options$) : ub% = UBOUND(options$) + lb% = LBOUND(o) : ub% = UBOUND(o) FOR i% = lb% TO ub% ' Walk the array of menu options LOCATE o(i%).row%, o(i%).col% ' Position the option @@ -140,16 +137,15 @@ END SUB '' ' Get choice from user in LIGHTBAR menu ' @param LIGHTBAR32 menu -' @param STRING options$ array of string options ' @param LIGHTBAR_OPTIONS array of LIGHTBAR_OPTIONS ' @return INTEGER choice user made (-1 = aborted) ' -FUNCTION LIGHTBAR32.get_choice%(menu AS LIGHTBAR32, options$(), o() AS LIGHTBAR32_OPTION) +FUNCTION LIGHTBAR32.get_choice%(menu AS LIGHTBAR32, o() AS LIGHTBAR32_OPTION) DIM k AS STRING DIM AS INTEGER i, key_code, do_move, lb, ub, sel ' Get lower and upper bounds of options array - lb% = LBOUND(options$) : ub% = UBOUND(options$) + lb% = LBOUND(o) : ub% = UBOUND(o) ' Define key constants CONST KEY_ESC = 27 @@ -200,9 +196,9 @@ FUNCTION LIGHTBAR32.get_choice%(menu AS LIGHTBAR32, options$(), o() AS LIGHTBAR3 SELECT CASE do_move% CASE 1: ' move LIGHTBAR32.sound menu.use_sounds%, menu.snd_move_frq!, menu.snd_move_dur!, menu.snd_move_vol! - LIGHTBAR32.draw menu, options$(), o(), sel% + LIGHTBAR32.draw menu, o(), sel% CASE 2: ' pick - LIGHTBAR32.draw menu, options$(), o(), sel% + LIGHTBAR32.draw menu, o(), sel% CASE 3: ' abort LIGHTBAR32.sound menu.use_sounds%, menu.snd_abrt_frq!, menu.snd_abrt_dur!, menu.snd_abrt_vol! LIGHTBAR32.get_choice% = -1 diff --git a/INPUT/README.md b/INPUT/README.md index 77c2761..b2a65d7 100644 --- a/INPUT/README.md +++ b/INPUT/README.md @@ -1,6 +1,6 @@ # [QB64_GJ_LIB](../README.md) ## GRYMMJACK'S INPUT LIBRARY - +> NOTE: QB64PE v3.8.0+ Required (SOUND updates) ## WHAT'S IN THE LIBRARY @@ -10,12 +10,156 @@ | [LIGHTBAR32.BAS](LIGHTBAR32.BAS) | A reusable modular lightbar menu (RGB32) | +### THINGS I WANT TO ADD (in order of preference/usefulness) + +- [ ] **Picked color** + - When lightbar menu option is chosen, redraws menu with the color so it appears picked. + - This is handy to reinforce to the user what they chose, esp. when lightbar menus might be different colors. + - A background color change that was used to choose options and staying like that when they press enter is helpful but can be improved by using a picked color. + - If no picked color set, use regular bar color bg and fg. + +- [ ] **Individual colors per option** + - Use default if not set in individual options + +- [ ] **Individual sounds for pick sound** + - Use default if not set in individual options + +- [ ] **Non-hot key mode** + - Technically, this already works if we do not use delimiters and put an extra space in front of the option `.txt` + - Just want to make the option official and add documentation + +- [ ] **Make arrows more intuitive** + - So up and down arrows only work when in vertical orientation or when applicable + - If run out of space to draw options vertically against _SCREENHEIGHT, stack to make another column to the right side and allow left and right arrows to go move between vertical column stacks. + - Same true for horizontal where if multiple rows are drawn then allow the use of the up and down arrows to go to the option above or below + - Use logical math like - if many rows from wrap, and selected option has a row above or below it, and if the option is longer in width and width of selected option could logically move to several options above or below, make the choice based on the ceter-most or left-most option. + ``` + foo bar1 baz1 bop1 foo2 bar2 baz2 bop foo bar baz bop foo + bar [battlestar-baztastica-9000] bop foo bar baz bop + + [] = selected option + + the available candidates for moving up are: + - bar1 baz1 bop1 foo1 bar2 baz2 + - but as a human i would expect either bop1 or foo1 to be where my selection moves + - when tied for center, use center-most left side + - in example above, that would be bop1 + + foo bar1 baz1 bop1 foo2 bar2 baz bop foo bar baz bop foo + bar [battlestar-baztastica] bop foo bar baz bop + + [] = selected option + + the available candidates for moving up are: + - bar1 baz1 bop1 foo1 bar2 + - but as a human i would expect either bop1 to be where my selection moves + - center is almost absolute + - in example above, + - left side of selected option has 9 chars + - right side of selected option has 8 chars + - bop1 is 9 characters from the leftmost side of selected option + + foo cylon-base-star baz1 bop1 foo2 bar2 baz bop foo bar baz bop foo + bar [battlestar-baztastica] bop foo bar baz bop + + the available candidates for moving up are: + - cylon-base-star baz1 + - but as a human i would expect cylon-base-star to be where my selection moves + - center is not absolute center + - logic = + 1. find options above current selected option as move candidates + 2. weight move candidates based on option length + 3. longer options get more weight + 4. move to the weightiest + 5. if duplicate weights exist, to break ties use move candidate closest to selected options center. + ``` + +- [ ] **Multiple choice support** + - Present options, then use a key to select or unselect. + - possible args: + - select_keycode: (default space) + - min_selections_allowed + - max_selections_allowed + +- [ ] **Random choice select** + - In this way interest can be gained. Imagine you don't care which option a user chooses, but you want it to be chance based. Why would you want this? Users who are playing a game where chance is a factor will get into the habit of hotkey sequencing. Imagine you are in a combat, and you want to fight, and the foes you want to choose from are in a lightbar menu. BUT your characters composure is impacted negatively so he is in fight-or-flight mode, and thus his own actions would be a little chaotic and random. We can simulate this with our random choice option. Picking a foe to fight in the list of foes for targetting an attack being random makes the player have to slow down and take a deep breath -- what if you target the foe your previous party member targetted and likelihood is high that the monster will be dead when it's your turn to attack? ... aha! gotcha. you just swung at the air! next time don't be so confident... + - In this mode, hot keys should be skipped. + +- [ ] **Slot machine select** + - Imagine the Price is Right game show wheel. + - In this mode, hot keys should be skipped. + - While the lightbar menu isn't a wheel, it could spin by automatically moving the selected option through a linear sequence. + - Args for this could be + - speed: at which the options animate + - friction: at which the options finally come to a stop. lower friction = longer spin time. + - easing-start function to make wheel bar feel heavy or light + - easting-end function + - user_activated_spin: true/false - if false automatic spin happens, otherwise activation key needs to be pressed to engage. + - user_activated_stop: true/false - if false spin only stops when user presses activation key to stop. + - spin_activation_keycode: key used to initiate the spin + - stop_activation_keycode: key used to stop the spin + +- [ ] **Auto move with a speed option** + - Here is another idea where the lightbar selection movement could be chaotic + - In this mode, hot keys should be skipped. + - Obviously speed should be a parameter. ;) + - Choosing options in ways like: + - Random - complete chaos + - Semi-random - random 1 time, then neighbor options n+/- selected option x times, repeat + - Weighted random - options can have weights when the weight is higher, it is more likely to be chosen. + - This could be used for simple games. Like the menu draws, but it moves at speed on it's own and the user can only hit the activation keycode (no arrow keys to navigate, etc.) for elements of chance. + +- [ ] **pipeprint support** for option text + - So that options can be super fancy with multiple colors and formatting through `pipeprint` + +- [ ] **adjacent selector support** + - Easier to show with example, but, options are presented just like they normally would be but instead of a bar simulating movement and selection changing colors, a text string chooses instead: + ``` + example: -> is the text string chooser. + + Pick which vegetable you want on your cheese burger: + + -> Onion + Tomato + Pickle + Lettuce + + ``` + - Possible options: + - txt_chooser_left: + - left side text + - txt_chooser_right: + - chooser_type: left, right, wrap ## HOW TO USE LIGHTBAR > First things first, there are 2 separate LIGHTBAR modules; a SCREEN 0 text > version, and a 32bit BPP version. +### TL;DR: The bare-bones example: +```basic +'$INCLUDE:'LIGHTBAR.BI' +_BLINK OFF +DIM menu AS LIGHTBAR +DIM options(1) AS LIGHTBAR_OPTION +DIM choice AS INTEGER +menu.opt_bg_color% = 0 : menu.opt_fg_color% = 12 ' Unselected option colors +menu.bar_bg_color% = 3 : menu.bar_fg_color% = 11 ' Selected option colors +menu.bar_kf_color% = 14 : menu.bar_kb_color% = 3 ' Selected hot key colors +menu.key_bg_color% = 0 : menu.key_fg_color% = 14 ' Unselected hot key colors +menu.opt_vertical% = 0 +menu.delimeter$ = "|" +menu.max_width% = _WIDTH(0) +options(0).txt = " |Y|es " +options(1).txt = " |N|o " +PRINT "Hey, is this neat or what? "; +choice% = LIGHTBAR%(menu, options()) +LOCATE 2, 1 +IF choice% = 0 THEN PRINT "IKR!?" +IF choice% = 1 THEN PRINT "Pffft! whatever..." +'$INCLUDE:'LIGHTBAR.BM' +``` + ### Setup for LIGHTBAR (either version) 1. Include in your project before any other code `LIGHTBAR.BI` at the top 2. Include in your project after all other code `LIGHTBAR.BM` at the bottom @@ -23,11 +167,10 @@ 4. In each menu choice use a common delimiter for your hot keys (if you like) 5. Insert `DIM menu AS LIGHTBAR` (or LIGHTBAR32 if using RGB32 one) 6. Configure your `menu` LIGHTBAR (see below) -7. Insert `DIM opts(3) AS STRING` (you can call `opts` whatever you want) -8. Insert `DIM options(3) AS LIGHTBAR_OPTION` (or LIGHTBAR32_OPTION...) -9. Position your cursor where you want the menu to start -10. Insert `choice% = LIGHTBAR%(menu, opts$(), options())` -11. Which option user picked will be an INTEGER in choice% according to array +7. Insert `DIM options(3) AS LIGHTBAR_OPTION` (or LIGHTBAR32_OPTION...) +8. Position your cursor where you want the menu to start +9. Insert `choice% = LIGHTBAR%(menu, opts$(), options())` +10. Which option user picked will be an INTEGER in choice% according to array #### LIGHTBAR Configuration > NOTE: Colors are INTEGERS to be compatible with SCREEN 0 mode @@ -95,4 +238,4 @@ See [LIGHTBAR32.BAS](LIGHTBAR32.BAS) for a full example. > Screenshot of output from [LIGHTBAR32.BAS](LIGHTBAR32.BAS) -![](LIGHTBAR32.png) \ No newline at end of file +![](LIGHTBAR32.png) diff --git a/MISC/MISC.BI b/MISC/MISC.BI new file mode 100644 index 0000000..2259abb --- /dev/null +++ b/MISC/MISC.BI @@ -0,0 +1,13 @@ +'' +' QB64_GJ_LIB +' GRYMMJACK'S MISC LIB +' +' Miscellaneous helpful functions and subs that don't fit anywhere else :) +' +' USAGE: +' Insert '$INCLUDE:'path_to_GJ_LIB/MISC/MISC.BI' at the top of file +' Insert '$INCLUDE:'path_to_GJ_LIB/MISC/MISC.BM' at the bottom of file +' +' @author Rick Christy +' +$LET GJ_LIB_MISC_INC_BI = 1 diff --git a/MISC/MISC.BM b/MISC/MISC.BM new file mode 100644 index 0000000..bd70fba --- /dev/null +++ b/MISC/MISC.BM @@ -0,0 +1,42 @@ +'' +' QB64_GJ_LIB +' GRYMMJACK'S MISC LIB +' +' Miscellaneous helpful functions and subs that don't fit anywhere else :) +' +' USAGE: +' Insert '$INCLUDE:'path_to_GJ_LIB/MISC/MISC.BI' at the top of file +' Insert '$INCLUDE:'path_to_GJ_LIB/MISC/MISC.BM' at the bottom of file +' +' @author Rick Christy +' +$LET GJ_LIB_MISC_INC_BM = 1 + + +'' +' Returns a number rounded to a fixed number of decimal places +' @param DOUBLE num Number to round +' @param INTEGER places number of places to round to +' @return DOUBLE rounded number +' +FUNCTION num_fix#(num#, places%) + DIM AS DOUBLE multiplier + DIM AS STRING mult_str + mult_str$ = _TRIM$("1" + STRING$(places%, "0")) + multiplier# = VAL(mult_str$) + num_fix# = FIX(num# * multiplier# + SGN(num#) * .5) / multiplier# +END FUNCTION + + +'' +' Clamps a INTEGER between a minimum and maximum range +' @param INTEGER num number to clamp +' @param INTEGER min minimum allowed +' @param INTEGER max maximum allowed +' @return INTEGER clamped number +' +FUNCTION clamp_int%(num%, min%, max%) + IF num% > max% THEN num% = max% + IF num% < min% THEN num% = min% + clamp_int% = num% +END FUNCTION diff --git a/MISC/README.md b/MISC/README.md new file mode 100644 index 0000000..168e961 --- /dev/null +++ b/MISC/README.md @@ -0,0 +1,22 @@ +# [QB64_GJ_LIB](../README.md) +## GRYMMJACK'S MISC LIBRARY + +> Miscellaneous helpful functions and subs that don't fit anywhere else :) + + + +## WHAT'S IN THE LIBRARY +| SUB / FUNCTION | NOTES | +|----------------|-------| +| num_fix | Returns a number rounded to a fixed number of decimal places | +| clamp_int | Clamps a INTEGER between a minimum and maximum range | + + + +### USAGE for MISC LIB (separately) +```basic +'...your code here... + +'Insert at bottom of code: +'$INCLUDE:'path_to_GJ_LIB/MISC/MISC.BM' +``` diff --git a/PIPEPRINT/PIPEPRINT.BAS b/PIPEPRINT/PIPEPRINT.BAS index 54ff9f3..f64073e 100644 --- a/PIPEPRINT/PIPEPRINT.BAS +++ b/PIPEPRINT/PIPEPRINT.BAS @@ -17,6 +17,8 @@ $IF GJ_LIB_PIPEPRINT_INC_BI = UNDEFINED THEN '$INCLUDE:'PIPEPRINT.BI' $END IF +OPTION _EXPLICIT +OPTION _EXPLICITARRAY _TITLE "QB64_GJ_LIB PIPEPRINT LIB TESTS" @@ -38,82 +40,86 @@ $ENDIF _CONTROLCHR OFF LOCATE 1,1,1 ' Initialize the cursor and turn it on -DIM SHARED PIPE_TESTS_DICT(16) AS DICT -PIPE_TESTS_DICT(0).key = "PIPETEST_COLORS_STANDARD_FG" -PIPE_TESTS_DICT(0).val = "|CL|08-|07-|15-|11[|14 YELLOW THING |11]|15-|07-|08-" -PIPE_TESTS_DICT(1).key = "PIPETEST_COLORS_STANDARD_BG" -PIPE_TESTS_DICT(1).val = "|CL|CY|24 |23 |31 |16 FOO |31 |23 |24 |16|07" -PIPE_TESTS_DICT(2).key = "PIPETEST_POSITIONING" -PIPE_TESTS_DICT(2).val = "|CL|[X30|[Y011,30|[X40|[Y011,40|[X30|[Y2020,30|[Y20|[X4020,40|[Y22|[X01" -PIPE_TESTS_DICT(3).key = "PIPETEST_CURSORS" -PIPE_TESTS_DICT(3).val = "|CL|[0CURSOR OFF" -PIPE_TESTS_DICT(4).key = "PIPETEST_COLORS_ICE" -PIPE_TESTS_DICT(4).val = "|CL|CY|25|15 BRIGHT WHITE ON BRIGHT BLUE |[1CURSOR ON|16|07" -PIPE_TESTS_DICT(5).key = "PIPETEST_MOVE_AB" -PIPE_TESTS_DICT(5).val = "|CL|[X10|[Y1010,10 line.|[A01Up one line|[B01Down one line" -PIPE_TESTS_DICT(6).key = "PIPETEST_MOVE_CD" -PIPE_TESTS_DICT(6).val = "|CL|[X10|[Y1010,10 line.|[C10Right 10|[D10Left 10" -PIPE_TESTS_DICT(7).key = "PIPETEST_RIGHT_JUSTIFY" -PIPE_TESTS_DICT(7).val = "|CL|@R{RIGHT JUSTIFIED TEXT}" -PIPE_TESTS_DICT(8).key = "PIPETEST_LEFT_JUSTIFY" -PIPE_TESTS_DICT(8).val = "|CL|[X20start|@L{LEFT JUSTIFIED TEXT}BOBO" -PIPE_TESTS_DICT(9).key = "PIPETEST_CENTER_JUSTIFY" -PIPE_TESTS_DICT(9).val = "|CL|[X40|@C{CENTER JUSTIFIED TEXT}" -PIPE_TESTS_DICT(10).key = "PIPETEST_BAR_TEST" -PIPE_TESTS_DICT(10).val = "|CL|23|16|@D79 |@R{RIGHT JUSTIFIED TEXT}" -PIPE_TESTS_DICT(11).key = "PIPETEST_PIPES" -PIPE_TESTS_DICT(11).val = "|CL|08|PI|09|PI|10|PI|15 PIPES!" -PIPE_TESTS_DICT(12).key = "PIPETEST_CRLF" -PIPE_TESTS_DICT(12).val = "|CL|07|16FOO BAR BAZ|CRBOP BIZ BUZ|CRBIP BEP BAP" -PIPE_TESTS_DICT(13).key = "PIPETEST_BOX_TEST" -PIPE_TESTS_DICT(13).val = "|CL|23|00|@D11 |[B01|[D11 |[C09 |[B01|[D11|@D11 |15|00|16|07|CR" -PIPE_TESTS_DICT(14).key = "PIPETEST_PIPE_STRING1" -PIPE_TESTS_DICT(14).val = "|CL|08-|07-|15-|11[|14 YELLOW THING |11]|15-|07-|08-" -PIPE_TESTS_DICT(15).key = "PIPETEST_FG_COLORS" +DIM SHARED PIPE_TESTS_DICT(16) AS DICTIONARY +PIPE_TESTS_DICT(0).key$ = "PIPETEST_COLORS_STANDARD_FG" +PIPE_TESTS_DICT(0).val$ = "|CL|08-|07-|15-|11[|14 YELLOW THING |11]|15-|07-|08-" +PIPE_TESTS_DICT(1).key$ = "PIPETEST_COLORS_STANDARD_BG" +PIPE_TESTS_DICT(1).val$ = "|CL|CY|24 |23 |31 |16 FOO |31 |23 |24 |16|07" +PIPE_TESTS_DICT(2).key$ = "PIPETEST_POSITIONING" +PIPE_TESTS_DICT(2).val$ = "|CL|[X30|[Y011,30|[X40|[Y011,40|[X30|[Y2020,30|[Y20|[X4020,40|[Y22|[X01" +PIPE_TESTS_DICT(3).key$ = "PIPETEST_CURSORS" +PIPE_TESTS_DICT(3).val$ = "|CL|[0CURSOR OFF" +PIPE_TESTS_DICT(4).key$ = "PIPETEST_COLORS_ICE" +PIPE_TESTS_DICT(4).val$ = "|CL|CY|25|15 BRIGHT WHITE ON BRIGHT BLUE |[1CURSOR ON|16|07" +PIPE_TESTS_DICT(5).key$ = "PIPETEST_MOVE_AB" +PIPE_TESTS_DICT(5).val$ = "|CL|[X10|[Y1010,10 line.|[A01Up one line|[B01Down one line" +PIPE_TESTS_DICT(6).key$ = "PIPETEST_MOVE_CD" +PIPE_TESTS_DICT(6).val$ = "|CL|[X10|[Y1010,10 line.|[C10Right 10|[D10Left 10" +PIPE_TESTS_DICT(7).key$ = "PIPETEST_RIGHT_JUSTIFY" +PIPE_TESTS_DICT(7).val$ = "|CL|@R{RIGHT JUSTIFIED TEXT}" +PIPE_TESTS_DICT(8).key$ = "PIPETEST_LEFT_JUSTIFY" +PIPE_TESTS_DICT(8).val$ = "|CL|[X20start|@L{LEFT JUSTIFIED TEXT}BOBO" +PIPE_TESTS_DICT(9).key$ = "PIPETEST_CENTER_JUSTIFY" +PIPE_TESTS_DICT(9).val$ = "|CL|[X40|@C{CENTER JUSTIFIED TEXT}" +PIPE_TESTS_DICT(10).key$ = "PIPETEST_BAR_TEST" +PIPE_TESTS_DICT(10).val$ = "|CL|23|16|@D79 |@R{RIGHT JUSTIFIED TEXT}" +PIPE_TESTS_DICT(11).key$ = "PIPETEST_PIPES" +PIPE_TESTS_DICT(11).val$ = "|CL|08|PI|09|PI|10|PI|15 PIPES!" +PIPE_TESTS_DICT(12).key$ = "PIPETEST_CRLF" +PIPE_TESTS_DICT(12).val$ = "|CL|07|16FOO BAR BAZ|CRBOP BIZ BUZ|CRBIP BEP BAP" +PIPE_TESTS_DICT(13).key$ = "PIPETEST_BOX_TEST" +PIPE_TESTS_DICT(13).val$ = "|CL|23|00|@D11 |[B01|[D11 |[C09 |[B01|[D11|@D11 |15|00|16|07|CR" +PIPE_TESTS_DICT(14).key$ = "PIPETEST_PIPE_STRING1" +PIPE_TESTS_DICT(14).val$ = "|CL|08-|07-|15-|11[|14 YELLOW THING |11]|15-|07-|08-" +PIPE_TESTS_DICT(15).key$ = "PIPETEST_FG_COLORS" +DIM c AS STRING c$ = "|23|00 BLACK |16" c$ = c$ + "|01 BLUE |02 GREEN |03 CYAN |04 RED |05 MAGENTA |06 YELLOW |07 WHITE " c$ = c$ + "|08 BRIGHT BLACK |09 BRIGHT BLUE |10 BRIGHT GREEN |11 BRIGHT CYAN " c$ = c$ + "|12 BRIGHT RED |13 BRIGHT MAGENTA |14 BRIGHT YELLOW |15 BRIGHT WHITE " -PIPE_TESTS_DICT(15).val = c$ -PIPE_TESTS_DICT(16).key = "PIPETEST_BG_COLORS" +PIPE_TESTS_DICT(15).val$ = c$ +PIPE_TESTS_DICT(16).key$ = "PIPETEST_BG_COLORS" c$ = "|CY|15|16 BLACK " c$ = c$ + "|15|17 BLUE |18 GREEN |19 CYAN |20 RED |21 MAGENTA |22 YELLOW |23 WHITE " c$ = c$ + "|24 BRIGHT BLACK |25 BRIGHT BLUE |26 BRIGHT GREEN |27 BRIGHT CYAN " c$ = c$ + "|28 BRIGHT RED |29 BRIGHT MAGENTA |00|30 BRIGHT YELLOW |31 BRIGHT WHITE |16|07" -PIPE_TESTS_DICT(16).val = c$ +PIPE_TESTS_DICT(16).val$ = c$ ' Walk dict for tests -l = LBOUND(PIPE_TESTS_DICT) : u = UBOUND(PIPE_TESTS_DICT) -FOR i = 0 TO u - test_start "PIPEPRINT with " + PIPE_TESTS_DICT(i).key - PRINT PIPEPRINT(PIPE_TESTS_DICT(i).val) - PRINT PIPEPRINT("|07|16SOURCE:|CR"); PIPE_TESTS_DICT(i).val - test_complete +DIM AS INTEGER lb, ub, i +lb% = LBOUND(PIPE_TESTS_DICT) : ub% = UBOUND(PIPE_TESTS_DICT) +FOR i% = 0 TO ub% + CALL test_start("PIPEPRINT with " + PIPE_TESTS_DICT(i).key$) + PRINT PIPEPRINT(PIPE_TESTS_DICT(i).val$) + PRINT PIPEPRINT("|07|16SOURCE:|CR"); PIPE_TESTS_DICT(i).val$ + CALL test_complete NEXT i PRINT : PRINT "All tests complete!" -SUB anykey () +SUB anykey() SLEEP END SUB -SUB test_start (s$) - nil$ = ansi_mode_reset_all +SUB test_start(s$) + DIM nil AS STRING + nil$ = ANSI.mode_reset_all IF GJ_LIB_ANSI_OUTPUT THEN PRINT nil$; CLS PRINT "Press any key to test "; s$ - anykey + CALL anykey END SUB SUB test_complete () + DIM nil AS STRING PRINT "Test complete, press a key." - nil$ = ansi_mode_reset_all + nil$ = ANSI.mode_reset_all IF GJ_LIB_ANSI_OUTPUT THEN PRINT nil$; - anykey + CALL anykey END SUB $IF GJ_LIB_UNIFIED_TESTING = DEFINED AND GJ_LIB_INC_BM = UNDEFINED THEN diff --git a/PIPEPRINT/PIPEPRINT.BM b/PIPEPRINT/PIPEPRINT.BM index 4c51827..012ec50 100644 --- a/PIPEPRINT/PIPEPRINT.BM +++ b/PIPEPRINT/PIPEPRINT.BM @@ -22,11 +22,11 @@ $LET GJ_LIB_PIPEPRINT_INC_BM = 1 '' ' Parses pipe codes and returns ANSI (can emulate ANSI via QB) ' -' @param s$ STRING to parse +' @param STRING s$ to parse ' @return STRING parsed with pipe codes replaced by ANSI codes ' -FUNCTION PIPEPRINT$ (s$) - DIM AS INTEGER w, nums, p, u, i, l, r +FUNCTION PIPEPRINT$(s$) + DIM AS INTEGER w, nums, p, ub, i, r, l, ansi_x, ansi_y DIM AS STRING sout, code, args, find, spaces, txt, t, nopi, repl, char w% = _WIDTH ansi_x% = POS(0) : ansi_y% = CSRLIN @@ -37,40 +37,40 @@ FUNCTION PIPEPRINT$ (s$) REDIM PIPES_POSITIONS(MAX_PIPES) AS INTEGER ' Find the pipes - str_find_pos s$, "|", PIPES_POSITIONS%(), NUM_PIPES_FOUND + CALL STR.find_pos(s$, "|", PIPES_POSITIONS%(), NUM_PIPES_FOUND) IF NUM_PIPES_FOUND = 0 THEN PIPEPRINT$ = s$ EXIT FUNCTION ELSE REDIM _PRESERVE PIPES_POSITIONS(NUM_PIPES_FOUND-1) AS INTEGER - u = UBOUND(PIPES_POSITIONS) + ub% = UBOUND(PIPES_POSITIONS) END IF ' Replace the pipe codes with ANSI codes IF NUM_PIPES_FOUND THEN - FOR i = 0 TO u - p% = PIPES_POSITIONS(i) + FOR i% = 0 TO ub% + p% = PIPES_POSITIONS(i%) code$ = MID$(s$, p%, 3) args$ = "" : char$ = "" : txt$ = "" : t$ = "" : nums% = 0 find$ = "" : repl$ = "" : spaces$ = "" : nopi$ = "" SELECT CASE code$ CASE "|[X", "|[Y", "|[A", "|[B", "|[C", "|[D", "|@D": - args$ = _TRIM$(MID$(s$, p%+3, 2)) + args$ = _TRIM$(MID$(s$, p% + 3, 2)) nums% = ABS(VAL(args$)) find$ = code$ + args$ END SELECT SELECT CASE code$ - CASE "|[X": repl$ = ansi_move_column(nums%) - CASE "|[Y": repl$ = ansi_locate(nums%, ansi_x%) - CASE "|[A": repl$ = ansi_move_up(nums%) - CASE "|[B": repl$ = ansi_move_down(nums%) - CASE "|[C": repl$ = ansi_move_right(nums%) - CASE "|[D": repl$ = ansi_move_left(nums%) + CASE "|[X": repl$ = ANSI.move_column(nums%) + CASE "|[Y": repl$ = ANSI.locate(nums%, ansi_x%) + CASE "|[A": repl$ = ANSI.move_up(nums%) + CASE "|[B": repl$ = ANSI.move_down(nums%) + CASE "|[C": repl$ = ANSI.move_right(nums%) + CASE "|[D": repl$ = ANSI.move_left(nums%) CASE "|@D": IF args$ = "00" THEN nums% = w% END IF - char$ = MID$(s$, p%+5, 1) + char$ = MID$(s$, p% + 5, 1) find$ = code$ + args$ + char$ repl$ = STRING$(nums%, char$) END SELECT @@ -86,15 +86,15 @@ FUNCTION PIPEPRINT$ (s$) CASE "|@R": spaces$ = STRING$(w% - LEN(nopi$), " ") repl$ = spaces$ + txt$ - repl$ = ansi_move_column(1) + repl$ + repl$ = ANSI.move_column(1) + repl$ CASE "|@L": spaces$ = STRING$(w% - LEN(nopi$), " ") repl$ = txt$ + spaces$ - repl$ = ansi_move_column(1) + repl$ + repl$ = ANSI.move_column(1) + repl$ CASE "|@C": spaces$ = STRING$((w% - LEN(nopi$)) \ 2, " ") repl$ = spaces$ + txt$ + spaces$ - repl$ = ansi_move_column(1) + repl$ + repl$ = ANSI.move_column(1) + repl$ END SELECT SELECT CASE code$ CASE "|[0", "|[1", "|[K", "|CL", "|CN", "|CY", "|CR", _ @@ -106,49 +106,49 @@ FUNCTION PIPEPRINT$ (s$) find$ = code$ END SELECT SELECT CASE code$ - CASE "|[0": repl$ = ansi_hide_cursor - CASE "|[1": repl$ = ansi_show_cursor - CASE "|[K": repl$ = ansi_erase_to_eol - CASE "|CL": repl$ = ansi_erase_screen - CASE "|CN": repl$ = ansi_mode_blinking - CASE "|CY": repl$ = ansi_mode_blinking_reset - CASE "|CR": repl$ = ansi_move_down(1) + ansi_move_column(1) + CASE "|[0": repl$ = ANSI.hide_cursor + CASE "|[1": repl$ = ANSI.show_cursor + CASE "|[K": repl$ = ANSI.erase_to_eol + CASE "|CL": repl$ = ANSI.erase_screen + CASE "|CN": repl$ = ANSI.mode_blinking + CASE "|CY": repl$ = ANSI.mode_blinking_reset + CASE "|CR": repl$ = ANSI.move_down(1) + ANSI.move_column(1) CASE "|PI": repl$ = "|" - CASE "|00": repl$ = ansi_fg_black - CASE "|01": repl$ = ansi_fg_blue - CASE "|02": repl$ = ansi_fg_green - CASE "|03": repl$ = ansi_fg_cyan - CASE "|04": repl$ = ansi_fg_red - CASE "|05": repl$ = ansi_fg_magenta - CASE "|06": repl$ = ansi_fg_yellow - CASE "|07": repl$ = ansi_fg_white - CASE "|08": repl$ = ansi_fg_bright_black - CASE "|09": repl$ = ansi_fg_bright_blue - CASE "|10": repl$ = ansi_fg_bright_green - CASE "|11": repl$ = ansi_fg_bright_cyan - CASE "|12": repl$ = ansi_fg_bright_red - CASE "|13": repl$ = ansi_fg_bright_magenta - CASE "|14": repl$ = ansi_fg_bright_yellow - CASE "|15": repl$ = ansi_fg_bright_white - CASE "|16": repl$ = ansi_bg_black - CASE "|17": repl$ = ansi_bg_blue - CASE "|18": repl$ = ansi_bg_green - CASE "|19": repl$ = ansi_bg_cyan - CASE "|20": repl$ = ansi_bg_red - CASE "|21": repl$ = ansi_bg_magenta - CASE "|22": repl$ = ansi_bg_yellow - CASE "|23": repl$ = ansi_bg_white - CASE "|24": repl$ = ansi_bg_bright_black - CASE "|25": repl$ = ansi_bg_bright_blue - CASE "|26": repl$ = ansi_bg_bright_green - CASE "|27": repl$ = ansi_bg_bright_cyan - CASE "|28": repl$ = ansi_bg_bright_red - CASE "|29": repl$ = ansi_bg_bright_magenta - CASE "|30": repl$ = ansi_bg_bright_yellow - CASE "|31": repl$ = ansi_bg_bright_white + CASE "|00": repl$ = ANSI.fg_black + CASE "|01": repl$ = ANSI.fg_blue + CASE "|02": repl$ = ANSI.fg_green + CASE "|03": repl$ = ANSI.fg_cyan + CASE "|04": repl$ = ANSI.fg_red + CASE "|05": repl$ = ANSI.fg_magenta + CASE "|06": repl$ = ANSI.fg_yellow + CASE "|07": repl$ = ANSI.fg_white + CASE "|08": repl$ = ANSI.fg_bright_black + CASE "|09": repl$ = ANSI.fg_bright_blue + CASE "|10": repl$ = ANSI.fg_bright_green + CASE "|11": repl$ = ANSI.fg_bright_cyan + CASE "|12": repl$ = ANSI.fg_bright_red + CASE "|13": repl$ = ANSI.fg_bright_magenta + CASE "|14": repl$ = ANSI.fg_bright_yellow + CASE "|15": repl$ = ANSI.fg_bright_white + CASE "|16": repl$ = ANSI.bg_black + CASE "|17": repl$ = ANSI.bg_blue + CASE "|18": repl$ = ANSI.bg_green + CASE "|19": repl$ = ANSI.bg_cyan + CASE "|20": repl$ = ANSI.bg_red + CASE "|21": repl$ = ANSI.bg_magenta + CASE "|22": repl$ = ANSI.bg_yellow + CASE "|23": repl$ = ANSI.bg_white + CASE "|24": repl$ = ANSI.bg_bright_black + CASE "|25": repl$ = ANSI.bg_bright_blue + CASE "|26": repl$ = ANSI.bg_bright_green + CASE "|27": repl$ = ANSI.bg_bright_cyan + CASE "|28": repl$ = ANSI.bg_bright_red + CASE "|29": repl$ = ANSI.bg_bright_magenta + CASE "|30": repl$ = ANSI.bg_bright_yellow + CASE "|31": repl$ = ANSI.bg_bright_white END SELECT - sout$ = str_replace$(sout$, find$, repl$, 1) - NEXT i + sout$ = STR.replace$(sout$, find$, repl$, 1) + NEXT i% END IF PIPEPRINT$ = sout$ END FUNCTION @@ -157,11 +157,11 @@ END FUNCTION '' ' Strips all pipe codes from a string ' -' @param s$ STRING to strip codes from +' @param STRING s$ to strip codes from ' @return STRING with pipe codes stripped ' -FUNCTION PIPESTRIP$ (s$) - DIM AS INTEGER w, nums, p, u, i, l, r +FUNCTION PIPESTRIP$(s$) + DIM AS INTEGER w, nums, p, ub, i, r, l DIM AS STRING sout, code, args, find, spaces, txt, t, repl, char sout$ = s$ @@ -170,25 +170,25 @@ FUNCTION PIPESTRIP$ (s$) REDIM PIPES_STRIP_POSITIONS(MAX_PIPES_STRIP) AS INTEGER ' Find the pipes - str_find_pos s$, "|", PIPES_STRIP_POSITIONS%(), NUM_PIPES_STRIP_FOUND + CALL STR.find_pos(s$, "|", PIPES_STRIP_POSITIONS%(), NUM_PIPES_STRIP_FOUND) IF NUM_PIPES_STRIP_FOUND = 0 THEN PIPESTRIP$ = s$ EXIT FUNCTION ELSE REDIM _PRESERVE PIPES_STRIP_POSITIONS(NUM_PIPES_STRIP_FOUND-1) AS INTEGER - u = UBOUND(PIPES_STRIP_POSITIONS) + ub% = UBOUND(PIPES_STRIP_POSITIONS) END IF ' Replace the pipe codes with ANSI codes IF NUM_PIPES_STRIP_FOUND THEN - FOR i = 0 TO u - p% = PIPES_STRIP_POSITIONS(i) + FOR i% = 0 TO ub% + p% = PIPES_STRIP_POSITIONS(i%) code$ = MID$(s$, p%, 3) args$ = "" : char$ = "" : txt$ = "" : t$ = "" : nums% = 0 find$ = "" : repl$ = "" : spaces$ = "" SELECT CASE code$ CASE "|[X", "|[Y", "|[A", "|[B", "|[C", "|[D", "|@D": - args$ = _TRIM$(MID$(s$, p%+3, 2)) + args$ = _TRIM$(MID$(s$, p% + 3, 2)) nums% = ABS(VAL(args$)) find$ = code$ + args$ END SELECT @@ -197,7 +197,7 @@ FUNCTION PIPESTRIP$ (s$) IF args$ = "00" THEN nums% = w% END IF - char$ = MID$(s$, p%+5, 1) + char$ = MID$(s$, p% + 5, 1) find$ = code$ + args$ + char$ END SELECT SELECT CASE code$ @@ -217,8 +217,8 @@ FUNCTION PIPESTRIP$ (s$) find$ = code$ END SELECT repl$ = "" - sout$ = str_replace$(sout$, find$, repl$, 1) - NEXT i + sout$ = STR.replace$(sout$, find$, repl$, 1) + NEXT i% END IF PIPESTRIP$ = sout$ END FUNCTION diff --git a/README.md b/README.md index 855a9c3..d546586 100644 --- a/README.md +++ b/README.md @@ -14,6 +14,7 @@ in it's current state. | LIBRARY | PURPOSE | NOTES | |---------|---------|------| | [ANSI](ANSI/README.md) | ANSI text mode | Full ANSI.SYS support plus 256 and RGB color modes as well as a QB64 native ansi emulation mode. | +| [ARR](ARR/README.md) | ARRay Library | A high level library for working with arrays of all types in QB64 | | [DICT](DICT/README.md) | Dictionary object (sorta) | Custom type and support for arrays using `.key` and `.val` | | [DUMP](DUMP/README.md) | Debugging library | Like PHPs `print_r` (kind of) | | [INPUT](INPUT/README.md) | Input library | For lightbar menus, text boxes, etc. | @@ -22,6 +23,8 @@ in it's current state. | [SYS](SYS/README.md) | System stuff | Contains misc. helpful utils/tools | | [VECT2D](VECT2D/README.md) | 2D Vector support | Thanks to William Barnes and Evan Shortiss | | [ASEPRITE](ASEPRITE/README.md) | Adds ASEPRITE support to QB64 | See https://www.aseprite.org | +| [CONSOLE](CONSOLE/README.md) | CONSOLE object with debugging. | +| [MISC](MISC/README.md) | Miscellaneous helpful functions and subs that don't fit anywhere else :) | @@ -114,3 +117,4 @@ things. I'll try to keep backwards compatibility though. - More ANSI stuff (file loading / displaying / ANSI music?) - Color stuff (palette cycling / `dump_rgb`) - PETSCII library + diff --git a/STRINGS/README.md b/STRINGS/README.md index ba0b583..0b59298 100644 --- a/STRINGS/README.md +++ b/STRINGS/README.md @@ -6,13 +6,46 @@ ## WHAT'S IN THE LIBRARY | SUB / FUNCTION | NOTES | |----------------|-------| -| str_implode$ | Implodes a string array into a string using delimiter as glue | -| str_explode | Explodes a string into an array of strings using a delimiter | -| str_find_pos | Searches for strings in strings, fills array of found positions | -| str_insert$ | Insert a string into another string at position | -| str_remove$ | Remove a string from a string | -| str_replace$ | Replaces a string with another string inside a string | -| str_slice_pos$ | Returns part of a string from start pos. to end pos. | +| STR.bool$ | Returns a string if n is true or false | +| STR.is_alpha_numeric% | Check if string consists purely of alphabet chars or numbers | +| STR.is_alpha% | Check if string consists purely of alphabetic characters | +| STR.is_numeric% | Check if string consists purely of numbers | +| STR.is_upper_case% | Check if string consists purely of upper case characters | +| STR.is_lower_case% | Check if string consists purely of lower case characters | +| STR.is_white_space% | Check if string consists of only white space | +| STR.is_printable% | Check if string consists of only printable characters | +| STR.is_graphical% | Check if string consists of only graphic characters | +| STR.is_punctuation% | Check if string consists of only punctuation characters | +| STR.is_control_chars% | Check if string consists of only control characters | +| STR.is_blank% | Check if string consists of only space or tab characters | +| STR.is_empty% | Check if string is null | +| STR.is_falsey% | Check if string is falsy: null or 0 | +| STR.is_truthy% | Check if string is truthy: not null or -1 | +| STR.is_sentence% | Check if string ends with ., !, or ? | +| STR.implode$ | Implodes a string array into a string using delimiter as glue | +| STR.explode | Explodes a string into an array of strings using a delimiter | +| STR.find_pos | Searches for strings in strings, fills array of found positions | +| STR.insert$ | Insert a string into another string at position | +| STR.remove$ | Remove a string from a string | +| STR.replace$ | Replaces a string with another string inside a string | +| STR.slice_pos$ | Returns part of a string from start pos. to end pos. | +| STR.pad_start$ | Returns a string padded at the start using char. | +| STR.pad_end$ | Returns a string padded at the end using char. | +| STR.pad_both$ | Returns a string padded on both sides using char. | +| STR.repeat$ | Returns a string repeated num times. | +| STR.starts_with% | Determines if a string starts with another string. | +| STR.ends_with% | Determines if a string ends with another string. | +| STR.reverse$ | Reverses a string. | +| STR.shuffle$ | Shuffles (randomizes) the characters in a string. | +| STR.ub$ | Returns a space trimmed _UNSIGNED _BYTE as a string. | +| STR.ui$ | Returns a space trimmed _UNSIGNED INTEGER as a string. | +| STR.ul$ | Returns a space trimmed _UNSIGNED LONG as a string. | +| STR.b$ | Returns a space trimmed _BYTE as a string. | +| STR.i$ | Returns a space trimmed INTEGER as a string. | +| STR.l$ | Returns a space trimmed LONG as a string. | +| STR.d$ | Returns a space trimmed DOUBLE as a string. | +| STR.s$ | Returns a space trimmed SINGLE as a string. | +| STR.v$ | Returns a space trimmed _FLOAT as a string. | @@ -32,4 +65,24 @@ ### EXAMPLE > Screenshot of output from [STRINGS.BAS](STRINGS.BAS) -![](STRINGS.png) \ No newline at end of file +![](STRINGS.png) + + +### TO DO +- [ ] STR.strip_quotes +- [ ] STR.strip_newlines (\r, \n) +- [ ] STR.strip_white_space (space, \r, \n, \t) +- [ ] STR.strip_slashes (remove from single quotes, double quotes) +- [ ] STR.add_slashes (single quotes, double quotes) +- [ ] STR.number_format +- [ ] STR.format +- [ ] STR.title_case `Title Case` +- [ ] STR.camel_case `CamelCase` +- [ ] STR.snake_case `snakeCase` +- [ ] STR.lower_first +- [ ] STR.upper_first +- [ ] STR.word_count +- [ ] STR.word_shuffle +- [ ] STR.wrap +- [ ] STR.wrap_pair +- [ ] STR.word_wrap diff --git a/STRINGS/STRINGS.BAS b/STRINGS/STRINGS.BAS index 13c3581..98f9fc8 100644 --- a/STRINGS/STRINGS.BAS +++ b/STRINGS/STRINGS.BAS @@ -1,7 +1,7 @@ -'' -' QB64_GJ_LIB +'' +' QB64_GJ_LIB ' GRYMMJACK'S STRINGS LIB -' +' ' Tests and example for STRINGS lib. ' ' @author Rick Christy @@ -16,7 +16,8 @@ $IF GJ_LIB_DUMP_INC_BI = UNDEFINED THEN '$INCLUDE:'../DUMP/DUMP.BI' $END IF '$DYNAMIC - +OPTION _EXPLICIT +OPTION _EXPLICITARRAY _TITLE "QB64_GJ_LIB STRINGS LIB TESTS" @@ -24,6 +25,81 @@ _TITLE "QB64_GJ_LIB STRINGS LIB TESTS" SCREEN _NEWIMAGE(1024, 768, 32) _SCREENMOVE _MIDDLE +DIM AS STRING phrase, phrase2, target, search, replace, temp_str +DIM AS INTEGER num_times, found, wordCount + +' DIM vars() AS STRING +' CALL ARR_STR.new(vars$(), "Rick, 3") +' PRINT STR.format("Hello %s, there are %d hours left until daybreak", vars$()) + +' DIM d() AS DICTIONARY +' CALL ARR_STR.new(vars$(), "Rick,3") +' PRINT STR.format("Hello %s, there are %d hours left until daybreak", vars$()) + +temp_str$ = "Intel486" +PRINT "Is "; CHR$(34); temp_str$; CHR$(34); " alpha numeric? "; +PRINT STR.bool$(STR.is_alpha_numeric%(temp_str$), "Yes", "No") + +temp_str$ = "Apple" +PRINT "Is "; CHR$(34); temp_str$; CHR$(34); " alpha? "; +PRINT STR.bool$(STR.is_alpha%(temp_str$), "Yes", "No") + +temp_str$ = "8088" +PRINT "Is "; CHR$(34); temp_str$; CHR$(34); " numeric? "; +PRINT STR.bool$(STR.is_numeric%(temp_str$), "Yes", "No") + +temp_str$ = "IBM" +PRINT "Is "; CHR$(34); temp_str$; CHR$(34); " upper case? "; +PRINT STR.bool$(STR.is_upper_case%(temp_str$), "Yes", "No") + +temp_str$ = "basicprogramming" +PRINT "Is "; CHR$(34); temp_str$; CHR$(34); " lower case? "; +PRINT STR.bool$(STR.is_lower_case%(temp_str$), "Yes", "No") + +temp_str$ = " " +PRINT "Is "; CHR$(34); temp_str$; CHR$(34); " white space? "; +PRINT STR.bool$(STR.is_white_space%(temp_str$), "Yes", "No") + +temp_str$ = "a,b!" +PRINT "Is "; CHR$(34); temp_str$; CHR$(34); " printable? "; +PRINT STR.bool$(STR.is_printable%(temp_str$), "Yes", "No") + +temp_str$ = "Hello, World!" +PRINT "Is "; CHR$(34); temp_str$; CHR$(34); " graphical? "; +PRINT STR.bool$(STR.is_graphical%(temp_str$), "Yes", "No") + +temp_str$ = "!" +PRINT "Is "; CHR$(34); temp_str$; CHR$(34); " punctuation? "; +PRINT STR.bool$(STR.is_punctuation%(temp_str$), "Yes", "No") + +temp_str$ = CHR$(7) +PRINT "Is "; CHR$(34); temp_str$; CHR$(34); " control chars? "; +PRINT STR.bool$(STR.is_control_chars%(temp_str$), "Yes", "No") + +temp_str$ = "DEADBEEFD34DB33F" +PRINT "Is "; CHR$(34); temp_str$; CHR$(34); " hexadecimal? "; +PRINT STR.bool$(STR.is_hexadecimal%(temp_str$), "Yes", "No") + +temp_str$ = " " +PRINT "Is "; CHR$(34); temp_str$; CHR$(34); " blank? "; +PRINT STR.bool$(STR.is_blank%(temp_str$), "Yes", "No") + +temp_str$ = "" +PRINT "Is "; CHR$(34); temp_str$; CHR$(34); " empty? "; +PRINT STR.bool$(STR.is_empty%(temp_str$), "Yes", "No") + +temp_str$ = "" +PRINT "Is "; CHR$(34); temp_str$; CHR$(34); " falsey? "; +PRINT STR.bool$(STR.is_falsey%(temp_str$), "Yes", "No") + +temp_str$ = "" +PRINT "Is "; CHR$(34); temp_str$; CHR$(34); " truthy? "; +PRINT STR.bool$(STR.is_truthy%(temp_str$), "Yes", "No") + +temp_str$ = "Did you eat yet?" +PRINT "Is "; CHR$(34); temp_str$; CHR$(34); " a sentence? "; +PRINT STR.bool$(STR.is_sentence%(temp_str$), "Yes", "No") + phrase$ = "" phrase$ = "foo" phrase$ = "foo bar" @@ -31,30 +107,30 @@ phrase$ = "foo bar baz" phrase$ = "foo bar baz bop bobo bilbo frodo" DIM SHARED words$(LEN(phrase$)) -str_explode phrase$, " ", words$(), wordCount% +STR.explode phrase$, " ", words$(), wordCount% IF wordCount% > 0 THEN REDIM _PRESERVE words$(wordCount%) ELSE REDIM _PRESERVE words$(0) END IF -PRINT dump_string_array$(words$(), "words") +PRINT DUMP.string_array$(words$(), "words") -phrase2$ = str_implode$(words$(), "::") -PRINT dump_string$(phrase2$, "phrase2") +phrase2$ = STR.implode$(words$(), "::") +PRINT DUMP.string$(phrase2$, "phrase2") target$ = "apple orange apple grapes apple apple pear orange" search$ = "apple" replace$ = "bananafosters" num_times% = -1 -PRINT str_replace$(target$, search$, replace$, num_times%) +PRINT STR.replace$(target$, search$, replace$, num_times%) target$ = "apple orange apple grapes apple apple pear orange" -PRINT str_remove$(target$, search$ + " ", num_times%) +PRINT STR.remove$(target$, search$ + " ", num_times%) target$ = "apple orange apple grapes apple apple pear orange" -PRINT str_insert$(target$, "pumpkin ", 0) +PRINT STR.insert$(target$, "pumpkin ", 0) target$ = "apple orange apple grapes apple apple pear orange" -PRINT str_insert$(target$, " pumpkin ", 1) +PRINT STR.insert$(target$, " pumpkin ", 1) target$ = "apple orange apple grapes apple apple pear orange" -PRINT str_insert$(target$, "pumpkin ", 12) +PRINT STR.insert$(target$, "pumpkin ", 12) ' Should return grapes: @@ -63,10 +139,66 @@ PRINT phrase$ '$DYNAMIC DIM SHARED parts%(20) found%=0 -str_find_pos phrase$, " ", parts%(), found% +STR.find_pos phrase$, " ", parts%(), found% REDIM _PRESERVE parts%(found%) -PRINT dump_integer_array(parts%(), "parts") -PRINT str_slice_pos(phrase$, 0, parts%(0)) +PRINT DUMP.integer_array(parts%(), "parts") +PRINT STR.slice_pos(phrase$, 0, parts%(0)) + +PRINT STR.pad_end$("My dog needs to go out", ".", 3) +PRINT STR.pad_start$("but my shoes are upstairs and i'm lazy", ".", 10) +PRINT STR.pad_both$("FML", "!", 5) +PRINT STR.repeat$("WOOF! ", 4) +PRINT STR.repeat$("whimper ", 2); STR.repeat$("*whine* ", 3) + +PRINT "cat butt ends with 'butt' -> "; STR.ends_with%("cat butt", "butt") +PRINT "cat butt starts with 'cat' -> "; STR.starts_with%("cat butt", "cat") +PRINT "cat butt ends with 'bum' -> "; STR.ends_with%("cat butt", "bum") +PRINT "cat butt starts with 'dog' -> "; STR.starts_with%("cat butt", "dog") + +DIM ub AS _UNSIGNED _BYTE : ub~%% = 255 +DIM ui AS _UNSIGNED INTEGER : ui~% = 65535 +DIM ul AS _UNSIGNED LONG : ul~& = 3133731337 +DIM b AS _BYTE : b%% = 127 +DIM i AS INTEGER : i% = 10 +DIM l AS LONG : l& = 31337 +DIM s AS SINGLE : s! = 101010.10 +DIM d AS DOUBLE : d# = 101010.101010 +DIM f AS _FLOAT : f## = 3.14259102835901 + +PRINT +PRINT ": [" + STR.ub$(ub~%%) + "]" +PRINT ": [" + STR.ui$(ui~%) + "]" +PRINT ": [" + STR.ul$(ul~&) + "]" +PRINT ": [" + STR.b$(b%%) + "]" +PRINT ": [" + STR.i$(i%) + "]" +PRINT ": [" + STR.l$(l&) + "]" +PRINT ": [" + STR.s$(s!) + "]" +PRINT ": [" + STR.d$(d#) + "]" +PRINT ": [" + STR.f$(f##) + "]" +PRINT + +PRINT "[Four score and seven years ago]" +PRINT "[" + STR.reverse$("Four score and seven years ago") + "]" +PRINT "[" + STR.shuffle$("Four score and seven years ago") + "]" +PRINT "[" + STR.shuffle$("Four score and seven years ago") + "]" +PRINT "[" + STR.shuffle$("Four score and seven years ago") + "]" +PRINT "[" + STR.shuffle$("Four score and seven years ago") + "]" +PRINT "[" + STR.shuffle$("Four score and seven years ago") + "]" +PRINT "[" + STR.shuffle$("Four score and seven years ago") + "]" +PRINT "[" + STR.shuffle$("Four score and seven years ago") + "]" +PRINT "[" + STR.shuffle$("Four score and seven years ago") + "]" +PRINT "[" + STR.shuffle$("Four score and seven years ago") + "]" +PRINT "[" + STR.shuffle$("Four score and seven years ago") + "]" + +DIM AS STRING sentence, delimiter +DIM words_extracted(50) AS STRING +DIM word_count AS INTEGER +sentence$ = "Four score and seven years ago" +delimiter$ = " " +word_count% = 0 +CALL STR.explode(sentence$, delimiter$, words_extracted$(), word_count%) +PRINT STR.implode$(words_extracted$(), " ") + $IF GJ_LIB_UNIFIED_TESTING = DEFINED AND GJ_LIB_INC_BM <> 1 THEN diff --git a/STRINGS/STRINGS.BI b/STRINGS/STRINGS.BI index c8f34e2..fb77eed 100644 --- a/STRINGS/STRINGS.BI +++ b/STRINGS/STRINGS.BI @@ -7,21 +7,38 @@ ' USAGE: ' Insert '$INCLUDE:'path_to_GJ_LIB/STRINGS/STRINGS.BI' at the top of file ' Insert '$INCLUDE:'path_to_GJ_LIB/STRINGS/STRINGS.BM' at the bottom of file -' +' ' @author Rick Christy ' @uses STRINGS.BM ' $LET GJ_LIB_STRINGS_INC_BI = 1 CONST GJ_LIB_MAX_STR_POSITIONS = 50 - - -DECLARE FUNCTION str_implode$ (arr$(), delim$) -DECLARE SUB str_explode (target$, delim$, dest$(), numParts%) -DECLARE SUB str_find_pos (target$, search$, arrFound%(), numFound%) -DECLARE FUNCTION str_insert$ (s$, ins$, p%) -DECLARE FUNCTION str_remove$ (s$, del$) -DECLARE FUNCTION str_del (s$, del$) -DECLARE FUNCTION str_replace$ (s$, search$, replace$) -DECLARE FUNCTION str_rep$ (s$, search$, replace$) -DECLARE FUNCTION str_slice_pos (s$, startPos%, endPos%) +DECLARE LIBRARY + 'is an alphabet letter(isalpha(c) or isdigit(c)) + FUNCTION GJ_LIB_isalnum% ALIAS "isalnum" (BYVAL c AS INTEGER) + 'is letter (isupper(c) or islower(c)) + FUNCTION GJ_LIB_isalpha% ALIAS "isalpha" (BYVAL c AS INTEGER) + 'is a decimal digit + FUNCTION GJ_LIB_isdigit% ALIAS "isdigit" (BYVAL c AS INTEGER) + 'is a printing character other than space + FUNCTION GJ_LIB_isgraph% ALIAS "isgraph" (BYVAL c AS INTEGER) + 'is a lower-case letter + FUNCTION GJ_LIB_islower% ALIAS "islower" (BYVAL c AS INTEGER) + 'is printing character. ASCII: &H20 (" ") to &H7E (~) + FUNCTION GJ_LIB_isprint% ALIAS "isprint" (BYVAL c AS INTEGER) + 'is printing character other than space, letter, digit + FUNCTION GJ_LIB_ispunct% ALIAS "ispunct" (BYVAL c AS INTEGER) + 'is space, formfeed, newline, return, tab, vertical tab + FUNCTION GJ_LIB_isspace% ALIAS "isspace" (BYVAL c AS INTEGER) + 'is only space or tab + FUNCTION GJ_LIB_isblank% ALIAS "isblank" (BYVAL c AS INTEGER) + 'is upper-case letter + FUNCTION GJ_LIB_isupper% ALIAS "isupper" (BYVAL c AS INTEGER) + 'is a hexdecimal digit character(0 thru 9 or A thru F) + FUNCTION GJ_LIB_isxdigit% ALIAS "isxdigit" (BYVAL c AS INTEGER) + 'return lower-case equivalent + FUNCTION GJ_LIB.tolower% ALIAS "tolower" (BYVAL c AS INTEGER) + 'return upper-case equivalent + FUNCTION GJ_LIB.toupper% ALIAS "toupper" (BYVAL c AS INTEGER) +END DECLARE \ No newline at end of file diff --git a/STRINGS/STRINGS.BM b/STRINGS/STRINGS.BM index 3440901..e90ba3e 100644 --- a/STRINGS/STRINGS.BM +++ b/STRINGS/STRINGS.BM @@ -2,15 +2,6 @@ ' GRYMMJACK'S STRINGS LIB ' ' Some commonly used functions that I missed in QB64 coming from PHP -' -' SUB/FUNCTION NOTE -' str_implode$ Implodes a string array into a string using delimiter as glue -' str_explode Explodes a string into an array of strings using a delimiter -' str_find_pos Searches for strings in strings, fills array of found positions -' str_insert$ Insert a string into another string at position -' str_remove$ Remove a string from a string -' str_replace$ Replaces a string with another string inside a string -' str_slice_pos$ Returns part of a string from start pos. to end pos. ' ' @author Rick Christy ' @uses STRINGS.BI @@ -19,26 +10,368 @@ $LET GJ_LIB_STRINGS_INC_BM = 1 $LET DEBUGGING = 1 + +'' +' Returns a string if n is true or false +' +' @param INTEGER n% to check +' @param STRING if_false$ string +' @param STRING if_true$ string +' @return STRING representing true or false +' +FUNCTION STR.bool$(n%, if_true$, if_false$) + IF n% = 0 THEN + STR.bool$ = if_false$ + ELSEIF n% = -1 THEN + STR.bool$ = if_true$ + END IF +END FUNCTION + + +'' +' Check if string is a sentence: ends in .!? +' +' @param STRING s$ to check +' @return INTEGER -1 if true, 0 if false +' +FUNCTION STR.is_sentence%(s$) + DIM last_char AS STRING + last_char$ = RIGHT$(s$, 1) + IF last_char$ = "." OR last_char$ = "!" OR last_char$ = "?" THEN + STR.is_sentence% = -1 + ELSE + STR.is_sentence% = 0 + END IF +END FUNCTION + + +'' +' Check if string is truthy: not null or -1 +' +' @param STRING s$ to check +' @return INTEGER -1 if true, 0 if false +' +FUNCTION STR.is_truthy%(s$) + IF s$ <> "" OR s$ = "-1" THEN + STR.is_truthy% = -1 + EXIT FUNCTION + END IF + STR.is_truthy% = 0 +END FUNCTION + + +'' +' Check if string is falsy: null or 0 +' +' @param STRING s$ to check +' @return INTEGER -1 if true, 0 if false +' +FUNCTION STR.is_falsey%(s$) + IF s$ = "" OR s$ = "0" THEN + STR.is_falsey% = -1 + EXIT FUNCTION + END IF + STR.is_falsey% = 0 +END FUNCTION + + +'' +' Check if string is null +' +' @param STRING s$ to check +' @return INTEGER -1 if true, 0 if false +' +FUNCTION STR.is_empty%(s$) + IF s$ = "" THEN + STR.is_empty% = -1 + EXIT FUNCTION + END IF + STR.is_empty% = 0 +END FUNCTION + + +'' +' Check if string consists purely of space and tab characters +' +' @param STRING s$ to check +' @return INTEGER -1 if true, 0 if false +' +FUNCTION STR.is_blank%(s$) + DIM AS INTEGER i + IF s$ = "" THEN EXIT FUNCTION + i% = 1 + DO: + IF GJ_LIB_isblank(ASC(s$, i%)) = 0 THEN + STR.is_blank% = 0 + EXIT FUNCTION + END IF + i% = i% + 1 + LOOP UNTIL i% = LEN(s$) + 1 + STR.is_blank% = -1 +END FUNCTION + + +'' +' Check if string consists purely of hexadecimal characters: +' ASCII 0-9 A-F +' +' @param STRING s$ to check +' @return INTEGER -1 if true, 0 if false +' +FUNCTION STR.is_hexadecimal%(s$) + DIM AS INTEGER i + IF s$ = "" THEN EXIT FUNCTION + i% = 1 + DO: + IF GJ_LIB_isxdigit(ASC(s$, i%)) = 0 THEN + STR.is_hexadecimal% = 0 + EXIT FUNCTION + END IF + i% = i% + 1 + LOOP UNTIL i% = LEN(s$) + 1 + STR.is_hexadecimal% = -1 +END FUNCTION + + +'' +' Check if string consists purely of control characters: +' ASCII 0-31 +' +' @param STRING s$ to check +' @return INTEGER -1 if true, 0 if false +' +FUNCTION STR.is_control_chars%(s$) + DIM AS INTEGER i + IF s$ = "" THEN EXIT FUNCTION + i% = 1 + DO: + IF ASC(s$, i%) = 0 OR ASC(s$, i%) > 31 THEN + STR.is_control_chars% = 0 + EXIT FUNCTION + END IF + i% = i% + 1 + LOOP UNTIL i% = LEN(s$) + 1 + STR.is_control_chars% = -1 +END FUNCTION + + +'' +' Check if string consists purely of punctuation characters: +' !"#$%&'()*+,-./:;<=>?@[\]^_`{|}~ +' +' @param STRING s$ to check +' @return INTEGER -1 if true, 0 if false +' +FUNCTION STR.is_punctuation%(s$) + DIM AS INTEGER i + IF s$ = "" THEN EXIT FUNCTION + i% = 1 + DO: + IF GJ_LIB_ispunct%(ASC(s$, i%)) = 0 THEN + STR.is_punctuation% = 0 + EXIT FUNCTION + END IF + i% = i% + 1 + LOOP UNTIL i% = LEN(s$) + 1 + STR.is_punctuation% = -1 +END FUNCTION + + +'' +' Check if string consists purely of graphic characters: +' it is either a number (0123456789), +' an uppercase letter (ABCDEFGHIJKLMNOPQRSTUVWXYZ), +' a lowercase letter (abcdefghijklmnopqrstuvwxyz), +' or a punctuation character(!"#$%&'()*+,-./:;<=>?@[\]^_`{|}~), +' or any graphical character specific to the current C locale. +' +' @param STRING s$ to check +' @return INTEGER -1 if true, 0 if false +' +FUNCTION STR.is_graphical%(s$) + DIM AS INTEGER i + IF s$ = "" THEN EXIT FUNCTION + i% = 1 + DO: + IF GJ_LIB_isgraph%(ASC(s$, i%)) = 0 THEN + STR.is_graphical% = 0 + EXIT FUNCTION + END IF + i% = i% + 1 + LOOP UNTIL i% = LEN(s$) + 1 + STR.is_graphical% = -1 +END FUNCTION + + +'' +' Check if string consists purely of printable characters: +' ASCII: &H20 (" ") to &H7E (~) +' +' @param STRING s$ to check +' @return INTEGER -1 if true, 0 if false +' +FUNCTION STR.is_printable%(s$) + DIM AS INTEGER i + IF s$ = "" THEN EXIT FUNCTION + i% = 1 + DO: + IF GJ_LIB_isprint%(ASC(s$, i%)) = 0 THEN + STR.is_printable% = 0 + EXIT FUNCTION + END IF + i% = i% + 1 + LOOP UNTIL i% = LEN(s$) + 1 + STR.is_printable% = -1 +END FUNCTION + + +'' +' Check if string consists purely of space characters: +' space, formfeed, newline, return, tab, vertical tab +' +' @param STRING s$ to check +' @return INTEGER -1 if true, 0 if false +' +FUNCTION STR.is_white_space%(s$) + DIM AS INTEGER i + IF s$ = "" THEN EXIT FUNCTION + i% = 1 + DO: + IF GJ_LIB_isspace%(ASC(s$, i%)) = 0 THEN + STR.is_white_space% = 0 + EXIT FUNCTION + END IF + i% = i% + 1 + LOOP UNTIL i% = LEN(s$) + 1 + STR.is_white_space% = -1 +END FUNCTION + + +'' +' Check if string consists purely of lower case characters +' +' @param STRING s$ to check +' @return INTEGER -1 if true, 0 if false +' +FUNCTION STR.is_lower_case%(s$) + DIM AS INTEGER i + IF s$ = "" THEN EXIT FUNCTION + i% = 1 + DO: + IF GJ_LIB_islower%(ASC(s$, i%)) = 0 THEN + STR.is_lower_case% = 0 + EXIT FUNCTION + END IF + i% = i% + 1 + LOOP UNTIL i% = LEN(s$) + 1 + STR.is_lower_case% = -1 +END FUNCTION + + +'' +' Check if string consists purely of upper case characters +' +' @param STRING s$ to check +' @return INTEGER -1 if true, 0 if false +' +FUNCTION STR.is_upper_case%(s$) + DIM AS INTEGER i + IF s$ = "" THEN EXIT FUNCTION + i% = 1 + DO: + IF GJ_LIB_isupper%(ASC(s$, i%)) = 0 THEN + STR.is_upper_case% = 0 + EXIT FUNCTION + END IF + i% = i% + 1 + LOOP UNTIL i% = LEN(s$) + 1 + STR.is_upper_case% = -1 +END FUNCTION + + +'' +' Check if string consists purely of numbers +' +' @param STRING s$ to check +' @return INTEGER -1 if true, 0 if false +' +FUNCTION STR.is_numeric%(s$) + DIM AS INTEGER i + IF s$ = "" THEN EXIT FUNCTION + i% = 1 + DO: + IF GJ_LIB_isdigit%(ASC(s$, i%)) = 0 THEN + STR.is_numeric% = 0 + EXIT FUNCTION + END IF + i% = i% + 1 + LOOP UNTIL i% = LEN(s$) + 1 + STR.is_numeric% = -1 +END FUNCTION + + +'' +' Check if string consists purely of alphabetical characters +' +' @param STRING s$ to check +' @return INTEGER -1 if true, 0 if false +' +FUNCTION STR.is_alpha%(s$) + DIM AS INTEGER i + IF s$ = "" THEN EXIT FUNCTION + i% = 1 + DO: + IF GJ_LIB_isalpha%(ASC(s$, i%)) = 0 THEN + STR.is_alpha% = 0 + EXIT FUNCTION + END IF + i% = i% + 1 + LOOP UNTIL i% = LEN(s$) + 1 + STR.is_alpha% = -1 +END FUNCTION + + +'' +' Check if string consists purely of alphabet characters or numbers +' +' @param STRING s$ to check +' @return INTEGER -1 if true, 0 if false +' +FUNCTION STR.is_alpha_numeric%(s$) + DIM AS INTEGER i + IF s$ = "" THEN EXIT FUNCTION + i% = 1 + DO: + IF GJ_LIB_isalnum%(ASC(s$, i%)) = 0 THEN + STR.is_alpha_numeric% = 0 + EXIT FUNCTION + END IF + i% = i% + 1 + LOOP UNTIL i% = LEN(s$) + 1 + STR.is_alpha_numeric% = -1 +END FUNCTION + + '' ' Implodes a string array into a string using delimiter as glue ' -' @param arr$() Array to implode from -' @param delim$ Delimiter to glue the array parts together with -' @return STRING of array parts from the implosion +' @param STRING ARRAY arr$() to implode from +' @param STRING delim$ Delimiter to glue the array parts together with +' @return STRING of array parts glued together with delimiter ' -FUNCTION str_implode$ (arr$(), delim$) - DIM AS STRING r +FUNCTION STR.implode$(arr$(), delim$) + DIM AS STRING res DIM AS INTEGER lb, ub, i - r$ = "" - lb = LBOUND(arr$) : ub = UBOUND(arr$) - FOR i = lb to ub - IF i + 1 <= ub THEN - r$ = r$ + arr$(i) + delim$ + res$ = "" + lb% = LBOUND(arr$) : ub% = UBOUND(arr$) + FOR i% = lb% TO ub% + IF i% + 1 <= ub% THEN + res$ = res$ + arr$(i) + delim$ ELSE - r$ = r$ + arr$(i) + res$ = res$ + arr$(i) END IF - NEXT i - str_implode$ = r$ + NEXT i% + STR.implode$ = res$ END FUNCTION @@ -47,45 +380,41 @@ END FUNCTION ' ' If the delimiter is not found, returns the target as dest$(0) ' -' @param target$ String to explode -' @param delim$ Delimiter -' @param dest$() Array to explode into -' @param numParts% The number of strings in the array +' @param STRING target$ to explode +' @param STRING delim$ delimiter +' @param STRING ARRAY dest$() to explode into +' @param INTEGER numParts% the number of strings in the array ' -SUB str_explode (target$, delim$, dest$(), numParts%) +SUB STR.explode(target$, delim$, dest$(), numParts%) DIM AS INTEGER length, delimLen, numFound, i - length = LEN(target$) - delimLen = LEN(delim$) - DIM delimsPos%(length) - IF length = 0 THEN EXIT SUB - numFound% = 0 - str_find_pos target$, delim$, delimsPos%(), numFound% - IF numFound% > 0 THEN - REDIM _PRESERVE delimsPos%(numFound% - 1) - ELSE - dest$(0) = target$ + length% = LEN(target$) : delimLen% = LEN(delim$) : numFound% = 0 + DIM delimsPos(length%) AS INTEGER + IF length% = 0 THEN EXIT SUB + + CALL STR.find_pos(target$, delim$, delimsPos%(), numFound%) + + IF numFound% <= 0 THEN numParts% = 0 - EXIT SUB - END IF - IF numFound% > 0 THEN + dest$(0) = target$ + ELSE + REDIM _PRESERVE delimsPos%(numFound% - 1) IF numFound% = 1 THEN - dest$(0) = LEFT$(target$, delimsPos%(0) - delimLen) - dest$(1) = MID$(target$, delimsPos%(0) + delimLen) numParts% = 1 - END IF - IF numFound% > 1 THEN - dest$(0) = LEFT$(target$, delimsPos%(0) - delimLen) - FOR i = 1 TO numFound% - IF i + 1 <= numFound% THEN - dest$(i) = MID$( _ + dest$(0) = LEFT$(target$, delimsPos%(0) - delimLen%) + dest$(1) = MID$(target$, delimsPos%(0) + delimLen%) + ELSEIF numFound% > 1 THEN + dest$(0) = LEFT$(target$, delimsPos%(0) - delimLen%) + FOR i% = 1 TO numFound% + IF i% + 1 <= numFound% THEN + dest$(i%) = MID$( _ target$, _ - delimsPos%(i - 1) + delimLen, _ - delimsPos%(i) - delimsPos%(i - 1) - delimLen _ + delimsPos%(i% - 1) + delimLen%, _ + delimsPos%(i%) - delimsPos%(i% - 1) - delimLen% _ ) END IF - NEXT i + NEXT i% dest$(numFound%) = MID$( _ - target$, delimsPos%(numFound% - 1) + delimLen _ + target$, delimsPos%(numFound% - 1) + delimLen% _ ) numParts% = numFound% END IF @@ -96,47 +425,47 @@ END SUB '' ' Searches for strings inside of strings and fills array with found positions ' -' @param target$ String to search -' @param search$ String to search for in target -' @param arrFound% The array to populate with positions search found in target -' @param numFound% The number of times search found a match -' -SUB str_find_pos (target$, search$, arrFound%(), numFound%) - DIM AS INTEGER length, found, x, i, u, searchLen - length = LEN(target$): found = -1: x = 0: i = 0 - u = UBOUND(arrFound%) - searchLen = LEN(search$) - DO WHILE i <= length - found = INSTR(i, target$, search$) - IF found > 0 AND x <= u THEN - arrFound%(x) = found - i = found + searchLen - x = x + 1 +' @param STRING target$ to search +' @param STRING search$ for in target +' @param INTEGER ARRAY arrFound%() populate with positions search found +' @param INTEGER numFound% times search found a match +' +SUB STR.find_pos(target$, search$, arrFound%(), numFound%) + DIM AS INTEGER length, found, x, i, ub, searchLen + length% = LEN(target$) : found% = -1 : x% = 0: i% = 0 + ub% = UBOUND(arrFound%) + searchLen% = LEN(search$) + DO WHILE i% <= length% + found% = INSTR(i%, target$, search$) + IF found% > 0 AND x% <= ub% THEN + arrFound%(x%) = found% + i% = found% + searchLen% + x% = x% + 1 ELSE - i = i + 1 + i% = i% + 1 END IF LOOP - numFound% = x + numFound% = x% END SUB '' ' Insert a string into another string at position ' -' @param s$ string to insert into -' @param ins$ string to insert -' @param p% position to insert +' @param STRING s$ to insert into +' @param STRING ins$ insert +' @param INTEGER p% position to insert ' @return STRING with insertion ' -FUNCTION str_insert$ (s$, ins$, p%) +FUNCTION STR.insert$(s$, ins$, p%) IF p% < LEN(s$) AND ins$ <> "" THEN IF p% = 0 THEN - str_insert$ = ins$ + RIGHT$(s$, LEN(s$) + LEN(ins$)-1) + STR.insert$ = ins$ + RIGHT$(s$, LEN(s$) + LEN(ins$) - 1) ELSE - str_insert$ = LEFT$(s$, p%+1) + ins$ + RIGHT$(s$, LEN(s$) - p%-1) + STR.insert$ = LEFT$(s$, p%+1) + ins$ + RIGHT$(s$, LEN(s$) - p%-1) END IF ELSE - str_insert$ = s$ + STR.insert$ = s$ END IF END FUNCTION @@ -144,43 +473,43 @@ END FUNCTION '' ' Remove a string from a string ' -' @param s$ string to remove from -' @param del$ string to delete -' @param count% number of times to remove +' @param STRING s$ to remove from +' @param STRING del$ to delete +' @param INTEGER count% times to remove ' @return STRING with del$ removed ' -FUNCTION str_remove$ (s$, del$, count%) +FUNCTION STR.remove$(s$, del$, count%) DIM AS INTEGER p IF count% = -1 THEN DO p% = INSTR(s$, del$) - s$ = str_del$(s$, del$) + s$ = STR.del$(s$, del$) LOOP UNTIL p% = 0 ELSE DO p% = INSTR(s$, del$) - s$ = str_del$(s$, del$) + s$ = STR.del$(s$, del$) count% = count% - 1 LOOP UNTIL p% = 0 OR count% = 0 END IF - str_remove$ = s$ + STR.remove$ = s$ END FUNCTION '' -' Delete a string from a string once (helper for str_remove$) +' Delete a string from a string once (helper for STR.remove$) ' -' @param s$ string to delete from -' @param del$ string to delete +' @param STRING s$ to delete from +' @param STRING del$ to delete ' @return STRING with del$ deleted ' -FUNCTION str_del$ (s$, del$) +FUNCTION STR.del$(s$, del$) DIM AS INTEGER i i% = INSTR(s$, del$) IF i% THEN - str_del$ = LEFT$(s$, i%-1) + RIGHT$(s$, LEN(s$) - (i% + LEN(del$))+1) + STR.del$ = LEFT$(s$, i%-1) + RIGHT$(s$, LEN(s$) - (i% + LEN(del$))+1) ELSE - str_del$ = s$ + STR.del$ = s$ END IF END FUNCTION @@ -188,46 +517,163 @@ END FUNCTION '' ' Replaces a string with another string inside a string ' -' @param s$ string to replace within -' @param search$ string to search for -' @param replace$ string to replace search with -' @param count% number of times to replace +' @param STRING s$ to replace within +' @param STRING search$ +' @param STRING rep$ string to replace search with if found +' @param INTEGER count% number of times to replace ' @return STRING with replacements ' -FUNCTION str_replace$ (s$, search$, replace$, count%) +FUNCTION STR.replace$(s$, search$, rep$, count%) DIM AS INTEGER p IF count% = -1 THEN DO p% = INSTR(s$, search$) - s$ = str_rep$(s$, search$, replace$) + s$ = STR.rep$(s$, search$, rep$) LOOP UNTIL p% = 0 ELSE DO p% = INSTR(s$, search$) - s$ = str_rep$(s$, search$, replace$) + s$ = STR.rep$(s$, search$, rep$) count% = count% - 1 LOOP UNTIL p% = 0 OR count% = 0 END IF - str_replace$ = s$ + STR.replace$ = s$ +END FUNCTION + + +'' +' Reverses a string +' +' @param STRING s$ to reverse +' @return STRING reversed string +' +FUNCTION STR.reverse$(s$) + DIM AS INTEGER i, l + DIM AS STRING res + res$ = "" + l% = LEN(s$) + IF l% = 0 THEN EXIT FUNCTION + FOR i% = l% TO 1 STEP -1 + res$ = res$ + CHR$(ASC(s$, i%)) + NEXT i% + STR.reverse$ = res$ +END FUNCTION + + +'' +' Shuffles (randomizes) the characters in a string +' +' @param STRING s$ string to reverse +' @return STRING shuffled string +' +FUNCTION STR.shuffle$(s$) + DIM AS INTEGER r + DIM AS STRING c, ls, rs, res + IF LEN(s$) = 0 THEN EXIT FUNCTION + RANDOMIZE TIMER + DO + r% = INT(RND * LEN(s$) + 1) ' random pos in diminishing string + c$ = MID$(s$, r%, 1) ' random char at pos from diminishing string + ls$ = MID$(s$, 1, r% - 1) ' left side of diminishing string sans c$ + rs$ = MID$(s$, r% + 1) ' right side of diminishing string sans c$ + s$ = ls$ + rs$ ' diminish the string (remove c$) + res$ = res$ + c$ ' build the returned string + LOOP UNTIL LEN(s$) = 0 + STR.shuffle$ = res$ +END FUNCTION + + +'' +' Pads both sides of a string with num% chars +' +' @param STRING s$ string to pad +' @param STRING char$ character to use for padding +' @param INTEGER num% number of characters to pad to +' @return STRING padded at the end +FUNCTION STR.pad_both$(s$, char$, num%) + STR.pad_both$ = STR.pad_end$(STR.pad_start$(s$, char$, num%), char$, num%) +END FUNCTION + + +'' +' Pads the end of a string with num% chars +' +' @param STRING s$ string to pad +' @param STRING char$ character to use for padding +' @param INTEGER num% number of characters to pad to +' @return STRING padded at the end +FUNCTION STR.pad_end$(s$, char$, num%) + STR.pad_end$ = s$ + STRING$(num%, char$) +END FUNCTION + + +'' +' Repeats a string num times +' +' @param STRING s$ string to repeat +' @param INTEGER num% number of times to repeat +' @return STRING repeated +FUNCTION STR.repeat$(s$, num%) + DIM i AS INTEGER + DIM res AS STRING + res$ = "" + FOR i% = 1 TO num% + res$ = res$ + s$ + NEXT i% + STR.repeat$ = res$ +END FUNCTION + + +'' +' Determines if a string starts with another string +' +' @param STRING s$ string to check +' @param INTEGER chars$ chars to check if string starts with +' @return INTEGER -1 if starts with 0 if not +FUNCTION STR.starts_with%(s$, chars$) + STR.starts_with% = (LEFT$(s$, LEN(chars$)) = chars$) +END FUNCTION + + +'' +' Determines if a string ends with another string +' +' @param STRING s$ string to check +' @param INTEGER chars$ chars to check if string ends with +' @return INTEGER -1 if ends with 0 if not +FUNCTION STR.ends_with%(s$, chars$) + STR.ends_with% = (RIGHT$(s$, LEN(chars$)) = chars$) END FUNCTION '' -' Replaces a string with another string once (helper for str_replace$) +' Pads the start of a string with num% chars ' -' @param s$ string to replace within -' @param search$ string to search for -' @param replace$ string to replace search with +' @param STRING s$ string to pad +' @param STRING char$ character to use for padding +' @param INTEGER num% number of characters to pad to +' @return STRING padded at the end +FUNCTION STR.pad_start$(s$, char$, num%) + STR.pad_start$ = STRING$(num%, char$) + s$ +END FUNCTION + + +'' +' Replaces a string with another string once (helper for STR.replace$) +' +' @param STRING s$ to replace within +' @param STRING search$ +' @param STRING rep$ string to replace search with if found ' @return STRING with replacement ' -FUNCTION str_rep$ (s$, search$, replace$) +FUNCTION STR.rep$(s$, search$, rep$) DIM AS INTEGER p p% = INSTR(s$, search$) IF p% THEN s$ = LEFT$(s$, p%-1) + RIGHT$(s$, LEN(s$) - p% - LEN(search$)+1) - str_rep$ = LEFT$(s$, p%-1) + replace$ + RIGHT$(s$, LEN(s$) - p%+1) + STR.rep$ = LEFT$(s$, p%-1) + rep$ + RIGHT$(s$, LEN(s$) - p%+1) ELSE - str_rep$ = s$ + STR.rep$ = s$ END IF END FUNCTION @@ -237,18 +683,117 @@ END FUNCTION ' NOTE: This is different than MID$ as MID$ specifies a start and a length, ' NOT an end position. ' -' @param s$ String to slice from -' @param startPos% Position to start slice from -' @param endPos% Position to end slice from +' @param STRING s$ to slice from +' @param INTEGER startPos% to start slice from +' @param INTEGER endPos% to end slice from ' @return STRING of sliced portion of original stright ' -FUNCTION str_slice_pos$ (s$, startPos%, endPos%) +FUNCTION STR.slice_pos$(s$, startPos%, endPos%) IF startPos% <= 0 THEN startPos% = 1 END IF IF endPos% > 0 THEN - str_slice_pos$ = MID$(s$, startPos%, endPos%-startPos%) + STR.slice_pos$ = MID$(s$, startPos%, endPos%-startPos%) ELSE - str_slice_pos$ = MID$(s$, startPos%) + STR.slice_pos$ = MID$(s$, startPos%) END IF -END FUNCTION \ No newline at end of file +END FUNCTION + + +'' +' Returns a space trimmed _UNSIGNED _BYTE as a string +' +' @param _UNSIGNED _BYTE n~%% number to return +' @return STRING space trimmed number +' +FUNCTION STR.ub$(n~%%) + STR.ub$ = _TRIM$(STR$(n~%%)) +END FUNCTION + + +'' +' Returns a space trimmed _UNSIGNED INTEGER as a string +' +' @param _UNSIGNED INTEGER n~% number to return +' @return STRING space trimmed number +' +FUNCTION STR.ui$(n~%) + STR.ui$ = _TRIM$(STR$(n~%)) +END FUNCTION + + +'' +' Returns a space trimmed _UNSIGNED LONG as a string +' +' @param _UNSIGNED LONG n~& number to return +' @return STRING space trimmed number +' +FUNCTION STR.ul$(n~&) + STR.ul$ = _TRIM$(STR$(n~&)) +END FUNCTION + + +'' +' Returns a space trimmed _BYTE as a string +' +' @param _BYTE n~% number to return +' @return STRING space trimmed number +' +FUNCTION STR.b$(n%%) + STR.b$ = _TRIM$(STR$(n%%)) +END FUNCTION + + +'' +' Returns a space trimmed INTEGER as a string +' +' @param INTEGER n% number to return +' @return STRING space trimmed number +' +FUNCTION STR.i$(n%) + STR.i$ = _TRIM$(STR$(n%)) +END FUNCTION + + +'' +' Returns a space trimmed LONG as a string +' +' @param LONG n& number to return +' @return STRING space trimmed number +' +FUNCTION STR.l$(n&) + STR.l$ = _TRIM$(STR$(n&)) +END FUNCTION + + +'' +' Returns a space trimmed SINGLE as a string +' +' @param SINGLE n! number to return +' @return STRING space trimmed number +' +FUNCTION STR.s$(n!) + STR.s$ = _TRIM$(STR$(n!)) +END FUNCTION + + +'' +' Returns a space trimmed DOUBLE as a string +' +' @param DOUBLE n& number to return +' @return STRING space trimmed number +' +FUNCTION STR.d$(n#) + STR.d$ = _TRIM$(STR$(n#)) +END FUNCTION + + +'' +' Returns a space trimmed _FLOAT as a string +' +' @param _FLOAT n& number to return +' @return STRING space trimmed number +' +FUNCTION STR.f$(n##) + STR.f$ = _TRIM$(STR$(n##)) +END FUNCTION diff --git a/SYS/SYS.BAS b/SYS/SYS.BAS index 71e99ab..42ea75d 100644 --- a/SYS/SYS.BAS +++ b/SYS/SYS.BAS @@ -15,7 +15,8 @@ $END IF $IF GJ_LIB_SYS_INC_BI = UNDEFINED THEN '$INCLUDE:'SYS.BI' $END IF - +OPTION _EXPLICIT +OPTION _EXPLICITARRAY _CONSOLETITLE "QB64_GJ_LIB SYS LIB TESTS" diff --git a/SYS/SYS.BI b/SYS/SYS.BI index 6f17764..f2c908b 100644 --- a/SYS/SYS.BI +++ b/SYS/SYS.BI @@ -11,6 +11,3 @@ ' @author Rick Christy ' $LET GJ_LIB_SYS_INC_BI = 1 - -DECLARE SUB open_url_in_browser (url$) -DECLARE FUNCTION sys_info$ () diff --git a/SYS/SYS.BM b/SYS/SYS.BM index 299cfe1..84448de 100644 --- a/SYS/SYS.BM +++ b/SYS/SYS.BM @@ -19,9 +19,9 @@ $LET GJ_LIB_SYS_INC_BM = 1 ' ' ex: open_url_in_browser "https://youtube.com/grymmjack" ' -' @param url$ URL to open (any protocol just passes through) +' @param STRING url$ to open (any protocol just passes through) ' -SUB open_url_in_browser (url$) +SUB open_url_in_browser(url$) DIM AS STRING cmd DIM AS INTEGER ret $IF WINDOWS THEN @@ -40,7 +40,7 @@ END SUB ' ' @return STRING of system information ' -FUNCTION sys_info$ () +FUNCTION sys_info$() DIM AS STRING sout, nl nl$ = CHR$(10) ' For some reason CHR$(13) won't combine strings sout$ = "" @@ -80,7 +80,7 @@ END FUNCTION ' ' @return STRING of devices and button information ' -FUNCTION device_info$ () +FUNCTION device_info$() DIM AS STRING sout, nl, device_name, snum_buttons DIM AS INTEGER i, num_buttons, device_count nl$ = CHR$(10) ' For some reason CHR$(13) won't combine strings diff --git a/VECT2D/README.md b/VECT2D/README.md index 990d47a..919e6a7 100644 --- a/VECT2D/README.md +++ b/VECT2D/README.md @@ -11,32 +11,32 @@ ## WHAT'S IN THE LIBRARY | SUB / FUNCTION | NOTES | |----------------|-------| -| VECT2D_setAxes | Sets both x and y axes of the vector | -| VECT2D_setX | Sets x axis of the vector | -| VECT2D_setY Sub | Sets y axis of the vector | +| VECT2D.setAxes | Sets both x and y axes of the vector | +| VECT2D.setX | Sets x axis of the vector | +| VECT2D.setY Sub | Sets y axis of the vector | | VECT2D$ | Returns VECT2D as a string | -| VECT2D_getX | Get x axis of VECT2D | -| VECT2D_getY | Get y axis of VECT2D | -| VECT2D_add | Add two VECT2D axes together | -| VECT2D_sub | Subtract two VECT2D axes from each other | -| VECT2D_multByVECT2D | Multiply two VECT2D axes together | -| VECT2D_multBySingle | Multiply VECT2D axes by a single number | -| VECT2D_divByVECT2D | Divide two VECT2D axes from each other | -| VECT2D_divBySingle | Divide VECT2D axes by a single number | -| VECT2D_normalize | Normalize a VEC2D into a unit vector | -| VECT2D_unit | Normalize a VEC2D into a unit vector (alias) | -| VECT2D_reverse | Reverse both VECT2D axes (invert sign) | -| VECT2D_abs | Get absolute values for VECT2D axes (ignore sign) | -| VECT2D_zero | Set both VECT2D axes to 0 | -| VECT2D_distance | Get distance between two VECT2Ds | -| VECT2D_rotate | Rotate a vector by radians | -| VECT2D_round | Round the axes of a vector | -| VECT2D_lengthsq | Return length squared of vector | -| VECT2D_length | Return length(magnitude) of vector | -| VECT2D_dotproduct | Get dot product of two vectors | -| VECT2D_crossproduct | Get cross product of two vectors | -| VECT2D_magnitude | Get magnitude(length) of vector | -| VECT2D_eq | Check if two vectors have equal axes | +| VECT2D.getX | Get x axis of VECT2D | +| VECT2D.getY | Get y axis of VECT2D | +| VECT2D.add | Add two VECT2D axes together | +| VECT2D.sub | Subtract two VECT2D axes from each other | +| VECT2D.multByVECT2D | Multiply two VECT2D axes together | +| VECT2D.multBySingle | Multiply VECT2D axes by a single number | +| VECT2D.divByVECT2D | Divide two VECT2D axes from each other | +| VECT2D.divBySingle | Divide VECT2D axes by a single number | +| VECT2D.normalize | Normalize a VEC2D into a unit vector | +| VECT2D.unit | Normalize a VEC2D into a unit vector (alias) | +| VECT2D.reverse | Reverse both VECT2D axes (invert sign) | +| VECT2D.abs | Get absolute values for VECT2D axes (ignore sign) | +| VECT2D.zero | Set both VECT2D axes to 0 | +| VECT2D.distance | Get distance between two VECT2Ds | +| VECT2D.rotate | Rotate a vector by radians | +| VECT2D.round | Round the axes of a vector | +| VECT2D.lengthsq | Return length squared of vector | +| VECT2D.length | Return length(magnitude) of vector | +| VECT2D.dotproduct | Get dot product of two vectors | +| VECT2D.crossproduct | Get cross product of two vectors | +| VECT2D.magnitude | Get magnitude(length) of vector | +| VECT2D.eq | Check if two vectors have equal axes | | radians_to_degrees | Converts radians to degress (wrapper to _R2D) | | degrees_to_radians | Converts degrees to radians (wrapper to _D2R) | diff --git a/VECT2D/VECT2D.BAS b/VECT2D/VECT2D.BAS index 763fbaf..f323239 100644 --- a/VECT2D/VECT2D.BAS +++ b/VECT2D/VECT2D.BAS @@ -1,6 +1,11 @@ ' Ported from: https://github.com/evanshortiss/vector2d +$IF GJ_LIB_UNIFIED_TESTING = DEFINED AND GJ_LIB_COMMON_BI = UNDEFINED THEN '$INCLUDE:'../_GJ_LIB_COMMON.BI' +$END IF +$IF GJ_LIB_VECT2D_INC_BI = UNDEFINED THEN '$INCLUDE:'./VECT2D.BI' +$END IF + DIM v1 AS VECT2D v1.x! = 10 : v1.y! = 20 @@ -11,116 +16,118 @@ v2.x! = 30 : v2.y! = 40 DIM v3 AS VECT2D ' setAxes -VECT2D_setAxes v3, 40.123, 80.456 +CALL VECT2D.setAxes(v3, 40.123, 80.456) ' VECT2D$ not rounded PRINT VECT2D$(v3, FALSE) ' VECT2D$ rounded PRINT VECT2D$(v3, TRUE) ' setX -VECT2D_setX v3, 313 +CALL VECT2D.setX(v3, 313) PRINT VECT2D$(v3, FALSE) ' setY -VECT2D_setY v3, 248.810 +CALL VECT2D.setY(v3, 248.810) PRINT VECT2D$(v3, FALSE) ' getX -PRINT _TRIM$(STR$(VECT2D_getX(v3))) +PRINT _TRIM$(STR$(VECT2D.getX(v3))) ' getY -PRINT _TRIM$(STR$(VECT2D_getY(v3))) +PRINT _TRIM$(STR$(VECT2D.getY(v3))) ' add -VECT2D_add v3, v1, v2 +CALL VECT2D.add(v3, v1, v2) PRINT VECT2D$(v3, FALSE) -VECT2D_add v3, v3, v2 +CALL VECT2D.add(v3, v3, v2) PRINT VECT2D$(v3, FALSE) -VECT2D_add v3, v3, v3 +CALL VECT2D.add(v3, v3, v3) PRINT VECT2D$(v3, FALSE) ' sub -VECT2D_sub v3, v2, v1 +CALL VECT2D.sub(v3, v2, v1) PRINT VECT2D$(v3, FALSE) -VECT2D_sub v3, v3, v2 +CALL VECT2D.sub(v3, v3, v2) PRINT VECT2D$(v3, FALSE) -VECT2D_sub v3, v3, v3 +CALL VECT2D.sub(v3, v3, v3) PRINT VECT2D$(v3, FALSE) ' multByVECT2D -VECT2D_multByVECT2D v3, v1, v2 +CALL VECT2D.multByVECT2D(v3, v1, v2) PRINT VECT2D$(v3, FALSE) ' multBySingle -VECT2D_multBySingle v3, v1, 10 +CALL VECT2D.multBySingle(v3, v1, 10) PRINT VECT2D$(v3, FALSE) ' divByVECT2D -VECT2D_divByVECT2D v3, v1, v2 +CALL VECT2D.divByVECT2D(v3, v1, v2) PRINT VECT2D$(v3, FALSE) ' divBySingle -VECT2D_divBySingle v3, v1, 10 +CALL VECT2D.divBySingle(v3, v1, 10) PRINT VECT2D$(v3, FALSE) ' normalize -VECT2D_normalize v3, v1 +CALL VECT2D.normalize(v3, v1) PRINT VECT2D$(v3, FALSE) ' unit -VECT2D_unit v3, v1 +CALL VECT2D.unit(v3, v1) PRINT VECT2D$(v3, FALSE) ' reverse -VECT2D_reverse v3, v1 +CALL VECT2D.reverse(v3, v1) PRINT VECT2D$(v3, FALSE) ' abs -VECT2D_abs v3, v1 +CALL VECT2D.abs(v3, v1) PRINT VECT2D$(v3, FALSE) ' zero -VECT2D_zero v3, v1 +CALL VECT2D.zero(v3, v1) PRINT VECT2D$(v3, FALSE) ' distance -PRINT _TRIM$(STR$(VECT2D_distance(v1, v2))) +PRINT _TRIM$(STR$(VECT2D.distance(v1, v2))) ' rotate -VECT2D_rotate v3, v1, 100 +CALL VECT2D.rotate(v3, v1, 100) PRINT VECT2D$(v3, FALSE) ' round v1.x! = 123.456 : v1.y! = 789.012 -VECT2D_round v3, v1 +CALL VECT2D.round(v3, v1) PRINT VECT2D$(v3, FALSE) ' lengthsq DIM s AS SINGLE -s = VECT2D_lengthsq(v1) +s = VECT2D.lengthsq(v1) PRINT _TRIM$(STR$(s!)) ' length -s = VECT2D_length(v1) +s = VECT2D.length(v1) PRINT _TRIM$(STR$(s!)) ' dotproduct -s = VECT2D_dotproduct(v1, v2) +s = VECT2D.dotproduct(v1, v2) PRINT _TRIM$(STR$(s!)) ' crossproduct -s = VECT2D_crossproduct(v1, v2) +s = VECT2D.crossproduct(v1, v2) PRINT _TRIM$(STR$(s!)) ' magnitude -s = VECT2D_magnitude(v1.x!, v1.y!) +s = VECT2D.magnitude(v1.x!, v1.y!) PRINT _TRIM$(STR$(s!)) ' eq DIM b AS INTEGER -b% = VECT2D_eq(v1, v2) +b% = VECT2D.eq(v1, v2) IF b% = TRUE THEN PRINT "TRUE" ELSE PRINT "FALSE" -b% = VECT2D_eq(v1, v1) +b% = VECT2D.eq(v1, v1) IF b% = TRUE THEN PRINT "TRUE" ELSE PRINT "FALSE" -'$INCLUDE:'./VECT2D.BM' \ No newline at end of file +$IF GJ_LIB_VECT2D_INC_BM = UNDEFINED THEN +'$INCLUDE:'./VECT2D.BM' +$END IF diff --git a/VECT2D/VECT2D.BM b/VECT2D/VECT2D.BM index 190926c..71b02c2 100644 --- a/VECT2D/VECT2D.BM +++ b/VECT2D/VECT2D.BM @@ -14,14 +14,16 @@ ' $LET GJ_LIB_VECT2D_INC_BM = 1 + '' ' Sets both x and y axes of the vector +' ' @param VECT2D vret Vector with both axes set ' @param SINGLE x x axis ' @param SINGLE y y axis ' @return VECT2D inside vret ' -SUB VECT2D_setAxes (vret AS VECT2D, x AS SINGLE, y AS SINGLE) +SUB VECT2D.setAxes(vret AS VECT2D, x AS SINGLE, y AS SINGLE) vret.x! = x! vret.y! = y! END SUB @@ -29,33 +31,36 @@ END SUB '' ' Sets x axis of the vector +' ' @param VECT2D vret Vector with x axis set ' @param SINGLE x x axis ' @return VECT2D inside vret ' -SUB VECT2D_setX (vret AS VECT2D, x AS SINGLE) +SUB VECT2D.setX(vret AS VECT2D, x AS SINGLE) vret.x! = x! END SUB '' ' Sets y axis of the vector +' ' @param VECT2D vret Vector with y axis set ' @param SINGLE y y axis ' @return VECT2D inside vret ' -SUB VECT2D_setY (vret AS VECT2D, y AS SINGLE) +SUB VECT2D.setY(vret AS VECT2D, y AS SINGLE) vret.y! = y! END SUB '' ' Returns VECT2D as a string +' ' @param VECT2D vec1 Vector to return as string ' @param INTEGER (TRUE/FALSE) round x and y? ' @return STRING representation of VECT2D ' -FUNCTION VECT2D$ (vec1 AS VECT2D, rounded AS INTEGER) +FUNCTION VECT2D$(vec1 AS VECT2D, rounded AS INTEGER) IF rounded% = FALSE THEN VECT2D$ = "(" _ + _TRIM$(STR$(vec1.x!)) _ @@ -63,7 +68,7 @@ FUNCTION VECT2D$ (vec1 AS VECT2D, rounded AS INTEGER) + ")" ELSE DIM vret AS VECT2D - VECT2D_round vret, vec1 + VECT2D.round vret, vec1 VECT2D$ = "(" _ + _TRIM$(STR$(vret.x!)) _ + ", " + _TRIM$(STR$(vret.y!)) _ @@ -74,33 +79,36 @@ END FUNCTION '' ' Get x axis of VECT2D +' ' @param VECT2D vec1 Vector to get x axis for ' @return SINGLE x axis ' -FUNCTION VECT2D_getX! (vec1 AS VECT2D) - VECT2D_getX! = vec1.x! +FUNCTION VECT2D.getX!(vec1 AS VECT2D) + VECT2D.getX! = vec1.x! END FUNCTION '' ' Get y axis of VECT2D +' ' @param VECT2D vec1 Vector to get y axis for ' @return SINGLE y axis ' -FUNCTION VECT2D_getY! (vec1 AS VECT2D) - VECT2D_getY! = vec1.y! +FUNCTION VECT2D.getY!(vec1 AS VECT2D) + VECT2D.getY! = vec1.y! END FUNCTION '' ' Add two VECT2D axes together +' ' @param VECT2D vret Return vector with result of addition ' @param VECT2D vec1 Left VECT2D operand ' @param VECT2D vec2 Right VECT2D operand ' @return VECT2D inside vret ' -SUB VECT2D_add (vret AS VECT2D, vec1 AS VECT2D, vec2 AS VECT2D) +SUB VECT2D.add(vret AS VECT2D, vec1 AS VECT2D, vec2 AS VECT2D) vret.x! = vec1.x! + vec2.x! vret.y! = vec1.y! + vec2.y! END SUB @@ -108,12 +116,13 @@ END SUB '' ' Subtract two VECT2D axes from each other +' ' @param VECT2D vret Return vector with result of subtraction ' @param VECT2D vec1 Left VECT2D operand ' @param VECT2D vec2 Right VECT2D operand ' @return VECT2D inside vret ' -SUB VECT2D_sub (vret AS VECT2D, vec1 AS VECT2D, vec2 AS VECT2D) +SUB VECT2D.sub(vret AS VECT2D, vec1 AS VECT2D, vec2 AS VECT2D) vret.x! = vec1.x! - vec2.x! vret.y! = vec1.y! - vec2.y! END SUB @@ -121,12 +130,13 @@ END SUB '' ' Multiply two VECT2D axes together +' ' @param VECT2D vret Return vector with result of mulitplication ' @param VECT2D vec1 Left VECT2D operand ' @param VECT2D vec2 Right VECT2D operand ' @return VECT2D inside vret ' -SUB VECT2D_multByVECT2D (vret AS VECT2D, vec1 AS VECT2D, vec2 AS VECT2D) +SUB VECT2D.multByVECT2D(vret AS VECT2D, vec1 AS VECT2D, vec2 AS VECT2D) vret.x! = vec1.x! * vec2.x! vret.y! = vec1.y! * vec2.y! END SUB @@ -134,12 +144,13 @@ END SUB '' ' Multiply VECT2D axes by a single number +' ' @param VECT2D vret Return vector with result of mulitplication ' @param VECT2D vec1 VECT2D to multiply axes of ' @param SINGLE n Number to mulitply by ' @return VECT2D inside vret ' -SUB VECT2D_multBySingle (vret AS VECT2D, vec1 AS VECT2D, n AS single) +SUB VECT2D.multBySingle(vret AS VECT2D, vec1 AS VECT2D, n AS single) vret.x! = vec1.x! * n! vret.y! = vec1.y! * n! END SUB @@ -147,12 +158,13 @@ END SUB '' ' Divide two VECT2D axes from each other +' ' @param VECT2D vret Return vector with result of division ' @param VECT2D vec1 Left VECT2D operand ' @param VECT2D vec2 Right VECT2D operand ' @return VECT2D inside vret ' -SUB VECT2D_divByVECT2D (vret AS VECT2D, vec1 AS VECT2D, vec2 AS VECT2D) +SUB VECT2D.divByVECT2D(vret AS VECT2D, vec1 AS VECT2D, vec2 AS VECT2D) vret.x! = vec1.x! / vec2.x! vret.y! = vec1.y! / vec2.y! END SUB @@ -160,48 +172,52 @@ END SUB '' ' Divide VECT2D axes by a single number +' ' @param VECT2D vret Return vector with result of division ' @param VECT2D vec1 VECT2D to divide axes of ' @param SINGLE n Number to divide by ' @return VECT2D inside vret ' -SUB VECT2D_divBySingle (vret AS VECT2D, vec1 AS VECT2D, n AS SINGLE) +SUB VECT2D.divBySingle(vret AS VECT2D, vec1 AS VECT2D, n AS SINGLE) vret.x! = vec1.x! / n! vret.y! = vec1.y! / n! END SUB '' -' Normalize a VEC2D into a unit vector +' Normalize a VECT2D into a unit vector +' ' @param VECT2D vret Return vector normalized ' @param VECT2D vec1 Vector to normalize ' @return VECT2D inside vret ' -SUB VECT2D_normalize (vret AS VECT2D, vec1 AS VECT2D) +SUB VECT2D.normalize(vret AS VECT2D, vec1 AS VECT2D) DIM magnitude AS SINGLE - magnitude! = VECT2D_magnitude(vec1.x!, vec1.y!) - VECT2D_divBySingle vret, vec1, magnitude! + magnitude! = VECT2D.magnitude(vec1.x!, vec1.y!) + VECT2D.divBySingle vret, vec1, magnitude! END SUB '' -' Normalize a VEC2D into a unit vector (alias) +' Normalize a VECT2D into a unit vector (alias) +' ' @param VECT2D vret Return vector normalized ' @param VECT2D vec1 Vector to normalize ' @return VECT2D inside vret ' -SUB VECT2D_unit (vret AS VECT2D, vec1 AS VECT2D) - VECT2D_normalize vret, vec1 +SUB VECT2D.unit(vret AS VECT2D, vec1 AS VECT2D) + VECT2D.normalize vret, vec1 END SUB '' ' Reverse both VECT2D axes (invert sign) +' ' @param VECT2D vret Return vector with axes reversed/inverted ' @param VECT2D vec1 Vector to reverse/invert ' @return VECT2D inside vret ' -SUB VECT2D_reverse (vret AS VECT2D, vec1 AS VECT2D) +SUB VECT2D.reverse(vret AS VECT2D, vec1 AS VECT2D) vret.x! = -vec1.x! vret.y! = -vec1.y! END SUB @@ -209,11 +225,12 @@ END SUB '' ' Get absolute values for VECT2D axes (ignore sign) +' ' @param VECT2D vret Return vector with unsigned axes ' @param VECT2D vec1 Vector to get axes for ' @return VECT2D inside vret ' -SUB VECT2D_abs (vret AS VECT2D, vec1 AS VECT2D) +SUB VECT2D.abs(vret AS VECT2D, vec1 AS VECT2D) vret.x! = ABS(vec1.x!) vret.y! = ABS(vec1.y!) END SUB @@ -221,11 +238,11 @@ END SUB '' ' Set both VECT2D axes to 0 +' ' @param VECT2D vret Return vector with zeroed axes -' @param VECT2D vec1 Vector to zero axes for ' @return VECT2D inside vret ' -SUB VECT2D_zero (vret AS VECT2D, vec1 AS VECT2D) +SUB VECT2D.zero(vret AS VECT2D) vret.x! = 0 vret.y! = 0 END SUB @@ -233,26 +250,28 @@ END SUB '' ' Get distance between two VECT2Ds +' ' @param VECT2D vec1 Vector to measure distance from ' @param VECT2D vec2 Vector to measure distance to ' @return SINGLE distance between the vectors ' -FUNCTION VECT2D_distance! (vec1 AS VECT2D, vec2 AS VECT2D) +FUNCTION VECT2D.distance!(vec1 AS VECT2D, vec2 AS VECT2D) DIM AS SINGLE x, y x! = vec1.x! - vec2.x! y! = vec1.y! - vec2.y! - VECT2D_distance! = SQR(x! * x! + y! * y!) + VECT2D.distance! = SQR(x! * x! + y! * y!) END FUNCTION '' ' Rotate a vector by radians +' ' @param VECT2D vret Return vector with rotated axes ' @param VECT2D vec1 Vectore to rotate axes of ' @param SINGLE radians Radians to rotate vector by ' @return VECT2D inside vret ' -SUB VECT2D_rotate (vret as VECT2D, vec1 AS VECT2D, radians as SINGLE) +SUB VECT2D.rotate(vret as VECT2D, vec1 AS VECT2D, radians as SINGLE) DIM AS SINGLE cosine, sine cosine! = COS(radians) sine! = SIN(radians) @@ -263,11 +282,12 @@ END SUB '' ' Round the axes of a vector +' ' @param VECT2D vret Return vector with rounded axes ' @param VECT2D vec1 Vector to round axes for ' @return VECT2D inside vret ' -SUB VECT2D_round (vret AS VECT2D, vec1 AS VECT2D) +SUB VECT2D.round(vret AS VECT2D, vec1 AS VECT2D) vret.x! = _ROUND(vec1.x!) vret.y! = _ROUND(vec1.y!) END SUB @@ -275,89 +295,97 @@ END SUB '' ' Return length squared of vector +' ' @param VECT2D vec1 Vector to operate on ' @return SINGLE length squared of vector ' -FUNCTION VECT2D_lengthsq! (vec1 AS VECT2D) - VECT2D_lengthsq! = vec1.x! * vec1.x! + vec1.y! * vec1.y! +FUNCTION VECT2D.lengthsq!(vec1 AS VECT2D) + VECT2D.lengthsq! = vec1.x! * vec1.x! + vec1.y! * vec1.y! END FUNCTION '' ' Return length(magnitude) of vector +' ' @param VECT2D vec1 Vector to get length(magnitude) for ' @return SINGLE length of vector ' -FUNCTION VECT2D_length! (vec1 AS VECT2D) - VECT2D_length! = VECT2D_magnitude(vec1.x!, vec1.y!) +FUNCTION VECT2D.length!(vec1 AS VECT2D) + VECT2D.length! = VECT2D.magnitude(vec1.x!, vec1.y!) END FUNCTION '' ' Get dot product of two vectors +' ' @param VECT2D vec1 Left VECT2D operand ' @param VECT2D vec1 Right VECT2D operand ' @return SINGLE dot product of two vectors ' -FUNCTION VECT2D_dotproduct! (vec1 AS VECT2D, vec2 AS VECT2D) - VECT2D_dotproduct! = vec1.x! * vec2.x! + vec1.y! * vec2.y! +FUNCTION VECT2D.dotproduct!(vec1 AS VECT2D, vec2 AS VECT2D) + VECT2D.dotproduct! = vec1.x! * vec2.x! + vec1.y! * vec2.y! END FUNCTION '' ' Get cross product of two vectors +' ' @param VECT2D vec1 Left VECT2D operand ' @param VECT2D vec1 Right VECT2D operand ' @return SINGLE cross product of two vectors ' -FUNCTION VECT2D_crossproduct! (vec1 AS VECT2D, vec2 AS VECT2D) - VECT2D_crossproduct! = vec1.x! * vec2.y! - vec1.y! * vec2.x! +FUNCTION VECT2D.crossproduct!(vec1 AS VECT2D, vec2 AS VECT2D) + VECT2D.crossproduct! = vec1.x! * vec2.y! - vec1.y! * vec2.x! END FUNCTION '' ' Get magnitude(length) of vector +' ' @param SINGLE x axis of vector ' @param SINGLE y axis of vector ' @return SINGLE magnitude(length) of vector ' -FUNCTION VECT2D_magnitude! (x AS SINGLE, y AS SINGLE) - VECT2D_magnitude! = SQR(x * x + y * y) +FUNCTION VECT2D.magnitude!(x AS SINGLE, y AS SINGLE) + VECT2D.magnitude! = SQR(x * x + y * y) END FUNCTION '' ' Check if two vectors have equal axes +' ' @param VECT2D vec1 Left VECT2D operand ' @param VECT2D vec2 Right VECT2D operand ' @return INTEGER (TRUE/FALSE) if vectors are equal ' -FUNCTION VECT2D_eq% (vec1 AS VECT2D, vec2 AS VECT2D) +FUNCTION VECT2D.eq%(vec1 AS VECT2D, vec2 AS VECT2D) IF vec1.x! = vec2.x! AND vec1.y! = vec2.y! THEN - VECT2D_eq% = TRUE + VECT2D.eq% = TRUE ELSE - VECT2D_eq% = FALSE + VECT2D.eq% = FALSE END IF END FUNCTION '' ' Converts radians to degress (wrapper to _R2D) +' ' @param SINGLE radians to convert to degrees ' @return SINGLE degrees converted from radians ' -FUNCTION radians_to_degrees! (radians AS SINGLE) - radians_to_degrees! = _R2D(radians) +FUNCTION VECT2D.radians_to_degrees!(radians AS SINGLE) + VECT2D.radians_to_degrees! = _R2D(radians) ' Formula: radians_to_degrees! = radians! * 180 / _PI END FUNCTION '' ' Converts degrees to radians (wrapper to _D2R) +' ' @param SINGLE radians to convert to degrees ' @return SINGLE radians converted from degrees ' -FUNCTION degrees_to_radians! (degrees AS SINGLE) - degrees_to_radians! = _D2R(degrees) +FUNCTION VECT2D.degrees_to_radians!(degrees AS SINGLE) + VECT2D.degrees_to_radians! = _D2R(degrees) ' Formula: degrees_to_radians! = degrees! * _PI / 180 END FUNCTION diff --git a/VIDEO_MODES/README.md b/VIDEO_MODES/README.md new file mode 100644 index 0000000..1cb1bcd --- /dev/null +++ b/VIDEO_MODES/README.md @@ -0,0 +1,105 @@ +VIDEO MODES LIBRARY just a lookup of resolutions, and palettes. +https://en.wikipedia.org/wiki/List_of_8-bit_computer_hardware_graphics + +https://en.wikipedia.org/wiki/List_of_software_palettes + +## DEFAULT / SOFTWARE PALETTES OF NOTE: +- [ ] WINDOWS+OS2 +- [ ] WINDOWS20 +- [ ] WINDOWS COLOR PICKER +- [ ] APPLEMAC16 +- [ ] APPLECRAYONS +- [ ] RISCOS +- [ ] WEBSAFE +- [ ] GRAYSCALE +- [ ] POWERPOINT +- [ ] PHOTOSHOP + +### TO DO: + +- [ ] CGA MODE 1 - 160×100 in 16 colors, chosen from a 16-color palette, utilizing a specific configuration of the 80x25 text mode. +- [ ] CGA MODE 2 - 320×200 in 4 colors, chosen from 3 fixed palettes, with high- and low-intensity variants, with color 1 chosen from a 16-color palette. + - [ ] PAL 0 - LOW INTENSITY - red, yellow, green, black + - [ ] PAL 0 - HIGH INTENSITY - red, yellow, green, black + - [ ] PAL 1 - LOW INTENSITY - cyan, magenta, white, black + - [ ] PAL 1 - HIGH INTENSITY - cyan, magenta, white, black + - [ ] PAL 2 - LOW INTENSITY - cyan, red, white, black + - [ ] PAL 2 - HIGH INTENSITY - cyan, red, white, black +- [ ] CGA MODE 3 - 640×200 in 2 colors, one black, one chosen from a 16-color palette. + +- [ ] EGA MODE - https://en.wikipedia.org/wiki/Enhanced_Graphics_Adapter + +EGA supports: + + 640 × 350 × 16 colors (from a 6 bit palette of 64 colors), pixel aspect ratio of 1:1.37. + 640 × 350 × 2 colors, pixel aspect ratio of 1:1.37. + 640 × 200 × 16 colors, pixel aspect ratio of 1:2.4. + 320 × 200 × 16 colors, pixel aspect ratio of 1:1.2. + +Text modes: + + 40 × 25 with 8×8 pixel font (effective resolution of 320 × 200) + 80 × 25 with 8×8 pixel font (effective resolution of 640 × 200) + 80 × 25 with 8×14 pixel font (effective resolution of 640 × 350) + 80 × 43 with 8×8 pixel font (effective resolution of 640 × 344) + +Extended graphics modes of third party boards: + + 640 × 400 + 640 × 480 + 720 × 540 + 800 × 560 + +- [ ] VGA MODE + +Standard graphics modes + + 640 × 480 in 16 colors or monochrome[14][15] + 640 × 350 or 640 × 200 in 16 colors or monochrome (EGA/CGA compatibility) + 320 × 200 in 256 colors (Mode 13h) + 320 × 200 in 4 or 16 colors (CGA compatibility) + +Nonstandard display modes can be implemented, with horizontal resolutions of: + + 512 to 800 pixels wide, in 16 colors + 256 to 400 pixels wide, in 256 colors + +VGA also implements several text modes: + + 80 × 25, rendered with a 9 × 16 pixel font, with an effective resolution of 720 × 400[21] + 40 × 25, with a 9 × 16 font, with an effective resolution of 360 × 400 + 80 × 43 or 80 × 50, with an 8 × 8 font grid, with an effective resolution of 640 × 344 or 640 × 400 pixels. + +- [ ] C64 MODES / PALETTE +- [ ] APPLE 2 MODES / PALETTE +- [ ] ZX SPECTRUM MODES / PALETTE + +https://en.wikipedia.org/wiki/List_of_video_game_console_palettes +- [ ] FAIRCHILD CHANNEL F +- [ ] ATARI 2600 NTSC +- [ ] ATARI 2600 PAL +- [ ] INTELLIVISION +- [ ] NES MODES / PALETTE +- [ ] GAMEBOY MODES / PALETTE +- [ ] SNES MODES / PALETTE +- [ ] SEGA MASTER SYSTEM MODES / PALETTE +- [ ] SEGA GENESIS MODES / PALETTE +- [ ] TURBOGRAFX 16 MODES / PALETTE + +- [ ] ATARI 8-BIT PALETTE + +https://www.atariarchives.org/agagd/chapter1.php +COLOR VALUES FOR COLOR REGISTERS + + VALUE HUE LUMINANCE VALUE HUE LUMINANCE +BLACK 0($00) 0 0 MAGENTA 82($52) 5 2 +DARK GREY 4($04) 0 4 PURPLE 96($60) 6 0 +GREY 6($06) 0 6 LAVENDER 102($66) 6 6 +WHITE 14($0E) 0 14 BLUE 116($74) 7 4 +GOLD 20($14) 1 4 LTBLUE 120($78) 7 8 +YELLOW 24($18) 1 8 TURQUOISE 164($A4) 10 4 +BROWN 34($22) 2 2 GREEN 196($C4) 12 4 +TAN 36($24) 2 4 LIGHT GREEN 200 ($C8) 12 8 +ORANGE 54($36) 3 6 YELLOW GREEN 214($D6) 13 6 +RED 66($42) 4 2 OLIVE GREEN 228($E4) 11 4 +PINK 72($48) 4 8 PEACH 246($F6) 15 6 diff --git a/_GJ_LIB.BI b/_GJ_LIB.BI index c23f46b..3e64fbe 100644 --- a/_GJ_LIB.BI +++ b/_GJ_LIB.BI @@ -50,3 +50,9 @@ $END IF $IF GJ_LIB_VECT2D_INC_BI = UNDEFINED THEN '$INCLUDE:'VECT2D/VECT2D.BI' $END IF +$IF GJ_LIB_CONSOLE_INC_BI = UNDEFINED THEN +'$INCLUDE:'CONSOLE/CONSOLE.BI' +$END IF +$IF GJ_LIB_MISC_INC_BI = UNDEFINED THEN +'$INCLUDE:'MISC/MISC.BI' +$END IF diff --git a/_GJ_LIB.BM b/_GJ_LIB.BM index 4744cf9..d9fba01 100644 --- a/_GJ_LIB.BM +++ b/_GJ_LIB.BM @@ -18,6 +18,42 @@ $LET GJ_LIB_INC_BM = 1 ' Unified includes to use everything in the library from single BM include +$IF GJ_LIB_ARR_BYTE_BAS = UNDEFINED THEN +'$INCLUDE:'ARR/ARR_BYTE.BAS' +$END IF +$IF GJ_LIB_ARR_DBL_BAS = UNDEFINED THEN +'$INCLUDE:'ARR/ARR_DBL.BAS' +$END IF +$IF GJ_LIB_ARR_FLT_BAS = UNDEFINED THEN +'$INCLUDE:'ARR/ARR_FLT.BAS' +$END IF +$IF GJ_LIB_ARR_INT_BAS = UNDEFINED THEN +'$INCLUDE:'ARR/ARR_INT.BAS' +$END IF +$IF GJ_LIB_ARR_INT64_BAS = UNDEFINED THEN +'$INCLUDE:'ARR/ARR_INT64.BAS' +$END IF +$IF GJ_LIB_ARR_LONG_BAS = UNDEFINED THEN +'$INCLUDE:'ARR/ARR_LONG.BAS' +$END IF +$IF GJ_LIB_ARR_SNG_BAS = UNDEFINED THEN +'$INCLUDE:'ARR/ARR_SNG.BAS' +$END IF +$IF GJ_LIB_ARR_STR_BAS = UNDEFINED THEN +'$INCLUDE:'ARR/ARR_STR.BAS' +$END IF +$IF GJ_LIB_ARR_UBYTE_BAS = UNDEFINED THEN +'$INCLUDE:'ARR/ARR_UBYTE.BAS' +$END IF +$IF GJ_LIB_ARR_UINT_BAS = UNDEFINED THEN +'$INCLUDE:'ARR/ARR_UINT.BAS' +$END IF +$IF GJ_LIB_ARR_UINT64_BAS = UNDEFINED THEN +'$INCLUDE:'ARR/ARR_UINT64.BAS' +$END IF +$IF GJ_LIB_ARR_ULONG_BAS = UNDEFINED THEN +'$INCLUDE:'ARR/ARR_ULONG.BAS' +$END IF $IF GJ_LIB_DICT_INC_BM = UNDEFINED THEN '$INCLUDE:'DICT/DICT.BM' $END IF @@ -45,3 +81,9 @@ $END IF $IF GJ_LIB_VECT2D_INC_BM = UNDEFINED THEN '$INCLUDE:'VECT2D/VECT2D.BM' $END IF +$IF GJ_LIB_CONSOLE_INC_BM = UNDEFINED THEN +'$INCLUDE:'CONSOLE/CONSOLE.BM' +$END IF +$IF GJ_LIB_MISC_INC_BM = UNDEFINED THEN +'$INCLUDE:'MISC/MISC.BM' +$END IF diff --git a/_GJ_LIB_COMMON.BI b/_GJ_LIB_COMMON.BI index 9498df2..c975d05 100644 --- a/_GJ_LIB_COMMON.BI +++ b/_GJ_LIB_COMMON.BI @@ -12,4 +12,12 @@ $END IF $IF FALSE = UNDEFINED AND TRUE = UNDEFINED THEN $LET TRUE = TRUE CONST FALSE = 0 : CONST TRUE = NOT FALSE +$END IF + +$IF SLASH = UNDEFINED THEN + $IF WIN AND SLASH THEN + CONST SLASH$ = "\" + $ELSE + CONST SLASH$ = "/" + $END IF $END IF \ No newline at end of file diff --git a/_OPTION_TEST_FLATTENED.BAS b/_OPTION_TEST_FLATTENED.BAS new file mode 100644 index 0000000..16a7bcd --- /dev/null +++ b/_OPTION_TEST_FLATTENED.BAS @@ -0,0 +1,16694 @@ +'' +' QB64_GJ_LIB +' +' Tests OPTION _EXPLICIT +' Tests OPTION _EXPLICITARRAY +' +' @author Rick Christy +' @uses _GJ_LIB.BI +' @uses _GJ_LIB.BM +' + +OPTION _EXPLICIT +OPTION _EXPLICITARRAY + '' + ' GRYMMJACK'S LIB FOR QB64 + ' + ' This is the master BI file, which includes the other BI files for the library. + ' + ' @author Rick Christy + ' @uses _GJ_LIB_COMMON.BI + ' @uses DICT/DICT.BI + ' @uses DUMP/DUMP.BI + ' @uses ANSI/ANSI.BI + ' @uses STRINGS/STRINGS.BI + ' @uses PIPEPRINT/PIPEPRINT.BI + ' @uses SYS/SYS.BI + ' + $LET GJ_LIB_INC_BI = 1 + $IF GJ_LIB_INC_COMMON_BI = UNDEFINED THEN + '' + ' GRYMMJACK'S LIB FOR QB64 + ' + ' This file contains constants and variables common among the entire library. + ' + ' @author Rick Christy + ' + $IF GJLIB_COMMON_BI = UNDEFINED THEN + $LET GJ_LIB_INC_COMMON_BI = 1 + $END IF + + $IF FALSE = UNDEFINED AND TRUE = UNDEFINED THEN + $LET TRUE = TRUE + CONST FALSE = 0 : CONST TRUE = NOT FALSE + $END IF + $END IF + + ' Unified testing + ' $LET GJ_LIB_UNIFIED_TESTING = 1 + ' Enable and disable debugging by setting this variable to TRUE or FALSE: + DIM GJ_LIB_DEBUGGING AS INTEGER : GJ_LIB_DEBUGGING = TRUE + + ' Unified includes to use everything in the library from single BI include + $IF GJ_LIB_DICT_INC_BI = UNDEFINED THEN + '' + ' QB64_GJ_LIB + ' GRYMMJACK'S DICT Object + ' + ' Simulates a dictionary object as found in other languages. + ' + ' USAGE: + ' Insert '$INCLUDE:'path_to_GJ_LIB/DICT/DICT.BI' at the top of file + ' Insert '$INCLUDE:'path_to_GJ_LIB/DICT/DICT.BM' at the bottom of file + ' + ' @author Rick Christy + ' @uses DICT.BM + ' + $LET GJ_LIB_DICT_INC_BI = 1 + + + + ' DICTIONARY type consists of keys and values and is intended for array use + TYPE DICTIONARY + key AS STRING + val AS STRING + END TYPE + $END IF + $IF GJ_LIB_DUMP_INC_BI = UNDEFINED THEN + '' + ' QB64_GJ_LIB + ' GRYMMJACK'S DUMP LIB + ' + ' USAGE: + ' Insert '$INCLUDE:'path_to_GJ_LIB/DUMP/DUMP.BI' at the top of file + ' Insert '$INCLUDE:'path_to_GJ_LIB/DUMP/DUMP.BM' at the bottom of file + ' + ' @author Rick Christy + ' @uses DUMP.BM + ' @uses DICT.BI + ' + $LET GJ_LIB_DUMP_INC_BI = 1 + $IF GJ_LIB_DICT_INC_BI = UNDEFINED THEN + '' + ' QB64_GJ_LIB + ' GRYMMJACK'S DICT Object + ' + ' Simulates a dictionary object as found in other languages. + ' + ' USAGE: + ' Insert '$INCLUDE:'path_to_GJ_LIB/DICT/DICT.BI' at the top of file + ' Insert '$INCLUDE:'path_to_GJ_LIB/DICT/DICT.BM' at the bottom of file + ' + ' @author Rick Christy + ' @uses DICT.BM + ' + $LET GJ_LIB_DICT_INC_BI = 1 + + + + ' DICTIONARY type consists of keys and values and is intended for array use + TYPE DICTIONARY + key AS STRING + val AS STRING + END TYPE + $END IF + + DIM SHARED GJ_LIB_NL AS STRING + $IF WIN THEN + GJ_LIB_NL$ = CHR$(13) + $ELSE + GJ_LIB_NL$ = CHR$(10) + $END IF + $END IF + $IF GJ_LIB_INPUT_LIGHTBAR_INC_BI = UNDEFINED THEN + '' + ' QB64_GJ_LIB + ' GRYMMJACK'S INPUT LIB - LIGHTBAR + ' + ' USAGE: + ' Insert '$INCLUDE:'path_to_GJ_LIB/INPUT/LIGHTBAR.BI' at the top of file + ' Insert '$INCLUDE:'path_to_GJ_LIB/INPUT/LIGHTBAR.BM' at the bottom of file + ' + ' @author Rick Christy + ' @uses LIGHTBAR.BM + ' + $LET GJ_LIB_INPUT_LIGHTBAR_INC_BI = 1 + + ' LIGHTBAR configuration UDT + TYPE LIGHTBAR + opt_bg_color AS INTEGER ' Unselected background color + opt_fg_color AS INTEGER ' Unselected foreground color + bar_bg_color AS INTEGER ' Selected background color + bar_fg_color AS INTEGER ' Selected foreground color + bar_kf_color AS INTEGER ' Selected hot key foreground color + bar_kb_color AS INTEGER ' Selected hot key background color + key_bg_color AS INTEGER ' Unselected hot key background color + key_fg_color AS INTEGER ' Unselected hot key foreground color + opt_selected AS INTEGER ' Selected option index + opt_vertical AS INTEGER ' 1 = true (then vertical) 0 = false (then horiz) + max_width AS INTEGER ' Maximum width for horizontal options + delimeter AS STRING ' Single character used to surround hot key + use_sounds AS INTEGER ' 1 = true (use sounds) 0 = false (no sounds) + snd_move_frq AS SINGLE ' Frequency for SOUND movement + snd_move_dur AS SINGLE ' Duration for SOUND movement + snd_move_vol AS SINGLE ' Volume for SOUND movement + snd_pick_frq AS SINGLE ' Frequency for SOUND pick + snd_pick_dur AS SINGLE ' Duration for SOUND pick + snd_pick_vol AS SINGLE ' Volume for SOUND pick + snd_abrt_frq AS SINGLE ' Frequency for SOUND pick + snd_abrt_dur AS SINGLE ' Duration for SOUND pick + snd_abrt_vol AS SINGLE ' Volume for SOUND pick + END TYPE + + ' UDT for option data + TYPE LIGHTBAR_OPTION + txt AS STRING ' Option raw string + row AS INTEGER ' Option row + col AS INTEGER ' Option column + lft AS STRING ' Option left side text + key AS STRING ' Option hot key + rgt AS STRING ' Option right side text + len AS INTEGER ' Option length (left + key + right) + sel AS INTEGER ' Is this option selected? 0 = no, 1 = yes + END TYPE + $END IF + $IF GJ_LIB_INPUT_LIGHTBAR32_INC_BI = UNDEFINED THEN + '' + ' QB64_GJ_LIB + ' GRYMMJACK'S INPUT LIB - LIGHTBAR32 + ' + ' USAGE: + ' Insert '$INCLUDE:'path_to_GJ_LIB/INPUT/LIGHTBAR32.BI' at the top of file + ' Insert '$INCLUDE:'path_to_GJ_LIB/INPUT/LIGHTBAR32.BM' at the bottom of file + ' + ' @author Rick Christy + ' @uses LIGHTBAR32.BM + ' + $LET GJ_LIB_INPUT_LIGHTBAR32_INC_BI = 1 + + ' LIGHTBAR configuration UDT + TYPE LIGHTBAR32 + opt_bg_color AS _UNSIGNED LONG ' Unselected background color + opt_fg_color AS _UNSIGNED LONG ' Unselected foreground color + bar_bg_color AS _UNSIGNED LONG ' Selected background color + bar_fg_color AS _UNSIGNED LONG ' Selected foreground color + bar_kf_color AS _UNSIGNED LONG ' Selected hot key foreground color + bar_kb_color AS _UNSIGNED LONG ' Selected hot key background color + key_bg_color AS _UNSIGNED LONG ' Unselected hot key background color + key_fg_color AS _UNSIGNED LONG ' Unselected hot key foreground color + opt_selected AS INTEGER ' Selected option index + opt_vertical AS INTEGER ' 1 = true (then vertical) 0 = false (then horiz) + max_width AS INTEGER ' Maximum width for horizontal options + delimeter AS STRING ' Single character used to surround hot key + use_sounds AS INTEGER ' 1 = true (use sounds) 0 = false (no sounds) + snd_move_frq AS SINGLE ' Frequency for SOUND movement + snd_move_dur AS SINGLE ' Duration for SOUND movement + snd_move_vol AS SINGLE ' Volume for SOUND movement + snd_pick_frq AS SINGLE ' Frequency for SOUND pick + snd_pick_dur AS SINGLE ' Duration for SOUND pick + snd_pick_vol AS SINGLE ' Volume for SOUND pick + snd_abrt_frq AS SINGLE ' Frequency for SOUND pick + snd_abrt_dur AS SINGLE ' Duration for SOUND pick + snd_abrt_vol AS SINGLE ' Volume for SOUND pick + END TYPE + + ' UDT for option data + TYPE LIGHTBAR32_OPTION + txt AS STRING ' Option raw string + row AS INTEGER ' Option row + col AS INTEGER ' Option column + lft AS STRING ' Option left side text + key AS STRING ' Option hot key + rgt AS STRING ' Option right side text + len AS INTEGER ' Option length (left + key + right) + sel AS INTEGER ' Is this option selected? 0 = no, 1 = yes + END TYPE + + DIM SHARED LB_EGA(0 TO 15) AS _UNSIGNED LONG + LB_EGA(0) = _RGB32(&H00, &H00, &H00) + LB_EGA(1) = _RGB32(&H00, &H00, &HAA) + LB_EGA(2) = _RGB32(&H00, &HAA, &H00) + LB_EGA(3) = _RGB32(&H00, &HAA, &HAA) + LB_EGA(4) = _RGB32(&HAA, &H00, &H00) + LB_EGA(5) = _RGB32(&HAA, &H00, &HAA) + LB_EGA(6) = _RGB32(&HAA, &H55, &H00) + LB_EGA(7) = _RGB32(&HAA, &HAA, &HAA) + LB_EGA(8) = _RGB32(&H55, &H55, &H55) + LB_EGA(9) = _RGB32(&H55, &H55, &HFF) + LB_EGA(10) = _RGB32(&H55, &HFF, &H55) + LB_EGA(11) = _RGB32(&H55, &HFF, &HFF) + LB_EGA(12) = _RGB32(&HFF, &H55, &H55) + LB_EGA(13) = _RGB32(&HFF, &H55, &HFF) + LB_EGA(14) = _RGB32(&HFF, &HFF, &H55) + LB_EGA(15) = _RGB32(&HFF, &HFF, &HFF) + $END IF + $IF GJ_LIB_ANSI_INC_BI = UNDEFINED THEN + '' + ' QB64_GJ_LIB + ' GRYMMJACK'S ANSI LIB + ' + ' USAGE: + ' Insert '$INCLUDE:'path_to_GJ_LIB/ANSI/ANSI.BI' at the top of file + ' Insert '$INCLUDE:'path_to_GJ_LIB/ANSI/ANSI.BM' at the bottom of file + ' + ' @author Rick Christy + ' @uses _GJ_LIB_COMMON.BI + ' @uses ANSI.BM + ' + $LET GJ_LIB_ANSI_INC_BI = 1 + + $IF GJ_LIB_INC_COMMON_BI = UNDEFINED THEN + '' + ' GRYMMJACK'S LIB FOR QB64 + ' + ' This file contains constants and variables common among the entire library. + ' + ' @author Rick Christy + ' + $IF GJLIB_COMMON_BI = UNDEFINED THEN + $LET GJ_LIB_INC_COMMON_BI = 1 + $END IF + + $IF FALSE = UNDEFINED AND TRUE = UNDEFINED THEN + $LET TRUE = TRUE + CONST FALSE = 0 : CONST TRUE = NOT FALSE + $END IF + $END IF + + ' Emulate ANSI functionality using QB internals while outputing ANSI codes? + ' Default = FALSE + $IF ANSI_DEBUGGING = UNDEFINED THEN + CONST ANSI.ESC=27 + $ELSE + CONST ANSI.ESC=32 + $END IF + + DIM SHARED GJ_LIB_ANSI_EMU AS INTEGER + GJ_LIB_ANSI_EMU = FALSE + + DIM SHARED GJ_LIB_ANSI_OUTPUT AS INTEGER + GJ_LIB_ANSI_OUTPUT = TRUE + + DIM SHARED ANSI.x% ' Cursor x position + DIM SHARED ANSI.y% ' Cursor y position + ANSI.x% = POS(0) + ANSI.y% = CSRLIN + DIM SHARED ANSI.save_x% ' Cursor x position + DIM SHARED ANSI.save_y% ' Cursor y position + ANSI.save_x% = POS(0) + ANSI.save_y% = CSRLIN + + DIM SHARED ANSI.fg_color& ' Foreground color + DIM SHARED ANSI.bg_color& ' Background color + DIM SHARED ANSI.old_fg_color& ' Old Foreground color + DIM SHARED ANSI.old_bg_color& ' Old Background color + + ANSI.fg_color& = 7 + ANSI.bg_color& = 0 + ANSI.old_fg_color& = ANSI.fg_color& + ANSI.old_bg_color& = ANSI.bg_color& + $END IF + $IF GJ_LIB_STRINGS_INC_BI = UNDEFINED THEN + '' + ' QB64_GJ_LIB + ' GRYMMJACK'S STRINGS LIB + ' + ' Some commonly used functions that I missed in QB64 coming from PHP + ' + ' USAGE: + ' Insert '$INCLUDE:'path_to_GJ_LIB/STRINGS/STRINGS.BI' at the top of file + ' Insert '$INCLUDE:'path_to_GJ_LIB/STRINGS/STRINGS.BM' at the bottom of file + ' + ' @author Rick Christy + ' @uses STRINGS.BM + ' + $LET GJ_LIB_STRINGS_INC_BI = 1 + CONST GJ_LIB_MAX_STR_POSITIONS = 50 + + DECLARE LIBRARY + 'is an alphabet letter(isalpha(c) or isdigit(c)) + FUNCTION GJ_LIB_isalnum% ALIAS "isalnum" (BYVAL c AS INTEGER) + 'is letter (isupper(c) or islower(c)) + FUNCTION GJ_LIB_isalpha% ALIAS "isalpha" (BYVAL c AS INTEGER) + 'is a decimal digit + FUNCTION GJ_LIB_isdigit% ALIAS "isdigit" (BYVAL c AS INTEGER) + 'is a printing character other than space + FUNCTION GJ_LIB_isgraph% ALIAS "isgraph" (BYVAL c AS INTEGER) + 'is a lower-case letter + FUNCTION GJ_LIB_islower% ALIAS "islower" (BYVAL c AS INTEGER) + 'is printing character. ASCII: &H20 (" ") to &H7E (~) + FUNCTION GJ_LIB_isprint% ALIAS "isprint" (BYVAL c AS INTEGER) + 'is printing character other than space, letter, digit + FUNCTION GJ_LIB_ispunct% ALIAS "ispunct" (BYVAL c AS INTEGER) + 'is space, formfeed, newline, return, tab, vertical tab + FUNCTION GJ_LIB_isspace% ALIAS "isspace" (BYVAL c AS INTEGER) + 'is only space or tab + FUNCTION GJ_LIB_isblank% ALIAS "isblank" (BYVAL c AS INTEGER) + 'is upper-case letter + FUNCTION GJ_LIB_isupper% ALIAS "isupper" (BYVAL c AS INTEGER) + 'is a hexdecimal digit character(0 thru 9 or A thru F) + FUNCTION GJ_LIB_isxdigit% ALIAS "isxdigit" (BYVAL c AS INTEGER) + 'return lower-case equivalent + FUNCTION GJ_LIB.tolower% ALIAS "tolower" (BYVAL c AS INTEGER) + 'return upper-case equivalent + FUNCTION GJ_LIB.toupper% ALIAS "toupper" (BYVAL c AS INTEGER) + END DECLARE + $END IF + $IF GJ_LIB_PIPEPRINT_INC_BI = UNDEFINED THEN + '' + ' QB64_GJ_LIB + ' GRYMMJACK'S PIPEPRINT LIB + ' + ' Pipe (|) Print emulates Mystic BBS pipe parsing + ' + ' USAGE: + ' Insert '$INCLUDE:'path_to_GJ_LIB/PIPEPRINT/PIPEPRINT.BI' at the top of file + ' Insert '$INCLUDE:'path_to_GJ_LIB/PIPEPRINT/PIPEPRINT.BM' at the bottom of file + ' + ' @author Rick Christy + ' @uses _GJ_LIB_COMMON.BI + ' @uses PIPEPRINT.BM + ' @uses DICT/DICT.BI + ' @uses DUMP/DUMP.BI + ' @uses ANSI/ANSI.BI + ' @uses STRINGS/STRINGS.BI + ' + $LET GJ_LIB_PIPEPRINT_INC_BI = 1 + CONST MAX_PIPES = 50 + CONST MAX_PIPES_STRIP = 50 + + $IF GJ_LIB_INC_COMMON_BI = UNDEFINED THEN + '' + ' GRYMMJACK'S LIB FOR QB64 + ' + ' This file contains constants and variables common among the entire library. + ' + ' @author Rick Christy + ' + $IF GJLIB_COMMON_BI = UNDEFINED THEN + $LET GJ_LIB_INC_COMMON_BI = 1 + $END IF + + $IF FALSE = UNDEFINED AND TRUE = UNDEFINED THEN + $LET TRUE = TRUE + CONST FALSE = 0 : CONST TRUE = NOT FALSE + $END IF + $END IF + $IF GJ_LIB_DICT_INC_BI = UNDEFINED THEN + '' + ' QB64_GJ_LIB + ' GRYMMJACK'S DICT Object + ' + ' Simulates a dictionary object as found in other languages. + ' + ' USAGE: + ' Insert '$INCLUDE:'path_to_GJ_LIB/DICT/DICT.BI' at the top of file + ' Insert '$INCLUDE:'path_to_GJ_LIB/DICT/DICT.BM' at the bottom of file + ' + ' @author Rick Christy + ' @uses DICT.BM + ' + $LET GJ_LIB_DICT_INC_BI = 1 + + + + ' DICTIONARY type consists of keys and values and is intended for array use + TYPE DICTIONARY + key AS STRING + val AS STRING + END TYPE + $END IF + $IF GJ_LIB_DUMP_INC_BI = UNDEFINED THEN + '' + ' QB64_GJ_LIB + ' GRYMMJACK'S DUMP LIB + ' + ' USAGE: + ' Insert '$INCLUDE:'path_to_GJ_LIB/DUMP/DUMP.BI' at the top of file + ' Insert '$INCLUDE:'path_to_GJ_LIB/DUMP/DUMP.BM' at the bottom of file + ' + ' @author Rick Christy + ' @uses DUMP.BM + ' @uses DICT.BI + ' + $LET GJ_LIB_DUMP_INC_BI = 1 + $IF GJ_LIB_DICT_INC_BI = UNDEFINED THEN + '' + ' QB64_GJ_LIB + ' GRYMMJACK'S DICT Object + ' + ' Simulates a dictionary object as found in other languages. + ' + ' USAGE: + ' Insert '$INCLUDE:'path_to_GJ_LIB/DICT/DICT.BI' at the top of file + ' Insert '$INCLUDE:'path_to_GJ_LIB/DICT/DICT.BM' at the bottom of file + ' + ' @author Rick Christy + ' @uses DICT.BM + ' + $LET GJ_LIB_DICT_INC_BI = 1 + + + + ' DICTIONARY type consists of keys and values and is intended for array use + TYPE DICTIONARY + key AS STRING + val AS STRING + END TYPE + $END IF + + DIM SHARED GJ_LIB_NL AS STRING + $IF WIN THEN + GJ_LIB_NL$ = CHR$(13) + $ELSE + GJ_LIB_NL$ = CHR$(10) + $END IF + $END IF + $IF GJ_LIB_ANSI_INC_BI = UNDEFINED THEN + '' + ' QB64_GJ_LIB + ' GRYMMJACK'S ANSI LIB + ' + ' USAGE: + ' Insert '$INCLUDE:'path_to_GJ_LIB/ANSI/ANSI.BI' at the top of file + ' Insert '$INCLUDE:'path_to_GJ_LIB/ANSI/ANSI.BM' at the bottom of file + ' + ' @author Rick Christy + ' @uses _GJ_LIB_COMMON.BI + ' @uses ANSI.BM + ' + $LET GJ_LIB_ANSI_INC_BI = 1 + + $IF GJ_LIB_INC_COMMON_BI = UNDEFINED THEN + '' + ' GRYMMJACK'S LIB FOR QB64 + ' + ' This file contains constants and variables common among the entire library. + ' + ' @author Rick Christy + ' + $IF GJLIB_COMMON_BI = UNDEFINED THEN + $LET GJ_LIB_INC_COMMON_BI = 1 + $END IF + + $IF FALSE = UNDEFINED AND TRUE = UNDEFINED THEN + $LET TRUE = TRUE + CONST FALSE = 0 : CONST TRUE = NOT FALSE + $END IF + $END IF + + ' Emulate ANSI functionality using QB internals while outputing ANSI codes? + ' Default = FALSE + $IF ANSI_DEBUGGING = UNDEFINED THEN + CONST ANSI.ESC=27 + $ELSE + CONST ANSI.ESC=32 + $END IF + + DIM SHARED GJ_LIB_ANSI_EMU AS INTEGER + GJ_LIB_ANSI_EMU = FALSE + + DIM SHARED GJ_LIB_ANSI_OUTPUT AS INTEGER + GJ_LIB_ANSI_OUTPUT = TRUE + + DIM SHARED ANSI.x% ' Cursor x position + DIM SHARED ANSI.y% ' Cursor y position + ANSI.x% = POS(0) + ANSI.y% = CSRLIN + DIM SHARED ANSI.save_x% ' Cursor x position + DIM SHARED ANSI.save_y% ' Cursor y position + ANSI.save_x% = POS(0) + ANSI.save_y% = CSRLIN + + DIM SHARED ANSI.fg_color& ' Foreground color + DIM SHARED ANSI.bg_color& ' Background color + DIM SHARED ANSI.old_fg_color& ' Old Foreground color + DIM SHARED ANSI.old_bg_color& ' Old Background color + + ANSI.fg_color& = 7 + ANSI.bg_color& = 0 + ANSI.old_fg_color& = ANSI.fg_color& + ANSI.old_bg_color& = ANSI.bg_color& + $END IF + $IF GJ_LIB_STRINGS_INC_BI = UNDEFINED THEN + '' + ' QB64_GJ_LIB + ' GRYMMJACK'S STRINGS LIB + ' + ' Some commonly used functions that I missed in QB64 coming from PHP + ' + ' USAGE: + ' Insert '$INCLUDE:'path_to_GJ_LIB/STRINGS/STRINGS.BI' at the top of file + ' Insert '$INCLUDE:'path_to_GJ_LIB/STRINGS/STRINGS.BM' at the bottom of file + ' + ' @author Rick Christy + ' @uses STRINGS.BM + ' + $LET GJ_LIB_STRINGS_INC_BI = 1 + CONST GJ_LIB_MAX_STR_POSITIONS = 50 + + DECLARE LIBRARY + 'is an alphabet letter(isalpha(c) or isdigit(c)) + FUNCTION GJ_LIB_isalnum% ALIAS "isalnum" (BYVAL c AS INTEGER) + 'is letter (isupper(c) or islower(c)) + FUNCTION GJ_LIB_isalpha% ALIAS "isalpha" (BYVAL c AS INTEGER) + 'is a decimal digit + FUNCTION GJ_LIB_isdigit% ALIAS "isdigit" (BYVAL c AS INTEGER) + 'is a printing character other than space + FUNCTION GJ_LIB_isgraph% ALIAS "isgraph" (BYVAL c AS INTEGER) + 'is a lower-case letter + FUNCTION GJ_LIB_islower% ALIAS "islower" (BYVAL c AS INTEGER) + 'is printing character. ASCII: &H20 (" ") to &H7E (~) + FUNCTION GJ_LIB_isprint% ALIAS "isprint" (BYVAL c AS INTEGER) + 'is printing character other than space, letter, digit + FUNCTION GJ_LIB_ispunct% ALIAS "ispunct" (BYVAL c AS INTEGER) + 'is space, formfeed, newline, return, tab, vertical tab + FUNCTION GJ_LIB_isspace% ALIAS "isspace" (BYVAL c AS INTEGER) + 'is only space or tab + FUNCTION GJ_LIB_isblank% ALIAS "isblank" (BYVAL c AS INTEGER) + 'is upper-case letter + FUNCTION GJ_LIB_isupper% ALIAS "isupper" (BYVAL c AS INTEGER) + 'is a hexdecimal digit character(0 thru 9 or A thru F) + FUNCTION GJ_LIB_isxdigit% ALIAS "isxdigit" (BYVAL c AS INTEGER) + 'return lower-case equivalent + FUNCTION GJ_LIB.tolower% ALIAS "tolower" (BYVAL c AS INTEGER) + 'return upper-case equivalent + FUNCTION GJ_LIB.toupper% ALIAS "toupper" (BYVAL c AS INTEGER) + END DECLARE + $END IF + + ' Emulate ANSI functionality using QB internals while outputing ANSI codes? + ' Default = FALSE + DIM SHARED GJ_LIB_PIPEPRINT_ANSI_EMU AS INTEGER + GJ_LIB_PIPEPRINT_ANSI_EMU = TRUE + + '$DYNAMIC + DIM SHARED PIPES_POSITIONS(MAX_PIPES) AS INTEGER + DIM SHARED NUM_PIPES_FOUND AS INTEGER + NUM_PIPES_FOUND = 0 + + DIM SHARED PIPES_STRIP_POSITIONS(MAX_PIPES_STRIP) AS INTEGER + DIM SHARED NUM_PIPES_STRIP_FOUND AS INTEGER + NUM_PIPES_STRIP_FOUND = 0 + $END IF + $IF GJ_LIB_SYS_INC_BI = UNDEFINED THEN + '' + ' QB64_GJ_LIB + ' GRYMMJACK'S SYS LIB + ' + ' Contains misc. helpful utils/tools + ' + ' USAGE: + ' Insert '$INCLUDE:'path_to_GJ_LIB/SYS/SYS.BI' at the top of file + ' Insert '$INCLUDE:'path_to_GJ_LIB/SYS/SYS.BM' at the bottom of file + ' + ' @author Rick Christy + ' + $LET GJ_LIB_SYS_INC_BI = 1 + $END IF + $IF GJ_LIB_VECT2D_INC_BI = UNDEFINED THEN + '' + ' QB64_GJ_LIB + ' GRYMMJACK'S VECT2D LIB + ' + ' 2D Vector support for QB64 + ' + ' USAGE: + ' Insert '$INCLUDE:'path_to_GJ_LIB/VECT2D/VECT2D.BI' at the top of file + ' Insert '$INCLUDE:'path_to_GJ_LIB/VECT2D/VECT2D.BM' at the bottom of file + ' + ' @author Rick Christy + ' @author Evan Shortiss + ' @support William Barnes + ' + $LET GJ_LIB_VECT2D_INC_BI = 1 + + TYPE VECT2D + x AS SINGLE + y AS SINGLE + END TYPE + $END IF + '' + ' GRYMMJACK'S LIB FOR QB64 + ' + ' This is the master BM file, which includes the other BM files for the library. + ' + ' Enable and disable debugging by setting this variable to TRUE or FALSE: + ' GJ_LIB_DEBUGGING = FALSE + ' This is not a CONST and can be set as needed as often as you like. + ' + ' @author Rick Christy + ' @uses DICT/DICT.BM + ' @uses DUMP/DUMP.BM + ' @uses ANSI/ANSI.BM + ' @uses STRINGS/STRINGS.BM + ' @uses PIPEPRINT/PIPEPRINT.BM + ' @uses SYS/SYS.BM + ' + $LET GJ_LIB_INC_BM = 1 + + ' Unified includes to use everything in the library from single BM include + $IF GJ_LIB_ARR_BYTE_BAS = UNDEFINED THEN + '$DYNAMIC + $LET GJ_LIB_ARR_BYTE_BAS = 1 + + '' + ' Slice an array from source to destination starting at index and count slices + ' + ' @param _BYTE() source_arr%% to slice from + ' @param _BYTE() dest_arr%% to put slices into + ' @param INTEGER start_idx% starting index to use as slice range + ' @param INTEGER count% number of slices - if negative, backwards from index + ' + SUB ARR_BYTE.slice(source_arr%%(), dest_arr%%(), start_idx%, count%) + DIM AS LONG ub, lb, i, n + lb& = LBOUND(source_arr%%) : ub& = UBOUND(source_arr%%) + IF start_idx% < lb& OR start_idx% + count% > ub& THEN EXIT SUB ' out of range + IF ub& - lb& < count% THEN EXIT SUB ' too many and not enough + REDIM dest_arr(0 TO ABS(count%)) AS _BYTE + IF SGN(count%) = -1 THEN + IF ((start_idx% - 1) - ABS(count%)) < 0 THEN EXIT SUB ' out of range + n& = 0 + FOR i& = (start_idx% - 1) TO ((start_idx% - 1) - ABS(count%)) STEP -1 + dest_arr%%(n&) = source_arr%%(i&) + n& = n& + 1 + NEXT i& + ELSE + IF ((start_idx% + 1) + ABS(count%)) > (ub& - lb&) THEN EXIT SUB ' out of range + n& = 0 + FOR i& = start_idx% + 1 TO ((start_idx% + 1) + count%) STEP 1 + dest_arr%%(n&) = source_arr%%(i&) + n& = n& + 1 + NEXT i& + END IF + END SUB + + + '' + ' Push a byte onto the end of a _BYTE array + ' + ' @param _BYTE arr%%() array to push into + ' @param _BYTE value%% of byte to push + ' + SUB ARR_BYTE.push(arr%%(), value%%) + DIM AS LONG ub, lb + lb& = LBOUND(arr%%) : ub& = UBOUND(arr%%) + REDIM _PRESERVE arr(lb& TO (ub& + 1)) AS _BYTE + arr%%(ub& + 1) = value%% + END SUB + + + '' + ' Pop a byte from the end of a _BYTE array + ' + ' @param _BYTE arr%%() array to pop from + ' @param _BYTE var%% of byte to store popped byte + ' + SUB ARR_BYTE.pop(arr%%(), var%%) + DIM AS LONG ub, lb + lb& = LBOUND(arr%%) : ub& = UBOUND(arr%%) + var%% = arr%%(ub&) + REDIM _PRESERVE arr(lb& TO (ub& - 1)) AS _BYTE + END SUB + + + '' + ' Pop a byte from the beginning of a _BYTE array + ' + ' @param _BYTE arr%%() array to pop from + ' @param _BYTE var%% of byte to store popped byte + ' + SUB ARR_BYTE.shift(arr%%(), var%%) + DIM AS LONG ub, lb, i + lb& = LBOUND(arr%%) : ub& = UBOUND(arr%%) + var%% = arr%%(lb&) + FOR i& = lb& TO ub& - 1 + arr%%(i&) = arr%%(i& + 1) + NEXT i& + REDIM _PRESERVE arr(lb& + 1 TO ub&) AS _BYTE + END SUB + + + '' + ' Copy an array of BYTEs to another _BYTE array + ' + ' @param _BYTE ARRAY source_arr%%() source array to copy + ' @param _BYTE ARRAY dest_arr%%() dest array to copy into + ' + SUB ARR_BYTE.copy(source_arr%%(), dest_arr%%()) + DIM AS LONG ub, lb, i + lb& = LBOUND(source_arr%%) : ub& = UBOUND(source_arr%%) + REDIM dest_arr(lb& TO ub&) AS _BYTE + FOR i& = lb& TO ub& + dest_arr%%(i&) = source_arr%%(i&) + NEXT i& + END SUB + + + '' + ' Push a byte into the beginning of a _BYTE array + ' + ' @param _BYTE arr%%() array to push into + ' @param _BYTE value%% of byte to push + ' + SUB ARR_BYTE.unshift(arr%%(), value%%) + DIM AS LONG ub, lb, i + lb& = LBOUND(arr%%) : ub& = UBOUND(arr%%) + DIM work(lb& TO ub&) AS _BYTE + CALL ARR_BYTE.copy(arr%%(), work%%()) + REDIM _PRESERVE arr(lb& TO (ub& + 1)) AS _BYTE + FOR i& = lb& + 1 TO ub& + 1 + arr%%(i&) = work%%(i& - 1) + NEXT i& + arr%%(lb&) = value%% + END SUB + + + '' + ' Joins an array of BYTEs as a string + ' + ' @param _BYTE ARRAY arr%%() to get as a string + ' @param STRING s$ to store stringified array in + ' + SUB ARR_BYTE.join(arr%%(), s$) + DIM AS LONG ub, lb, i + s$ = "" + lb& = LBOUND(arr%%) : ub& = UBOUND(arr%%) + FOR i& = lb& TO ub& + s$ = s$ + _TRIM$(STR$(arr%%(i&))) + ", " + NEXT i& + ' remove trailing comma + s$ = MID$(s$, 1, (LEN(s$)-LEN(", "))) + END SUB + + + '' + ' Create a new _BYTE array using string of bytes seperated by commas + ' + ' @param _BYTE ARRAY arr%%() to store the bytes in + ' @param STRING s$ string of comma separated bytes + ' + SUB ARR_BYTE.new(arr%%(), s$) + DIM AS LONG i, count + DIM t AS STRING + count& = 0 + FOR i& = 1 TO LEN(s$) + IF ASC(s$, i&) = ASC(",") THEN count& = count& + 1 + NEXT i& + REDIM arr(0 TO count&) AS _BYTE + IF count& = 0 THEN + arr%%(0) = VAL(s$) + EXIT SUB + END IF + count& = 0 + FOR i& = 1 TO LEN(s$) + t$ = t$ + CHR$(ASC(s$, i&)) + IF ASC(s$, i&) = ASC(",") OR i& = LEN(s$) THEN + arr%%(count&) = VAL(t$) + count& = count& + 1 + t$ = "" + END IF + NEXT i& + END SUB + + + '' + ' Return the visually longest element of a _BYTE array + ' + ' @param _BYTE ARRAY arr%%() to check in + ' @return _BYTE value of visually longest element + ' + FUNCTION ARR_BYTE.longest%%(arr%%()) + DIM AS LONG lb, ub, i, res, lw + lb& = LBOUND(arr%%) : ub& = UBOUND(arr%%) + res& = 0 : lw& = 0 + FOR i& = lb& TO ub& + IF LEN(_TRIM$(STR$(arr%%(i&)))) > lw& THEN + lw& = LEN(_TRIM$(STR$(arr%%(i&)))) + res& = i& + END IF + NEXT i& + ARR_BYTE.longest%% = arr%%(res&) + END FUNCTION + + + '' + ' Perform some math on every element of a _BYTE array + ' + ' @param _BYTE ARRAY source_arr%%() to do math on + ' @param _BYTE ARRAY dest_arr%%() to store results in + ' @param STRING op$ one of: + ' and or xor shl shr + ' "+", "-", "*", "\", "&&", "||", "!!", "<<", ">>" + ' @param _BYTE value%% to use for operand + ' + SUB ARR_BYTE.math(source_arr%%(), dest_arr%%(), op$, value%%) + DIM AS LONG lb, ub, i + lb& = LBOUND(source_arr%%) : ub& = UBOUND(source_arr%%) + REDIM dest_arr(lb& TO ub&) AS _BYTE + FOR i& = lb& TO ub& + SELECT CASE op$ + CASE "+": + dest_arr%%(i&) = source_arr%%(i&) + value%% + CASE "-": + dest_arr%%(i&) = source_arr%%(i&) - value%% + CASE "*": + dest_arr%%(i&) = source_arr%%(i&) * value%% + CASE "\": + IF value%% > 0 THEN + dest_arr%%(i&) = source_arr%%(i&) \ value%% + END IF + CASE "&&": + dest_arr%%(i&) = source_arr%%(i&) AND value%% + CASE "||": + dest_arr%%(i&) = source_arr%%(i&) OR value%% + CASE "!!": + dest_arr%%(i&) = source_arr%%(i&) XOR value%% + CASE "<<": + dest_arr%%(i&) = _SHL(source_arr%%(i&), value%%) + CASE ">>": + dest_arr%%(i&) = _SHR(source_arr%%(i&), value%%) + END SELECT + NEXT i& + END SUB + + + '' + ' Return the minimum element value in _BYTE array + ' + ' @param _BYTE ARRAY arr%%() to check in + ' @return _BYTE minimum value found + ' + FUNCTION ARR_BYTE.min%%(arr%%()) + DIM AS LONG lb, ub, i + DIM AS _BYTE s + lb& = LBOUND(arr%%) : ub& = UBOUND(arr%%) + s%% = 127 + FOR i& = lb& TO ub& + IF arr%%(i&) < s%% THEN + s%% = arr%%(i&) + END IF + NEXT i& + ARR_BYTE.min%% = s%% + END FUNCTION + + + '' + ' Return the maximum element value in _BYTE array + ' + ' @param _BYTE ARRAY arr%%() to check in + ' @return _BYTE maximum value found + ' + FUNCTION ARR_BYTE.max%%(arr%%()) + DIM AS LONG lb, ub, i + DIM AS _BYTE s + lb& = LBOUND(arr%%) : ub& = UBOUND(arr%%) + s%% = 0 + FOR i& = lb& TO ub& + IF arr%%(i&) > s%% THEN + s%% = arr%%(i&) + END IF + NEXT i& + ARR_BYTE.max%% = s%% + END FUNCTION + + + '' + ' Return the visually shortest element of a _BYTE array + ' + ' @param _BYTE ARRAY arr%%() to check in + ' @return _BYTE value of visually shortest element + ' + FUNCTION ARR_BYTE.shortest%%(arr%%()) + DIM AS LONG lb, ub, i, res, sw + lb& = LBOUND(arr%%) : ub& = UBOUND(arr%%) + res& = 0 : sw& = 0 + FOR i& = lb& TO ub& + IF LEN(_TRIM$(STR$(arr%%(i&)))) < sw& THEN + sw& = LEN(_TRIM$(STR$(arr%%(i&)))) + res& = i& + END IF + NEXT i& + ARR_BYTE.shortest%% = arr%%(res&) + END FUNCTION + + + '' + ' Return the first element of a _BYTE array + ' + ' @param _BYTE ARRAY arr%%() to check in + ' @return _BYTE value of first element + ' + FUNCTION ARR_BYTE.first%%(arr%%()) + ARR_BYTE.first%% = arr%%(LBOUND(arr%%)) + END FUNCTION + + + '' + ' Return the last element of a _BYTE array + ' + ' @param _BYTE ARRAY arr%%() to check in + ' @return _BYTE value of last element + ' + FUNCTION ARR_BYTE.last%%(arr%%()) + ARR_BYTE.last%% = arr%%(UBOUND(arr%%)) + END FUNCTION + + + '' + ' Return every nth array element of a _BYTE array + ' + ' @param _BYTE ARRAY source_arr%%() to get from + ' @param _BYTE ARRAY dest_arr%%() to store in + ' @param INTEGER nth% element + ' + SUB ARR_BYTE.nth(source_arr%%(), dest_arr%%(), nth%) + DIM AS LONG lb, ub, i, n + lb& = LBOUND(source_arr%%) : ub& = UBOUND(source_arr%%) + n& = (ub& - lb&) \ nth% + REDIM dest_arr(n&) AS _BYTE + n& = 0 + FOR i& = lb& to ub& + IF i& MOD nth% = 0 THEN + dest_arr%%(n&) = source_arr%%(i&) + n& = n& + 1 + END IF + NEXT i& + END SUB + + + '' + ' Checks if value exists in _BYTE array + ' + ' @param _BYTE ARRAY arr%%() to check in + ' @param _BYTE value%% value to check for + ' @return INTEGER -1 if found or 0 if not found + ' + FUNCTION ARR_BYTE.in%(arr%%(), value%%) + DIM AS LONG lb, ub, i + lb& = LBOUND(arr%%) : ub& = UBOUND(arr%%) + FOR i& = lb& TO ub& + IF arr%%(i&) = value%% THEN + ARR_BYTE.in% = -1 + EXIT FUNCTION + END IF + NEXT i& + ARR_BYTE.in% = 0 + END FUNCTION + + + '' + ' Checks if value exists in _BYTE array and returns index if found + ' + ' @param _BYTE ARRAY arr%%() to check in + ' @param _BYTE value%% value to check for + ' @return INTEGER index of element if found or -1 if not found + ' + FUNCTION ARR_BYTE.find%(arr%%(), value%%) + DIM AS LONG lb, ub, i + lb& = LBOUND(arr%%) : ub& = UBOUND(arr%%) + FOR i& = lb& TO ub& + IF arr%%(i&) = value%% THEN + ARR_BYTE.find% = i& + EXIT FUNCTION + END IF + NEXT i& + ARR_BYTE.find% = -1 + END FUNCTION + + + '' + ' Return the number of elements in a _BYTE array + ' + ' @param _BYTE ARRAY arr%%() to count + ' @return INTEGER number of elements + ' + FUNCTION ARR_BYTE.count&(arr%%()) + ARR_BYTE.count& = UBOUND(arr%%) - LBOUND(arr%%) + END FUNCTION + + + '' + ' Return the size of a _BYTE array + ' + ' @param _BYTE ARRAY arr%%() to get size of + ' @return LONG size in bytes + ' + FUNCTION ARR_BYTE.size&(arr%%()) + ARR_BYTE.size& = LEN(arr%%()) + END FUNCTION + + + '' + ' Reverses the elements of a _BYTE array + ' + ' @param _BYTE ARRAY source_arr%%() to reverse + ' @param _BYTE ARRAY dest_arr%%() to store reversed array in + ' + SUB ARR_BYTE.reverse(source_arr%%(), dest_arr%%()) + DIM AS LONG lb, ub, i, n + lb& = LBOUND(source_arr%%) : ub& = UBOUND(source_arr%%) + REDIM dest_arr(0 TO (ub& - lb&)) AS _BYTE + n& = 0 + FOR i& = ub& TO lb& STEP -1 + dest_arr%%(n&) = source_arr%%(i&) + n& = n& + 1 + NEXT i& + END SUB + + + '' + ' Returns a random byte from a _BYTE array + ' + ' @param _BYTE ARRAY arr%%() array to get random element from + ' @return _BYTE random element + ' + FUNCTION ARR_BYTE.random%%(arr%%()) + DIM AS LONG lb, ub + lb& = LBOUND(arr%%) : ub& = UBOUND(arr%%) + RANDOMIZE TIMER + ARR_BYTE.random%% = arr%%(INT(RND * (ub& - lb&)) + 1) + END FUNCTION + + + '' + ' Returns the sum of all elements in a _BYTE array + ' + ' @param _BYTE ARRAY arr%%() array to get some for + ' @return LONG sum of all elements + ' + FUNCTION ARR_BYTE.sum&(arr%%()) + DIM AS LONG lb, ub, i + DIM sum AS LONG + lb& = LBOUND(arr%%) : ub& = UBOUND(arr%%) + FOR i& = lb& TO ub& + sum& = sum& + arr%%(i&) + NEXT i& + ARR_BYTE.sum& = sum& + END FUNCTION + + + '' + ' Returns the average value of elements in a _BYTE array + ' + ' @param _BYTE ARRAY arr%%() array to get average for + ' @return LONG average of elements + ' + FUNCTION ARR_BYTE.avg&(arr%%()) + DIM AS LONG lb, ub, i + DIM sum AS LONG + lb& = LBOUND(arr%%) : ub& = UBOUND(arr%%) + FOR i& = lb& TO ub& + sum& = sum& + arr%%(i&) + NEXT i& + ARR_BYTE.avg& = sum& / (ub& - lb&) + END FUNCTION + + + '' + ' Shuffle the elements of a _BYTE array + ' + ' @param _BYTE ARRAY source_arr%%() to shuffle + ' @param _BYTE ARRAY dest_arr%%() to store shuffled array in + ' + SUB ARR_BYTE.shuffle(source_arr%%(), dest_arr%%()) + DIM AS LONG lb, ub, i, count + lb& = LBOUND(source_arr%%) : ub& = UBOUND(source_arr%%) + count& = ub& - lb& + REDIM dest_arr(0 TO count&) AS _BYTE + CALL ARR_BYTE.copy(source_arr%%(), dest_arr%%()) + RANDOMIZE TIMER + FOR i& = 0 TO count& + SWAP dest_arr%%(i&), dest_arr%%(lb& + RND * (ub& - lb&)) + NEXT i& + END SUB + + + '' + ' Makes a _BYTE array contain only unique values + ' + ' @param _BYTE ARRAY source_arr%%() array to get uniques for + ' @param _BYTE ARRAY dest_arr%%() array to store uniques in + ' + SUB ARR_BYTE.unique(source_arr%%(), dest_arr%%()) + DIM AS LONG lb, ub, i + DIM tmp AS _BYTE + lb& = LBOUND(source_arr%%) : ub& = UBOUND(source_arr%%) + DIM work_arr(0) AS _BYTE + FOR i& = lb& TO ub& + IF NOT ARR_BYTE.in%(work_arr%%(), source_arr%%(i&)) THEN + CALL ARR_BYTE.push(work_arr%%(), source_arr%%(i&)) + END IF + NEXT i& + CALL ARR_BYTE.shift(work_arr%%(), tmp%%) + CALL ARR_BYTE.copy(work_arr%%(), dest_arr%%()) + END SUB + + + '' + ' Filters a _BYTE array to only elements greater than value + ' + ' @param _BYTE ARRAY source_arr%%() array to work on + ' @param _BYTE ARRAY dest_arr%%() array to store in + ' @param _BYTE value%% to be greater than to be returned + ' + SUB ARR_BYTE.gt(source_arr%%(), dest_arr%%(), value%%) + DIM AS LONG lb, ub, i + DIM tmp AS _BYTE + lb& = LBOUND(source_arr%%) : ub& = UBOUND(source_arr%%) + DIM work_arr(0) AS _BYTE + FOR i& = lb& TO ub& + IF source_arr%%(i&) > value%% THEN + CALL ARR_BYTE.push(work_arr%%(), source_arr%%(i&)) + END IF + NEXT i& + CALL ARR_BYTE.shift(work_arr%%(), tmp%%) + CALL ARR_BYTE.copy(work_arr%%(), dest_arr%%()) + END SUB + + + '' + ' Filters a _BYTE array to only elements greater than or equal to value + ' + ' @param _BYTE ARRAY source_arr%%() array to work on + ' @param _BYTE ARRAY dest_arr%%() array to store in + ' @param _BYTE value%% to be greater than or equal to be returned + ' + SUB ARR_BYTE.gte(source_arr%%(), dest_arr%%(), value%%) + DIM AS LONG lb, ub, i + DIM tmp AS _BYTE + lb& = LBOUND(source_arr%%) : ub& = UBOUND(source_arr%%) + DIM work_arr(0) AS _BYTE + FOR i& = lb& TO ub& + IF source_arr%%(i&) >= value%% THEN + CALL ARR_BYTE.push(work_arr%%(), source_arr%%(i&)) + END IF + NEXT i& + CALL ARR_BYTE.shift(work_arr%%(), tmp%%) + CALL ARR_BYTE.copy(work_arr%%(), dest_arr%%()) + END SUB + + + '' + ' Filters a _BYTE array to only elements less than value + ' + ' @param _BYTE ARRAY source_arr%%() array to work on + ' @param _BYTE ARRAY dest_arr%%() array to store in + ' @param _BYTE value%% to be less than to be returned + ' + SUB ARR_BYTE.lt(source_arr%%(), dest_arr%%(), value%%) + DIM AS LONG lb, ub, i + DIM tmp AS _BYTE + lb& = LBOUND(source_arr%%) : ub& = UBOUND(source_arr%%) + DIM work_arr(0) AS _BYTE + FOR i& = lb& TO ub& + IF source_arr%%(i&) < value%% THEN + CALL ARR_BYTE.push(work_arr%%(), source_arr%%(i&)) + END IF + NEXT i& + CALL ARR_BYTE.shift(work_arr%%(), tmp%%) + CALL ARR_BYTE.copy(work_arr%%(), dest_arr%%()) + END SUB + + + '' + ' Filters a _BYTE array to only elements less than or equal to value + ' + ' @param _BYTE ARRAY source_arr%%() array to work on + ' @param _BYTE ARRAY dest_arr%%() array to store in + ' @param _BYTE value%% to be less than or equal to be returned + ' + SUB ARR_BYTE.lte(source_arr%%(), dest_arr%%(), value%%) + DIM AS LONG lb, ub, i + DIM tmp AS _BYTE + lb& = LBOUND(source_arr%%) : ub& = UBOUND(source_arr%%) + DIM work_arr(0) AS _BYTE + FOR i& = lb& TO ub& + IF source_arr%%(i&) <= value%% THEN + CALL ARR_BYTE.push(work_arr%%(), source_arr%%(i&)) + END IF + NEXT i& + CALL ARR_BYTE.shift(work_arr%%(), tmp%%) + CALL ARR_BYTE.copy(work_arr%%(), dest_arr%%()) + END SUB + + + '' + ' Finds and replaces values across all elements in a _BYTE ARRAY + ' + ' @param _BYTE ARRAY arr%%() to check in + ' @param _BYTE find%% value to find + ' @param _BYTE replace%% value to replace with if found + ' + SUB ARR_BYTE.replace(arr%%(), find%%, replace%%) + DIM AS LONG lb, ub, i + lb& = LBOUND(arr%%) : ub& = UBOUND(arr%%) + FOR i& = lb& TO ub& + IF arr%%(i&) = find%% THEN + arr%%(i&) = replace%% + END IF + NEXT i& + END SUB + + + '' + ' Inserts a new element into _BYTE array after index + ' + ' @param _BYTE ARRAY arr%%() array to work on + ' @param _BYTE value%% to insert + ' @param INTEGER index% of element to insert at + ' + SUB ARR_BYTE.insert(arr%%(), value%%, index%) + DIM AS LONG lb, ub, i + DIM tmp AS _BYTE + DIM work_arr(0) AS _BYTE + lb& = LBOUND(arr%%) : ub& = UBOUND(arr%%) + IF index% >= lb& AND index% <= ub& THEN + ' build new array up to index + FOR i& = lb& TO index% + CALL ARR_BYTE.push(work_arr%%(), arr%%(i&)) + NEXT i& + ' insert new element + CALL ARR_BYTE.push(work_arr%%(), value%%) + ' finish building array from index + 1 + FOR i& = index% + 1 TO ub& + CALL ARR_BYTE.push(work_arr%%(), arr%%(i&)) + NEXT i& + CALL ARR_BYTE.shift(work_arr%%(), tmp%%) + CALL ARR_BYTE.copy(work_arr%%(), arr%%()) + END IF + END SUB + + + '' + ' Removes element from a _BYTE array by element index + ' + ' @param _BYTE ARRAY arr%%() array to work on + ' @param INTEGER index% of element to remove + ' + SUB ARR_BYTE.remove(arr%%(), index%) + DIM AS LONG lb, ub, i + DIM tmp AS _BYTE + DIM work_arr(0) AS _BYTE + lb& = LBOUND(arr%%) : ub& = UBOUND(arr%%) + IF index% >= lb& AND index% <= ub& THEN + ' build new array up to index + FOR i& = lb& TO index% - 1 + CALL ARR_BYTE.push(work_arr%%(), arr%%(i&)) + NEXT i& + ' skip elements + FOR i& = index% + 1 TO ub& + CALL ARR_BYTE.push(work_arr%%(), arr%%(i&)) + NEXT i& + CALL ARR_BYTE.shift(work_arr%%(), tmp%%) + CALL ARR_BYTE.copy(work_arr%%(), arr%%()) + END IF + END SUB + + + '' + ' Filters a _BYTE array to only elements that have odd values + ' + ' @param _BYTE ARRAY source_arr%%() array to work on + ' @param _BYTE ARRAY dest_arr%%() array to store in + ' + SUB ARR_BYTE.odd(source_arr%%(), dest_arr%%()) + DIM AS LONG lb, ub, i + DIM tmp AS _BYTE + lb& = LBOUND(source_arr%%) : ub& = UBOUND(source_arr%%) + DIM work_arr(0) AS _BYTE + FOR i& = lb& TO ub& + IF source_arr%%(i&) MOD 2 <> 0 THEN + CALL ARR_BYTE.push(work_arr%%(), source_arr%%(i&)) + END IF + NEXT i& + CALL ARR_BYTE.shift(work_arr%%(), tmp%%) + CALL ARR_BYTE.copy(work_arr%%(), dest_arr%%()) + END SUB + + + '' + ' Filters a _BYTE array to only elements that have even values + ' + ' @param _BYTE ARRAY source_arr%%() array to work on + ' @param _BYTE ARRAY dest_arr%%() array to store in + ' + SUB ARR_BYTE.even(source_arr%%(), dest_arr%%()) + DIM AS LONG lb, ub, i + DIM tmp AS _BYTE + lb& = LBOUND(source_arr%%) : ub& = UBOUND(source_arr%%) + DIM work_arr(0) AS _BYTE + FOR i& = lb& TO ub& + IF source_arr%%(i&) MOD 2 = 0 THEN + CALL ARR_BYTE.push(work_arr%%(), source_arr%%(i&)) + END IF + NEXT i& + CALL ARR_BYTE.shift(work_arr%%(), tmp%%) + CALL ARR_BYTE.copy(work_arr%%(), dest_arr%%()) + END SUB + + + '' + ' Filters a _BYTE array to only elements that have values evenly divisible by divisor + ' + ' @param _BYTE ARRAY source_arr%%() array to work on + ' @param _BYTE ARRAY dest_arr%%() array to store in + ' @param _BYTE divisor%% for modulo + ' + SUB ARR_BYTE.mod(source_arr%%(), dest_arr%%(), divisor%%) + DIM AS LONG lb, ub, i + DIM tmp AS _BYTE + lb& = LBOUND(source_arr%%) : ub& = UBOUND(source_arr%%) + DIM work_arr(0) AS _BYTE + FOR i& = lb& TO ub& + IF source_arr%%(i&) MOD divisor%% = 0 THEN + CALL ARR_BYTE.push(work_arr%%(), source_arr%%(i&)) + END IF + NEXT i& + CALL ARR_BYTE.shift(work_arr%%(), tmp%%) + CALL ARR_BYTE.copy(work_arr%%(), dest_arr%%()) + END SUB + + + '' + ' Filters a _BYTE array to only elements between min and max + ' + ' @param _BYTE ARRAY source_arr%%() array to work on + ' @param _BYTE ARRAY dest_arr%%() array to store in + ' @param _BYTE min%% to be greater than or equal to be returned + ' @param _BYTE max%% to be less than or equal to be returned + ' + SUB ARR_BYTE.between(source_arr%%(), dest_arr%%(), min%%, max%%) + DIM AS LONG lb, ub, i + DIM tmp AS _BYTE + lb& = LBOUND(source_arr%%) : ub& = UBOUND(source_arr%%) + DIM work_arr(0) AS _BYTE + FOR i& = lb& TO ub& + IF source_arr%%(i&) >= min%% _ + AND source_arr%%(i&) <= max%% THEN + CALL ARR_BYTE.push(work_arr%%(), source_arr%%(i&)) + END IF + NEXT i& + CALL ARR_BYTE.shift(work_arr%%(), tmp%%) + CALL ARR_BYTE.copy(work_arr%%(), dest_arr%%()) + END SUB + + + '' + ' Sorts _BYTE array in ascending order + ' + ' @param _BYTE ARRAY source_arr%%() array to sort + ' @param _BYTE ARRAY dest_arr%%() array to store sorted in + ' + SUB ARR_BYTE.sort(source_arr%%(), dest_arr%%()) + DIM AS LONG lb, ub, count + lb& = LBOUND(source_arr%%) : ub& = UBOUND(source_arr%%) + count& = ub& - lb& + REDIM dest_arr(0 TO count&) AS _BYTE + CALL ARR_BYTE.copy(source_arr%%(), dest_arr%%()) + CALL ARR_BYTE.quicksort(dest_arr%%(), lb&, ub&, 0) + END SUB + + + '' + ' Sorts _BYTE array in descending order + ' + ' @param _BYTE ARRAY source_arr%%() array to sort + ' @param _BYTE ARRAY dest_arr%%() array to store sorted in + ' + SUB ARR_BYTE.rsort(source_arr%%(), dest_arr%%()) + DIM AS LONG lb, ub, count + lb& = LBOUND(source_arr%%) : ub& = UBOUND(source_arr%%) + count& = ub& - lb& + REDIM dest_arr(0 TO count&) AS _BYTE + CALL ARR_BYTE.copy(source_arr%%(), dest_arr%%()) + CALL ARR_BYTE.quicksort(dest_arr%%(), lb&, ub&, 1) + END SUB + + + '' + ' Quicksort array with pivot algorithm by logiclrd + ' + ' @link https://www.tek-tips.com/faqs.cfm?fid=336 + ' @param _BYTE ARRAY array%%() to sort + ' @param INTEGER start% of range to sort + ' @param INTEGER finish% range of sort + ' @param INTEGER order% to sort by (0 = asc / 1 = desc) + ' + SUB ARR_BYTE.quicksort(arr%%(), start%, finish%, order%) + DIM i AS LONG + DIM pivot AS INTEGER + DIM pivotvalue AS _BYTE + + 'first, partition the array + pivot% = start% + pivotvalue%% = arr%%(pivot%) + FOR i& = start% + 1 TO finish% + IF order% = 0 THEN ' ascending order + IF arr%%(i&) < pivotvalue%% THEN + arr%%(pivot%) = arr%%(i&) + arr%%(i&) = arr%%(pivot% + 1) + arr%%(pivot% + 1) = pivotvalue%% + pivot% = pivot% + 1 + END IF + ELSEIF order% = 1 THEN ' descending order + IF arr%%(i&) > pivotvalue%% THEN + arr%%(pivot%) = arr%%(i&) + arr%%(i&) = arr%%(pivot% + 1) + arr%%(pivot% + 1) = pivotvalue%% + pivot% = pivot% + 1 + END IF + END IF + NEXT i& + + 'then, sort the subarrays to each side of the pivot + IF pivot% - start% >= 2 THEN + CALL ARR_BYTE.quicksort(arr%%(), start%, pivot% - 1, order%) + END IF + IF finish% - pivot% >= 2 THEN + CALL ARR_BYTE.quicksort(arr%%(), pivot% + 1, finish%, order%) + END IF + END SUB + $END IF + $IF GJ_LIB_ARR_DBL_BAS = UNDEFINED THEN + '$DYNAMIC + $LET GJ_LIB_ARR_DBL_BAS = 1 + + '' + ' Slice an array from source to destination starting at index and count slices + ' + ' @param DOUBLE() source_arr# to slice from + ' @param DOUBLE() dest_arr# to put slices into + ' @param INTEGER start_idx% starting index to use as slice range + ' @param INTEGER count% number of slices - if negative, backwards from index + ' + SUB ARR_DBL.slice(source_arr#(), dest_arr#(), start_idx%, count%) + DIM AS LONG ub, lb, i, n + lb& = LBOUND(source_arr#) : ub& = UBOUND(source_arr#) + IF start_idx% < lb& OR start_idx% + count% > ub& THEN EXIT SUB ' out of range + IF ub& - lb& < count% THEN EXIT SUB ' too many and not enough + REDIM dest_arr(0 TO ABS(count%)) AS DOUBLE + IF SGN(count%) = -1 THEN + IF ((start_idx% - 1) - ABS(count%)) < 0 THEN EXIT SUB ' out of range + n& = 0 + FOR i& = (start_idx% - 1) TO ((start_idx% - 1) - ABS(count%)) STEP -1 + dest_arr#(n&) = source_arr#(i&) + n& = n& + 1 + NEXT i& + ELSE + IF ((start_idx% + 1) + ABS(count%)) > (ub& - lb&) THEN EXIT SUB ' out of range + n& = 0 + FOR i& = start_idx% + 1 TO ((start_idx% + 1) + count%) STEP 1 + dest_arr#(n&) = source_arr#(i&) + n& = n& + 1 + NEXT i& + END IF + END SUB + + + '' + ' Push a dbl onto the end of a DOUBLE array + ' + ' @param DOUBLE arr#() array to push into + ' @param DOUBLE value# of byte to push + ' + SUB ARR_DBL.push(arr#(), value#) + DIM AS LONG ub, lb + lb& = LBOUND(arr#) : ub& = UBOUND(arr#) + REDIM _PRESERVE arr(lb& TO (ub& + 1)) AS DOUBLE + arr#(ub& + 1) = value# + END SUB + + + '' + ' Pop a dbl from the end of a DOUBLE array + ' + ' @param DOUBLE arr#() array to pop from + ' @param DOUBLE var# of dbl to store popped dbl + ' + SUB ARR_DBL.pop(arr#(), var#) + DIM AS LONG ub, lb + lb& = LBOUND(arr#) : ub& = UBOUND(arr#) + var# = arr#(ub&) + REDIM _PRESERVE arr(lb& TO (ub& - 1)) AS DOUBLE + END SUB + + + '' + ' Pop a dbl from the beginning of a DOUBLE array + ' + ' @param DOUBLE arr#() array to pop from + ' @param DOUBLE var# of dbl to store popped dbl + ' + SUB ARR_DBL.shift(arr#(), var#) + DIM AS LONG ub, lb, i + lb& = LBOUND(arr#) : ub& = UBOUND(arr#) + var# = arr#(lb&) + FOR i& = lb& TO ub& - 1 + arr#(i&) = arr#(i& + 1) + NEXT i& + REDIM _PRESERVE arr(lb& + 1 TO ub&) AS DOUBLE + END SUB + + + '' + ' Copy an array of DBLs to another DOUBLE array + ' + ' @param DOUBLE ARRAY source_arr#() source array to copy + ' @param DOUBLE ARRAY dest_arr#() dest array to copy into + ' + SUB ARR_DBL.copy(source_arr#(), dest_arr#()) + DIM AS LONG ub, lb, i + lb& = LBOUND(source_arr#) : ub& = UBOUND(source_arr#) + REDIM dest_arr(lb& TO ub&) AS DOUBLE + FOR i& = lb& TO ub& + dest_arr#(i&) = source_arr#(i&) + NEXT i& + END SUB + + + '' + ' Push a dbl into the beginning of a DOUBLE array + ' + ' @param DOUBLE arr#() array to push into + ' @param DOUBLE value# of dbl to push + ' + SUB ARR_DBL.unshift(arr#(), value#) + DIM AS LONG ub, lb, i + lb& = LBOUND(arr#) : ub& = UBOUND(arr#) + DIM work(lb& TO ub&) AS DOUBLE + CALL ARR_DBL.copy(arr#(), work#()) + REDIM _PRESERVE arr(lb& TO (ub& + 1)) AS DOUBLE + FOR i& = lb& + 1 TO ub& + 1 + arr#(i&) = work#(i& - 1) + NEXT i& + arr#(lb&) = value# + END SUB + + + '' + ' Joins an array of DBLs as a string + ' + ' @param DOUBLE ARRAY arr#() to get as a string + ' @param STRING s$ to store stringified array in + ' + SUB ARR_DBL.join(arr#(), s$) + DIM AS LONG ub, lb, i + s$ = "" + lb& = LBOUND(arr#) : ub& = UBOUND(arr#) + FOR i& = lb& TO ub& + s$ = s$ + _TRIM$(STR$(arr#(i&))) + ", " + NEXT i& + ' remove trailing comma + s$ = MID$(s$, 1, (LEN(s$)-LEN(", "))) + END SUB + + + '' + ' Create a new DOUBLE array using string of dbls seperated by commas + ' + ' @param DOUBLE ARRAY arr#() to store the dbls in + ' @param STRING s$ string of comma separated dbls + ' + SUB ARR_DBL.new(arr#(), s$) + DIM AS LONG i, count + DIM t AS STRING + count& = 0 + FOR i& = 1 TO LEN(s$) + IF ASC(s$, i&) = ASC(",") THEN count& = count& + 1 + NEXT i& + REDIM arr(0 TO count&) AS DOUBLE + IF count& = 0 THEN + arr#(0) = VAL(s$) + EXIT SUB + END IF + count& = 0 + FOR i& = 1 TO LEN(s$) + t$ = t$ + CHR$(ASC(s$, i&)) + IF ASC(s$, i&) = ASC(",") OR i& = LEN(s$) THEN + arr#(count&) = VAL(t$) + count& = count& + 1 + t$ = "" + END IF + NEXT i& + END SUB + + + '' + ' Return the visually longest element of a DOUBLE array + ' + ' @param DOUBLE ARRAY arr#() to check in + ' @return DOUBLE value of visually longest element + ' + FUNCTION ARR_DBL.longest#(arr#()) + DIM AS LONG lb, ub, i, res, lw + lb& = LBOUND(arr#) : ub& = UBOUND(arr#) + res& = 0 : lw& = 0 + FOR i& = lb& TO ub& + IF LEN(_TRIM$(STR$(arr#(i&)))) > lw& THEN + lw& = LEN(_TRIM$(STR$(arr#(i&)))) + res& = i& + END IF + NEXT i& + ARR_DBL.longest# = arr#(res&) + END FUNCTION + + + '' + ' Perform some math on every element of a DOUBLE array + ' + ' @param DOUBLE ARRAY source_arr#() to do math on + ' @param DOUBLE ARRAY dest_arr#() to store results in + ' @param STRING op$ one of: + ' and or xor shl shr + ' "+", "-", "*", "\", "&&", "||", "!!", "<<", ">>" + ' @param DOUBLE value# to use for operand + ' + SUB ARR_DBL.math(source_arr#(), dest_arr#(), op$, value#) + DIM AS LONG lb, ub, i + lb& = LBOUND(source_arr#) : ub& = UBOUND(source_arr#) + REDIM dest_arr(lb& TO ub&) AS DOUBLE + FOR i& = lb& TO ub& + SELECT CASE op$ + CASE "+": + dest_arr#(i&) = source_arr#(i&) + value# + CASE "-": + dest_arr#(i&) = source_arr#(i&) - value# + CASE "*": + dest_arr#(i&) = source_arr#(i&) * value# + CASE "\": + IF value# > 0 THEN + dest_arr#(i&) = source_arr#(i&) \ value# + END IF + CASE "&&": + dest_arr#(i&) = source_arr#(i&) AND value# + CASE "||": + dest_arr#(i&) = source_arr#(i&) OR value# + CASE "!!": + dest_arr#(i&) = source_arr#(i&) XOR value# + CASE "<<": + dest_arr#(i&) = _SHL(source_arr#(i&), value#) + CASE ">>": + dest_arr#(i&) = _SHR(source_arr#(i&), value#) + END SELECT + NEXT i& + END SUB + + + '' + ' Return the minimum element value in DOUBLE array + ' + ' @param DOUBLE ARRAY arr#() to check in + ' @return DOUBLE minimum value found + ' + FUNCTION ARR_DBL.min#(arr#()) + DIM AS LONG lb, ub, i + DIM AS DOUBLE s + lb& = LBOUND(arr#) : ub& = UBOUND(arr#) + s# = 127 + FOR i& = lb& TO ub& + IF arr#(i&) < s# THEN + s# = arr#(i&) + END IF + NEXT i& + ARR_DBL.min# = s# + END FUNCTION + + + '' + ' Return the maximum element value in DOUBLE array + ' + ' @param DOUBLE ARRAY arr#() to check in + ' @return DOUBLE maximum value found + ' + FUNCTION ARR_DBL.max#(arr#()) + DIM AS LONG lb, ub, i + DIM AS DOUBLE s + lb& = LBOUND(arr#) : ub& = UBOUND(arr#) + s# = 0 + FOR i& = lb& TO ub& + IF arr#(i&) > s# THEN + s# = arr#(i&) + END IF + NEXT i& + ARR_DBL.max# = s# + END FUNCTION + + + '' + ' Return the visually shortest element of a DOUBLE array + ' + ' @param DOUBLE ARRAY arr#() to check in + ' @return DOUBLE value of visually shortest element + ' + FUNCTION ARR_DBL.shortest#(arr#()) + DIM AS LONG lb, ub, i, res, sw + lb& = LBOUND(arr#) : ub& = UBOUND(arr#) + res& = 0 : sw& = 0 + FOR i& = lb& TO ub& + IF LEN(_TRIM$(STR$(arr#(i&)))) < sw& THEN + sw& = LEN(_TRIM$(STR$(arr#(i&)))) + res& = i& + END IF + NEXT i& + ARR_DBL.shortest# = arr#(res&) + END FUNCTION + + + '' + ' Return the first element of a DOUBLE array + ' + ' @param DOUBLE ARRAY arr#() to check in + ' @return DOUBLE value of first element + ' + FUNCTION ARR_DBL.first#(arr#()) + ARR_DBL.first# = arr#(LBOUND(arr#)) + END FUNCTION + + + '' + ' Return the last element of a DOUBLE array + ' + ' @param DOUBLE ARRAY arr#() to check in + ' @return DOUBLE value of last element + ' + FUNCTION ARR_DBL.last#(arr#()) + ARR_DBL.last# = arr#(UBOUND(arr#)) + END FUNCTION + + + '' + ' Return every nth array element of a DOUBLE array + ' + ' @param DOUBLE ARRAY source_arr#() to get from + ' @param DOUBLE ARRAY dest_arr#() to store in + ' @param INTEGER nth% element + ' + SUB ARR_DBL.nth(source_arr#(), dest_arr#(), nth%) + DIM AS LONG lb, ub, i, n + lb& = LBOUND(source_arr#) : ub& = UBOUND(source_arr#) + n& = (ub& - lb&) \ nth% + REDIM dest_arr(n&) AS DOUBLE + n& = 0 + FOR i& = lb& to ub& + IF i& MOD nth% = 0 THEN + dest_arr#(n&) = source_arr#(i&) + n& = n& + 1 + END IF + NEXT i& + END SUB + + + '' + ' Checks if value exists in DOUBLE array + ' + ' @param DOUBLE ARRAY arr#() to check in + ' @param DOUBLE value# value to check for + ' @return INTEGER -1 if found or 0 if not found + ' + FUNCTION ARR_DBL.in%(arr#(), value#) + DIM AS LONG lb, ub, i + lb& = LBOUND(arr#) : ub& = UBOUND(arr#) + FOR i& = lb& TO ub& + IF arr#(i&) = value# THEN + ARR_DBL.in% = -1 + EXIT FUNCTION + END IF + NEXT i& + ARR_DBL.in% = 0 + END FUNCTION + + + '' + ' Checks if value exists in DOUBLE array and returns index if found + ' + ' @param DOUBLE ARRAY arr#() to check in + ' @param DOUBLE value# value to check for + ' @return INTEGER index of element if found or -1 if not found + ' + FUNCTION ARR_DBL.find%(arr#(), value#) + DIM AS LONG lb, ub, i + lb& = LBOUND(arr#) : ub& = UBOUND(arr#) + FOR i& = lb& TO ub& + IF arr#(i&) = value# THEN + ARR_DBL.find% = i& + EXIT FUNCTION + END IF + NEXT i& + ARR_DBL.find% = -1 + END FUNCTION + + + '' + ' Return the number of elements in a DOUBLE array + ' + ' @param DOUBLE ARRAY arr#() to count + ' @return INTEGER number of elements + ' + FUNCTION ARR_DBL.count&(arr#()) + ARR_DBL.count& = UBOUND(arr#) - LBOUND(arr#) + END FUNCTION + + + '' + ' Return the size of a DOUBLE array + ' + ' @param DOUBLE ARRAY arr#() to get size of + ' @return LONG size in bytes + ' + FUNCTION ARR_DBL.size&(arr#()) + ARR_DBL.size& = LEN(arr#()) + END FUNCTION + + + '' + ' Reverses the elements of a DOUBLE array + ' + ' @param DOUBLE ARRAY source_arr#() to reverse + ' @param DOUBLE ARRAY dest_arr#() to store reversed array in + ' + SUB ARR_DBL.reverse(source_arr#(), dest_arr#()) + DIM AS LONG lb, ub, i, n + lb& = LBOUND(source_arr#) : ub& = UBOUND(source_arr#) + REDIM dest_arr(0 TO (ub& - lb&)) AS DOUBLE + n& = 0 + FOR i& = ub& TO lb& STEP -1 + dest_arr#(n&) = source_arr#(i&) + n& = n& + 1 + NEXT i& + END SUB + + + '' + ' Returns a random dbl from a DOUBLE array + ' + ' @param DOUBLE ARRAY arr#() array to get random element from + ' @return DOUBLE random element + ' + FUNCTION ARR_DBL.random#(arr#()) + DIM AS LONG lb, ub + lb& = LBOUND(arr#) : ub& = UBOUND(arr#) + RANDOMIZE TIMER + ARR_DBL.random# = arr#(INT(RND * (ub& - lb&)) + 1) + END FUNCTION + + + '' + ' Returns the sum of all elements in a DOUBLE array + ' + ' @param DOUBLE ARRAY arr#() array to get some for + ' @return LONG sum of all elements + ' + FUNCTION ARR_DBL.sum&(arr#()) + DIM AS LONG lb, ub, i + DIM sum AS LONG + lb& = LBOUND(arr#) : ub& = UBOUND(arr#) + FOR i& = lb& TO ub& + sum& = sum& + arr#(i&) + NEXT i& + ARR_DBL.sum& = sum& + END FUNCTION + + + '' + ' Returns the average value of elements in a DOUBLE array + ' + ' @param DOUBLE ARRAY arr#() array to get average for + ' @return LONG average of elements + ' + FUNCTION ARR_DBL.avg&(arr#()) + DIM AS LONG lb, ub, i + DIM sum AS LONG + lb& = LBOUND(arr#) : ub& = UBOUND(arr#) + FOR i& = lb& TO ub& + sum& = sum& + arr#(i&) + NEXT i& + ARR_DBL.avg& = sum& / (ub& - lb&) + END FUNCTION + + + '' + ' Shuffle the elements of a DOUBLE array + ' + ' @param DOUBLE ARRAY source_arr#() to shuffle + ' @param DOUBLE ARRAY dest_arr#() to store shuffled array in + ' + SUB ARR_DBL.shuffle(source_arr#(), dest_arr#()) + DIM AS LONG lb, ub, i, count + lb& = LBOUND(source_arr#) : ub& = UBOUND(source_arr#) + count& = ub& - lb& + REDIM dest_arr(0 TO count&) AS DOUBLE + CALL ARR_DBL.copy(source_arr#(), dest_arr#()) + RANDOMIZE TIMER + FOR i& = 0 TO count& + SWAP dest_arr#(i&), dest_arr#(lb& + RND * (ub& - lb&)) + NEXT i& + END SUB + + + '' + ' Makes a DOUBLE array contain only unique values + ' + ' @param DOUBLE ARRAY source_arr#() array to get uniques for + ' @param DOUBLE ARRAY dest_arr#() array to store uniques in + ' + SUB ARR_DBL.unique(source_arr#(), dest_arr#()) + DIM AS LONG lb, ub, i + DIM tmp AS DOUBLE + lb& = LBOUND(source_arr#) : ub& = UBOUND(source_arr#) + DIM work_arr(0) AS DOUBLE + FOR i& = lb& TO ub& + IF NOT ARR_DBL.in%(work_arr#(), source_arr#(i&)) THEN + CALL ARR_DBL.push(work_arr#(), source_arr#(i&)) + END IF + NEXT i& + CALL ARR_DBL.shift(work_arr#(), tmp#) + CALL ARR_DBL.copy(work_arr#(), dest_arr#()) + END SUB + + + '' + ' Filters a DOUBLE array to only elements greater than value + ' + ' @param DOUBLE ARRAY source_arr#() array to work on + ' @param DOUBLE ARRAY dest_arr#() array to store in + ' @param DOUBLE value# to be greater than to be returned + ' + SUB ARR_DBL.gt(source_arr#(), dest_arr#(), value#) + DIM AS LONG lb, ub, i + DIM tmp AS DOUBLE + lb& = LBOUND(source_arr#) : ub& = UBOUND(source_arr#) + DIM work_arr(0) AS DOUBLE + FOR i& = lb& TO ub& + IF source_arr#(i&) > value# THEN + CALL ARR_DBL.push(work_arr#(), source_arr#(i&)) + END IF + NEXT i& + CALL ARR_DBL.shift(work_arr#(), tmp#) + CALL ARR_DBL.copy(work_arr#(), dest_arr#()) + END SUB + + + '' + ' Filters a DOUBLE array to only elements greater than or equal to value + ' + ' @param DOUBLE ARRAY source_arr#() array to work on + ' @param DOUBLE ARRAY dest_arr#() array to store in + ' @param DOUBLE value# to be greater than or equal to be returned + ' + SUB ARR_DBL.gte(source_arr#(), dest_arr#(), value#) + DIM AS LONG lb, ub, i + DIM tmp AS DOUBLE + lb& = LBOUND(source_arr#) : ub& = UBOUND(source_arr#) + DIM work_arr(0) AS DOUBLE + FOR i& = lb& TO ub& + IF source_arr#(i&) >= value# THEN + CALL ARR_DBL.push(work_arr#(), source_arr#(i&)) + END IF + NEXT i& + CALL ARR_DBL.shift(work_arr#(), tmp#) + CALL ARR_DBL.copy(work_arr#(), dest_arr#()) + END SUB + + + '' + ' Filters a DOUBLE array to only elements less than value + ' + ' @param DOUBLE ARRAY source_arr#() array to work on + ' @param DOUBLE ARRAY dest_arr#() array to store in + ' @param DOUBLE value# to be less than to be returned + ' + SUB ARR_DBL.lt(source_arr#(), dest_arr#(), value#) + DIM AS LONG lb, ub, i + DIM tmp AS DOUBLE + lb& = LBOUND(source_arr#) : ub& = UBOUND(source_arr#) + DIM work_arr(0) AS DOUBLE + FOR i& = lb& TO ub& + IF source_arr#(i&) < value# THEN + CALL ARR_DBL.push(work_arr#(), source_arr#(i&)) + END IF + NEXT i& + CALL ARR_DBL.shift(work_arr#(), tmp#) + CALL ARR_DBL.copy(work_arr#(), dest_arr#()) + END SUB + + + '' + ' Filters a DOUBLE array to only elements less than or equal to value + ' + ' @param DOUBLE ARRAY source_arr#() array to work on + ' @param DOUBLE ARRAY dest_arr#() array to store in + ' @param DOUBLE value# to be less than or equal to be returned + ' + SUB ARR_DBL.lte(source_arr#(), dest_arr#(), value#) + DIM AS LONG lb, ub, i + DIM tmp AS DOUBLE + lb& = LBOUND(source_arr#) : ub& = UBOUND(source_arr#) + DIM work_arr(0) AS DOUBLE + FOR i& = lb& TO ub& + IF source_arr#(i&) <= value# THEN + CALL ARR_DBL.push(work_arr#(), source_arr#(i&)) + END IF + NEXT i& + CALL ARR_DBL.shift(work_arr#(), tmp#) + CALL ARR_DBL.copy(work_arr#(), dest_arr#()) + END SUB + + + '' + ' Finds and replaces values across all elements in a DOUBLE ARRAY + ' + ' @param DOUBLE ARRAY arr#() to check in + ' @param DOUBLE find# value to find + ' @param DOUBLE replace# value to replace with if found + ' + SUB ARR_DBL.replace(arr#(), find#, replace#) + DIM AS LONG lb, ub, i + lb& = LBOUND(arr#) : ub& = UBOUND(arr#) + FOR i& = lb& TO ub& + IF arr#(i&) = find# THEN + arr#(i&) = replace# + END IF + NEXT i& + END SUB + + + '' + ' Inserts a new element into DOUBLE array after index + ' + ' @param DOUBLE ARRAY arr#() array to work on + ' @param DOUBLE value# to insert + ' @param INTEGER index% of element to insert at + ' + SUB ARR_DBL.insert(arr#(), value#, index%) + DIM AS LONG lb, ub, i + DIM tmp AS DOUBLE + DIM work_arr(0) AS DOUBLE + lb& = LBOUND(arr#) : ub& = UBOUND(arr#) + IF index% >= lb& AND index% <= ub& THEN + ' build new array up to index + FOR i& = lb& TO index% + CALL ARR_DBL.push(work_arr#(), arr#(i&)) + NEXT i& + ' insert new element + CALL ARR_DBL.push(work_arr#(), value#) + ' finish building array from index + 1 + FOR i& = index% + 1 TO ub& + CALL ARR_DBL.push(work_arr#(), arr#(i&)) + NEXT i& + CALL ARR_DBL.shift(work_arr#(), tmp#) + CALL ARR_DBL.copy(work_arr#(), arr#()) + END IF + END SUB + + + '' + ' Removes element from a DOUBLE array by element index + ' + ' @param DOUBLE ARRAY arr#() array to work on + ' @param INTEGER index% of element to remove + ' + SUB ARR_DBL.remove(arr#(), index%) + DIM AS LONG lb, ub, i + DIM tmp AS DOUBLE + DIM work_arr(0) AS DOUBLE + lb& = LBOUND(arr#) : ub& = UBOUND(arr#) + IF index% >= lb& AND index% <= ub& THEN + ' build new array up to index + FOR i& = lb& TO index% - 1 + CALL ARR_DBL.push(work_arr#(), arr#(i&)) + NEXT i& + ' skip elements + FOR i& = index% + 1 TO ub& + CALL ARR_DBL.push(work_arr#(), arr#(i&)) + NEXT i& + CALL ARR_DBL.shift(work_arr#(), tmp#) + CALL ARR_DBL.copy(work_arr#(), arr#()) + END IF + END SUB + + + '' + ' Filters a DOUBLE array to only elements that have odd values + ' + ' @param DOUBLE ARRAY source_arr#() array to work on + ' @param DOUBLE ARRAY dest_arr#() array to store in + ' + SUB ARR_DBL.odd(source_arr#(), dest_arr#()) + DIM AS LONG lb, ub, i + DIM tmp AS DOUBLE + lb& = LBOUND(source_arr#) : ub& = UBOUND(source_arr#) + DIM work_arr(0) AS DOUBLE + FOR i& = lb& TO ub& + IF source_arr#(i&) MOD 2 <> 0 THEN + CALL ARR_DBL.push(work_arr#(), source_arr#(i&)) + END IF + NEXT i& + CALL ARR_DBL.shift(work_arr#(), tmp#) + CALL ARR_DBL.copy(work_arr#(), dest_arr#()) + END SUB + + + '' + ' Filters a DOUBLE array to only elements that have even values + ' + ' @param DOUBLE ARRAY source_arr#() array to work on + ' @param DOUBLE ARRAY dest_arr#() array to store in + ' + SUB ARR_DBL.even(source_arr#(), dest_arr#()) + DIM AS LONG lb, ub, i + DIM tmp AS DOUBLE + lb& = LBOUND(source_arr#) : ub& = UBOUND(source_arr#) + DIM work_arr(0) AS DOUBLE + FOR i& = lb& TO ub& + IF source_arr#(i&) MOD 2 = 0 THEN + CALL ARR_DBL.push(work_arr#(), source_arr#(i&)) + END IF + NEXT i& + CALL ARR_DBL.shift(work_arr#(), tmp#) + CALL ARR_DBL.copy(work_arr#(), dest_arr#()) + END SUB + + + '' + ' Filters a DOUBLE array to only elements that have values evenly divisible by divisor + ' + ' @param DOUBLE ARRAY source_arr#() array to work on + ' @param DOUBLE ARRAY dest_arr#() array to store in + ' @param DOUBLE divisor# for modulo + ' + SUB ARR_DBL.mod(source_arr#(), dest_arr#(), divisor#) + DIM AS LONG lb, ub, i + DIM tmp AS DOUBLE + lb& = LBOUND(source_arr#) : ub& = UBOUND(source_arr#) + DIM work_arr(0) AS DOUBLE + FOR i& = lb& TO ub& + IF source_arr#(i&) MOD divisor# = 0 THEN + CALL ARR_DBL.push(work_arr#(), source_arr#(i&)) + END IF + NEXT i& + CALL ARR_DBL.shift(work_arr#(), tmp#) + CALL ARR_DBL.copy(work_arr#(), dest_arr#()) + END SUB + + + '' + ' Filters a DOUBLE array to only elements between min and max + ' + ' @param DOUBLE ARRAY source_arr#() array to work on + ' @param DOUBLE ARRAY dest_arr#() array to store in + ' @param DOUBLE min# to be greater than or equal to be returned + ' @param DOUBLE max# to be less than or equal to be returned + ' + SUB ARR_DBL.between(source_arr#(), dest_arr#(), min#, max#) + DIM AS LONG lb, ub, i + DIM tmp AS DOUBLE + lb& = LBOUND(source_arr#) : ub& = UBOUND(source_arr#) + DIM work_arr(0) AS DOUBLE + FOR i& = lb& TO ub& + IF source_arr#(i&) >= min# _ + AND source_arr#(i&) <= max# THEN + CALL ARR_DBL.push(work_arr#(), source_arr#(i&)) + END IF + NEXT i& + CALL ARR_DBL.shift(work_arr#(), tmp#) + CALL ARR_DBL.copy(work_arr#(), dest_arr#()) + END SUB + + + '' + ' Sorts DOUBLE array in ascending order + ' + ' @param DOUBLE ARRAY source_arr#() array to sort + ' @param DOUBLE ARRAY dest_arr#() array to store sorted in + ' + SUB ARR_DBL.sort(source_arr#(), dest_arr#()) + DIM AS LONG lb, ub, count + lb& = LBOUND(source_arr#) : ub& = UBOUND(source_arr#) + count& = ub& - lb& + REDIM dest_arr(0 TO count&) AS DOUBLE + CALL ARR_DBL.copy(source_arr#(), dest_arr#()) + CALL ARR_DBL.quicksort(dest_arr#(), lb&, ub&, 0) + END SUB + + + '' + ' Sorts DOUBLE array in descending order + ' + ' @param DOUBLE ARRAY source_arr#() array to sort + ' @param DOUBLE ARRAY dest_arr#() array to store sorted in + ' + SUB ARR_DBL.rsort(source_arr#(), dest_arr#()) + DIM AS LONG lb, ub, count + lb& = LBOUND(source_arr#) : ub& = UBOUND(source_arr#) + count& = ub& - lb& + REDIM dest_arr(0 TO count&) AS DOUBLE + CALL ARR_DBL.copy(source_arr#(), dest_arr#()) + CALL ARR_DBL.quicksort(dest_arr#(), lb&, ub&, 1) + END SUB + + + '' + ' Quicksort array with pivot algorithm by logiclrd + ' + ' @link https://www.tek-tips.com/faqs.cfm?fid=336 + ' @param DOUBLE ARRAY array#() to sort + ' @param INTEGER start% of range to sort + ' @param INTEGER finish% range of sort + ' @param INTEGER order% to sort by (0 = asc / 1 = desc) + ' + SUB ARR_DBL.quicksort(arr#(), start%, finish%, order%) + DIM i AS LONG + DIM pivot AS INTEGER + DIM pivotvalue AS DOUBLE + + 'first, partition the array + pivot% = start% + pivotvalue# = arr#(pivot%) + FOR i& = start% + 1 TO finish% + IF order% = 0 THEN ' ascending order + IF arr#(i&) < pivotvalue# THEN + arr#(pivot%) = arr#(i&) + arr#(i&) = arr#(pivot% + 1) + arr#(pivot% + 1) = pivotvalue# + pivot% = pivot% + 1 + END IF + ELSEIF order% = 1 THEN ' descending order + IF arr#(i&) > pivotvalue# THEN + arr#(pivot%) = arr#(i&) + arr#(i&) = arr#(pivot% + 1) + arr#(pivot% + 1) = pivotvalue# + pivot% = pivot% + 1 + END IF + END IF + NEXT i& + + 'then, sort the subarrays to each side of the pivot + IF pivot% - start% >= 2 THEN + CALL ARR_DBL.quicksort(arr#(), start%, pivot% - 1, order%) + END IF + IF finish% - pivot% >= 2 THEN + CALL ARR_DBL.quicksort(arr#(), pivot% + 1, finish%, order%) + END IF + END SUB + $END IF + $IF GJ_LIB_ARR_FLT_BAS = UNDEFINED THEN + '$DYNAMIC + $LET GJ_LIB_ARR_FLT_BAS = 1 + + '' + ' Slice an array from source to destination starting at index and count slices + ' + ' @param _FLOAT() source_arr## to slice from + ' @param _FLOAT() dest_arr## to put slices into + ' @param INTEGER start_idx% starting index to use as slice range + ' @param INTEGER count% number of slices - if negative, backwards from index + ' + SUB ARR_FLT.slice(source_arr##(), dest_arr##(), start_idx%, count%) + DIM AS LONG ub, lb, i, n + lb& = LBOUND(source_arr##) : ub& = UBOUND(source_arr##) + IF start_idx% < lb& OR start_idx% + count% > ub& THEN EXIT SUB ' out of range + IF ub& - lb& < count% THEN EXIT SUB ' too many and not enough + REDIM dest_arr(0 TO ABS(count%)) AS _FLOAT + IF SGN(count%) = -1 THEN + IF ((start_idx% - 1) - ABS(count%)) < 0 THEN EXIT SUB ' out of range + n& = 0 + FOR i& = (start_idx% - 1) TO ((start_idx% - 1) - ABS(count%)) STEP -1 + dest_arr##(n&) = source_arr##(i&) + n& = n& + 1 + NEXT i& + ELSE + IF ((start_idx% + 1) + ABS(count%)) > (ub& - lb&) THEN EXIT SUB ' out of range + n& = 0 + FOR i& = start_idx% + 1 TO ((start_idx% + 1) + count%) STEP 1 + dest_arr##(n&) = source_arr##(i&) + n& = n& + 1 + NEXT i& + END IF + END SUB + + + '' + ' Push a flt onto the end of a _FLOAT array + ' + ' @param _FLOAT arr##() array to push into + ' @param _FLOAT value## of byte to push + ' + SUB ARR_FLT.push(arr##(), value##) + DIM AS LONG ub, lb + lb& = LBOUND(arr##) : ub& = UBOUND(arr##) + REDIM _PRESERVE arr(lb& TO (ub& + 1)) AS _FLOAT + arr##(ub& + 1) = value## + END SUB + + + '' + ' Pop a flt from the end of a _FLOAT array + ' + ' @param _FLOAT arr##() array to pop from + ' @param _FLOAT var## of flt to store popped flt + ' + SUB ARR_FLT.pop(arr##(), var##) + DIM AS LONG ub, lb + lb& = LBOUND(arr##) : ub& = UBOUND(arr##) + var## = arr##(ub&) + REDIM _PRESERVE arr(lb& TO (ub& - 1)) AS _FLOAT + END SUB + + + '' + ' Pop a flt from the beginning of a _FLOAT array + ' + ' @param _FLOAT arr##() array to pop from + ' @param _FLOAT var## of flt to store popped flt + ' + SUB ARR_FLT.shift(arr##(), var##) + DIM AS LONG ub, lb, i + lb& = LBOUND(arr##) : ub& = UBOUND(arr##) + var## = arr##(lb&) + FOR i& = lb& TO ub& - 1 + arr##(i&) = arr##(i& + 1) + NEXT i& + REDIM _PRESERVE arr(lb& + 1 TO ub&) AS _FLOAT + END SUB + + + '' + ' Copy an array of FLTs to another _FLOAT array + ' + ' @param _FLOAT ARRAY source_arr##() source array to copy + ' @param _FLOAT ARRAY dest_arr##() dest array to copy into + ' + SUB ARR_FLT.copy(source_arr##(), dest_arr##()) + DIM AS LONG ub, lb, i + lb& = LBOUND(source_arr##) : ub& = UBOUND(source_arr##) + REDIM dest_arr(lb& TO ub&) AS _FLOAT + FOR i& = lb& TO ub& + dest_arr##(i&) = source_arr##(i&) + NEXT i& + END SUB + + + '' + ' Push a flt into the beginning of a _FLOAT array + ' + ' @param _FLOAT arr##() array to push into + ' @param _FLOAT value## of flt to push + ' + SUB ARR_FLT.unshift(arr##(), value##) + DIM AS LONG ub, lb, i + lb& = LBOUND(arr##) : ub& = UBOUND(arr##) + DIM work(lb& TO ub&) AS _FLOAT + CALL ARR_FLT.copy(arr##(), work##()) + REDIM _PRESERVE arr(lb& TO (ub& + 1)) AS _FLOAT + FOR i& = lb& + 1 TO ub& + 1 + arr##(i&) = work##(i& - 1) + NEXT i& + arr##(lb&) = value## + END SUB + + + '' + ' Joins an array of FLTs as a string + ' + ' @param _FLOAT ARRAY arr##() to get as a string + ' @param STRING s$ to store stringified array in + ' + SUB ARR_FLT.join(arr##(), s$) + DIM AS LONG ub, lb, i + s$ = "" + lb& = LBOUND(arr##) : ub& = UBOUND(arr##) + FOR i& = lb& TO ub& + s$ = s$ + _TRIM$(STR$(arr##(i&))) + ", " + NEXT i& + ' remove trailing comma + s$ = MID$(s$, 1, (LEN(s$)-LEN(", "))) + END SUB + + + '' + ' Create a new _FLOAT array using string of flts seperated by commas + ' + ' @param _FLOAT ARRAY arr##() to store the flts in + ' @param STRING s$ string of comma separated flts + ' + SUB ARR_FLT.new(arr##(), s$) + DIM AS LONG i, count + DIM t AS STRING + count& = 0 + FOR i& = 1 TO LEN(s$) + IF ASC(s$, i&) = ASC(",") THEN count& = count& + 1 + NEXT i& + REDIM arr(0 TO count&) AS _FLOAT + IF count& = 0 THEN + arr##(0) = VAL(s$) + EXIT SUB + END IF + count& = 0 + FOR i& = 1 TO LEN(s$) + t$ = t$ + CHR$(ASC(s$, i&)) + IF ASC(s$, i&) = ASC(",") OR i& = LEN(s$) THEN + arr##(count&) = VAL(t$) + count& = count& + 1 + t$ = "" + END IF + NEXT i& + END SUB + + + '' + ' Return the visually longest element of a _FLOAT array + ' + ' @param _FLOAT ARRAY arr##() to check in + ' @return _FLOAT value of visually longest element + ' + FUNCTION ARR_FLT.longest##(arr##()) + DIM AS LONG lb, ub, i, res, lw + lb& = LBOUND(arr##) : ub& = UBOUND(arr##) + res& = 0 : lw& = 0 + FOR i& = lb& TO ub& + IF LEN(_TRIM$(STR$(arr##(i&)))) > lw& THEN + lw& = LEN(_TRIM$(STR$(arr##(i&)))) + res& = i& + END IF + NEXT i& + ARR_FLT.longest## = arr##(res&) + END FUNCTION + + + '' + ' Perform some math on every element of a _FLOAT array + ' + ' @param _FLOAT ARRAY source_arr##() to do math on + ' @param _FLOAT ARRAY dest_arr##() to store results in + ' @param STRING op$ one of: + ' and or xor shl shr + ' "+", "-", "*", "\", "&&", "||", "!!", "<<", ">>" + ' @param _FLOAT value## to use for operand + ' + SUB ARR_FLT.math(source_arr##(), dest_arr##(), op$, value##) + DIM AS LONG lb, ub, i + lb& = LBOUND(source_arr##) : ub& = UBOUND(source_arr##) + REDIM dest_arr(lb& TO ub&) AS _FLOAT + FOR i& = lb& TO ub& + SELECT CASE op$ + CASE "+": + dest_arr##(i&) = source_arr##(i&) + value## + CASE "-": + dest_arr##(i&) = source_arr##(i&) - value## + CASE "*": + dest_arr##(i&) = source_arr##(i&) * value## + CASE "\": + IF value## > 0 THEN + dest_arr##(i&) = source_arr##(i&) \ value## + END IF + CASE "&&": + dest_arr##(i&) = source_arr##(i&) AND value## + CASE "||": + dest_arr##(i&) = source_arr##(i&) OR value## + CASE "!!": + dest_arr##(i&) = source_arr##(i&) XOR value## + CASE "<<": + dest_arr##(i&) = _SHL(source_arr##(i&), value##) + CASE ">>": + dest_arr##(i&) = _SHR(source_arr##(i&), value##) + END SELECT + NEXT i& + END SUB + + + '' + ' Return the minimum element value in _FLOAT array + ' + ' @param _FLOAT ARRAY arr##() to check in + ' @return _FLOAT minimum value found + ' + FUNCTION ARR_FLT.min##(arr##()) + DIM AS LONG lb, ub, i + DIM AS _FLOAT s + lb& = LBOUND(arr##) : ub& = UBOUND(arr##) + s## = 127 + FOR i& = lb& TO ub& + IF arr##(i&) < s## THEN + s## = arr##(i&) + END IF + NEXT i& + ARR_FLT.min## = s## + END FUNCTION + + + '' + ' Return the maximum element value in _FLOAT array + ' + ' @param _FLOAT ARRAY arr##() to check in + ' @return _FLOAT maximum value found + ' + FUNCTION ARR_FLT.max##(arr##()) + DIM AS LONG lb, ub, i + DIM AS _FLOAT s + lb& = LBOUND(arr##) : ub& = UBOUND(arr##) + s## = 0 + FOR i& = lb& TO ub& + IF arr##(i&) > s## THEN + s## = arr##(i&) + END IF + NEXT i& + ARR_FLT.max## = s## + END FUNCTION + + + '' + ' Return the visually shortest element of a _FLOAT array + ' + ' @param _FLOAT ARRAY arr##() to check in + ' @return _FLOAT value of visually shortest element + ' + FUNCTION ARR_FLT.shortest##(arr##()) + DIM AS LONG lb, ub, i, res, sw + lb& = LBOUND(arr##) : ub& = UBOUND(arr##) + res& = 0 : sw& = 0 + FOR i& = lb& TO ub& + IF LEN(_TRIM$(STR$(arr##(i&)))) < sw& THEN + sw& = LEN(_TRIM$(STR$(arr##(i&)))) + res& = i& + END IF + NEXT i& + ARR_FLT.shortest## = arr##(res&) + END FUNCTION + + + '' + ' Return the first element of a _FLOAT array + ' + ' @param _FLOAT ARRAY arr##() to check in + ' @return _FLOAT value of first element + ' + FUNCTION ARR_FLT.first##(arr##()) + ARR_FLT.first## = arr##(LBOUND(arr##)) + END FUNCTION + + + '' + ' Return the last element of a _FLOAT array + ' + ' @param _FLOAT ARRAY arr##() to check in + ' @return _FLOAT value of last element + ' + FUNCTION ARR_FLT.last##(arr##()) + ARR_FLT.last## = arr##(UBOUND(arr##)) + END FUNCTION + + + '' + ' Return every nth array element of a _FLOAT array + ' + ' @param _FLOAT ARRAY source_arr##() to get from + ' @param _FLOAT ARRAY dest_arr##() to store in + ' @param INTEGER nth% element + ' + SUB ARR_FLT.nth(source_arr##(), dest_arr##(), nth%) + DIM AS LONG lb, ub, i, n + lb& = LBOUND(source_arr##) : ub& = UBOUND(source_arr##) + n& = (ub& - lb&) \ nth% + REDIM dest_arr(n&) AS _FLOAT + n& = 0 + FOR i& = lb& to ub& + IF i& MOD nth% = 0 THEN + dest_arr##(n&) = source_arr##(i&) + n& = n& + 1 + END IF + NEXT i& + END SUB + + + '' + ' Checks if value exists in _FLOAT array + ' + ' @param _FLOAT ARRAY arr##() to check in + ' @param _FLOAT value## value to check for + ' @return INTEGER -1 if found or 0 if not found + ' + FUNCTION ARR_FLT.in%(arr##(), value##) + DIM AS LONG lb, ub, i + lb& = LBOUND(arr##) : ub& = UBOUND(arr##) + FOR i& = lb& TO ub& + IF arr##(i&) = value## THEN + ARR_FLT.in% = -1 + EXIT FUNCTION + END IF + NEXT i& + ARR_FLT.in% = 0 + END FUNCTION + + + '' + ' Checks if value exists in _FLOAT array and returns index if found + ' + ' @param _FLOAT ARRAY arr##() to check in + ' @param _FLOAT value## value to check for + ' @return INTEGER index of element if found or -1 if not found + ' + FUNCTION ARR_FLT.find%(arr##(), value##) + DIM AS LONG lb, ub, i + lb& = LBOUND(arr##) : ub& = UBOUND(arr##) + FOR i& = lb& TO ub& + IF arr##(i&) = value## THEN + ARR_FLT.find% = i& + EXIT FUNCTION + END IF + NEXT i& + ARR_FLT.find% = -1 + END FUNCTION + + + '' + ' Return the number of elements in a _FLOAT array + ' + ' @param _FLOAT ARRAY arr##() to count + ' @return INTEGER number of elements + ' + FUNCTION ARR_FLT.count&(arr##()) + ARR_FLT.count& = UBOUND(arr##) - LBOUND(arr##) + END FUNCTION + + + '' + ' Return the size of a _FLOAT array + ' + ' @param _FLOAT ARRAY arr##() to get size of + ' @return LONG size in bytes + ' + FUNCTION ARR_FLT.size&(arr##()) + ARR_FLT.size& = LEN(arr##()) + END FUNCTION + + + '' + ' Reverses the elements of a _FLOAT array + ' + ' @param _FLOAT ARRAY source_arr##() to reverse + ' @param _FLOAT ARRAY dest_arr##() to store reversed array in + ' + SUB ARR_FLT.reverse(source_arr##(), dest_arr##()) + DIM AS LONG lb, ub, i, n + lb& = LBOUND(source_arr##) : ub& = UBOUND(source_arr##) + REDIM dest_arr(0 TO (ub& - lb&)) AS _FLOAT + n& = 0 + FOR i& = ub& TO lb& STEP -1 + dest_arr##(n&) = source_arr##(i&) + n& = n& + 1 + NEXT i& + END SUB + + + '' + ' Returns a random flt from a _FLOAT array + ' + ' @param _FLOAT ARRAY arr##() array to get random element from + ' @return _FLOAT random element + ' + FUNCTION ARR_FLT.random##(arr##()) + DIM AS LONG lb, ub + lb& = LBOUND(arr##) : ub& = UBOUND(arr##) + RANDOMIZE TIMER + ARR_FLT.random## = arr##(INT(RND * (ub& - lb&)) + 1) + END FUNCTION + + + '' + ' Returns the sum of all elements in a _FLOAT array + ' + ' @param _FLOAT ARRAY arr##() array to get some for + ' @return LONG sum of all elements + ' + FUNCTION ARR_FLT.sum&(arr##()) + DIM AS LONG lb, ub, i + DIM sum AS LONG + lb& = LBOUND(arr##) : ub& = UBOUND(arr##) + FOR i& = lb& TO ub& + sum& = sum& + arr##(i&) + NEXT i& + ARR_FLT.sum& = sum& + END FUNCTION + + + '' + ' Returns the average value of elements in a _FLOAT array + ' + ' @param _FLOAT ARRAY arr##() array to get average for + ' @return LONG average of elements + ' + FUNCTION ARR_FLT.avg&(arr##()) + DIM AS LONG lb, ub, i + DIM sum AS LONG + lb& = LBOUND(arr##) : ub& = UBOUND(arr##) + FOR i& = lb& TO ub& + sum& = sum& + arr##(i&) + NEXT i& + ARR_FLT.avg& = sum& / (ub& - lb&) + END FUNCTION + + + '' + ' Shuffle the elements of a _FLOAT array + ' + ' @param _FLOAT ARRAY source_arr##() to shuffle + ' @param _FLOAT ARRAY dest_arr##() to store shuffled array in + ' + SUB ARR_FLT.shuffle(source_arr##(), dest_arr##()) + DIM AS LONG lb, ub, i, count + lb& = LBOUND(source_arr##) : ub& = UBOUND(source_arr##) + count& = ub& - lb& + REDIM dest_arr(0 TO count&) AS _FLOAT + CALL ARR_FLT.copy(source_arr##(), dest_arr##()) + RANDOMIZE TIMER + FOR i& = 0 TO count& + SWAP dest_arr##(i&), dest_arr##(lb& + RND * (ub& - lb&)) + NEXT i& + END SUB + + + '' + ' Makes a _FLOAT array contain only unique values + ' + ' @param _FLOAT ARRAY source_arr##() array to get uniques for + ' @param _FLOAT ARRAY dest_arr##() array to store uniques in + ' + SUB ARR_FLT.unique(source_arr##(), dest_arr##()) + DIM AS LONG lb, ub, i + DIM tmp AS _FLOAT + lb& = LBOUND(source_arr##) : ub& = UBOUND(source_arr##) + DIM work_arr(0) AS _FLOAT + FOR i& = lb& TO ub& + IF NOT ARR_FLT.in%(work_arr##(), source_arr##(i&)) THEN + CALL ARR_FLT.push(work_arr##(), source_arr##(i&)) + END IF + NEXT i& + CALL ARR_FLT.shift(work_arr##(), tmp##) + CALL ARR_FLT.copy(work_arr##(), dest_arr##()) + END SUB + + + '' + ' Filters a _FLOAT array to only elements greater than value + ' + ' @param _FLOAT ARRAY source_arr##() array to work on + ' @param _FLOAT ARRAY dest_arr##() array to store in + ' @param _FLOAT value## to be greater than to be returned + ' + SUB ARR_FLT.gt(source_arr##(), dest_arr##(), value##) + DIM AS LONG lb, ub, i + DIM tmp AS _FLOAT + lb& = LBOUND(source_arr##) : ub& = UBOUND(source_arr##) + DIM work_arr(0) AS _FLOAT + FOR i& = lb& TO ub& + IF source_arr##(i&) > value## THEN + CALL ARR_FLT.push(work_arr##(), source_arr##(i&)) + END IF + NEXT i& + CALL ARR_FLT.shift(work_arr##(), tmp##) + CALL ARR_FLT.copy(work_arr##(), dest_arr##()) + END SUB + + + '' + ' Filters a _FLOAT array to only elements greater than or equal to value + ' + ' @param _FLOAT ARRAY source_arr##() array to work on + ' @param _FLOAT ARRAY dest_arr##() array to store in + ' @param _FLOAT value## to be greater than or equal to be returned + ' + SUB ARR_FLT.gte(source_arr##(), dest_arr##(), value##) + DIM AS LONG lb, ub, i + DIM tmp AS _FLOAT + lb& = LBOUND(source_arr##) : ub& = UBOUND(source_arr##) + DIM work_arr(0) AS _FLOAT + FOR i& = lb& TO ub& + IF source_arr##(i&) >= value## THEN + CALL ARR_FLT.push(work_arr##(), source_arr##(i&)) + END IF + NEXT i& + CALL ARR_FLT.shift(work_arr##(), tmp##) + CALL ARR_FLT.copy(work_arr##(), dest_arr##()) + END SUB + + + '' + ' Filters a _FLOAT array to only elements less than value + ' + ' @param _FLOAT ARRAY source_arr##() array to work on + ' @param _FLOAT ARRAY dest_arr##() array to store in + ' @param _FLOAT value## to be less than to be returned + ' + SUB ARR_FLT.lt(source_arr##(), dest_arr##(), value##) + DIM AS LONG lb, ub, i + DIM tmp AS _FLOAT + lb& = LBOUND(source_arr##) : ub& = UBOUND(source_arr##) + DIM work_arr(0) AS _FLOAT + FOR i& = lb& TO ub& + IF source_arr##(i&) < value## THEN + CALL ARR_FLT.push(work_arr##(), source_arr##(i&)) + END IF + NEXT i& + CALL ARR_FLT.shift(work_arr##(), tmp##) + CALL ARR_FLT.copy(work_arr##(), dest_arr##()) + END SUB + + + '' + ' Filters a _FLOAT array to only elements less than or equal to value + ' + ' @param _FLOAT ARRAY source_arr##() array to work on + ' @param _FLOAT ARRAY dest_arr##() array to store in + ' @param _FLOAT value## to be less than or equal to be returned + ' + SUB ARR_FLT.lte(source_arr##(), dest_arr##(), value##) + DIM AS LONG lb, ub, i + DIM tmp AS _FLOAT + lb& = LBOUND(source_arr##) : ub& = UBOUND(source_arr##) + DIM work_arr(0) AS _FLOAT + FOR i& = lb& TO ub& + IF source_arr##(i&) <= value## THEN + CALL ARR_FLT.push(work_arr##(), source_arr##(i&)) + END IF + NEXT i& + CALL ARR_FLT.shift(work_arr##(), tmp##) + CALL ARR_FLT.copy(work_arr##(), dest_arr##()) + END SUB + + + '' + ' Finds and replaces values across all elements in a _FLOAT ARRAY + ' + ' @param _FLOAT ARRAY arr##() to check in + ' @param _FLOAT find## value to find + ' @param _FLOAT replace## value to replace with if found + ' + SUB ARR_FLT.replace(arr##(), find##, replace##) + DIM AS LONG lb, ub, i + lb& = LBOUND(arr##) : ub& = UBOUND(arr##) + FOR i& = lb& TO ub& + IF arr##(i&) = find## THEN + arr##(i&) = replace## + END IF + NEXT i& + END SUB + + + '' + ' Inserts a new element into _FLOAT array after index + ' + ' @param _FLOAT ARRAY arr##() array to work on + ' @param _FLOAT value## to insert + ' @param INTEGER index% of element to insert at + ' + SUB ARR_FLT.insert(arr##(), value##, index%) + DIM AS LONG lb, ub, i + DIM tmp AS _FLOAT + DIM work_arr(0) AS _FLOAT + lb& = LBOUND(arr##) : ub& = UBOUND(arr##) + IF index% >= lb& AND index% <= ub& THEN + ' build new array up to index + FOR i& = lb& TO index% + CALL ARR_FLT.push(work_arr##(), arr##(i&)) + NEXT i& + ' insert new element + CALL ARR_FLT.push(work_arr##(), value##) + ' finish building array from index + 1 + FOR i& = index% + 1 TO ub& + CALL ARR_FLT.push(work_arr##(), arr##(i&)) + NEXT i& + CALL ARR_FLT.shift(work_arr##(), tmp##) + CALL ARR_FLT.copy(work_arr##(), arr##()) + END IF + END SUB + + + '' + ' Removes element from a _FLOAT array by element index + ' + ' @param _FLOAT ARRAY arr##() array to work on + ' @param INTEGER index% of element to remove + ' + SUB ARR_FLT.remove(arr##(), index%) + DIM AS LONG lb, ub, i + DIM tmp AS _FLOAT + DIM work_arr(0) AS _FLOAT + lb& = LBOUND(arr##) : ub& = UBOUND(arr##) + IF index% >= lb& AND index% <= ub& THEN + ' build new array up to index + FOR i& = lb& TO index% - 1 + CALL ARR_FLT.push(work_arr##(), arr##(i&)) + NEXT i& + ' skip elements + FOR i& = index% + 1 TO ub& + CALL ARR_FLT.push(work_arr##(), arr##(i&)) + NEXT i& + CALL ARR_FLT.shift(work_arr##(), tmp##) + CALL ARR_FLT.copy(work_arr##(), arr##()) + END IF + END SUB + + + '' + ' Filters a _FLOAT array to only elements that have odd values + ' + ' @param _FLOAT ARRAY source_arr##() array to work on + ' @param _FLOAT ARRAY dest_arr##() array to store in + ' + SUB ARR_FLT.odd(source_arr##(), dest_arr##()) + DIM AS LONG lb, ub, i + DIM tmp AS _FLOAT + lb& = LBOUND(source_arr##) : ub& = UBOUND(source_arr##) + DIM work_arr(0) AS _FLOAT + FOR i& = lb& TO ub& + IF source_arr##(i&) MOD 2 <> 0 THEN + CALL ARR_FLT.push(work_arr##(), source_arr##(i&)) + END IF + NEXT i& + CALL ARR_FLT.shift(work_arr##(), tmp##) + CALL ARR_FLT.copy(work_arr##(), dest_arr##()) + END SUB + + + '' + ' Filters a _FLOAT array to only elements that have even values + ' + ' @param _FLOAT ARRAY source_arr##() array to work on + ' @param _FLOAT ARRAY dest_arr##() array to store in + ' + SUB ARR_FLT.even(source_arr##(), dest_arr##()) + DIM AS LONG lb, ub, i + DIM tmp AS _FLOAT + lb& = LBOUND(source_arr##) : ub& = UBOUND(source_arr##) + DIM work_arr(0) AS _FLOAT + FOR i& = lb& TO ub& + IF source_arr##(i&) MOD 2 = 0 THEN + CALL ARR_FLT.push(work_arr##(), source_arr##(i&)) + END IF + NEXT i& + CALL ARR_FLT.shift(work_arr##(), tmp##) + CALL ARR_FLT.copy(work_arr##(), dest_arr##()) + END SUB + + + '' + ' Filters a _FLOAT array to only elements that have values evenly divisible by divisor + ' + ' @param _FLOAT ARRAY source_arr##() array to work on + ' @param _FLOAT ARRAY dest_arr##() array to store in + ' @param _FLOAT divisor## for modulo + ' + SUB ARR_FLT.mod(source_arr##(), dest_arr##(), divisor##) + DIM AS LONG lb, ub, i + DIM tmp AS _FLOAT + lb& = LBOUND(source_arr##) : ub& = UBOUND(source_arr##) + DIM work_arr(0) AS _FLOAT + FOR i& = lb& TO ub& + IF source_arr##(i&) MOD divisor## = 0 THEN + CALL ARR_FLT.push(work_arr##(), source_arr##(i&)) + END IF + NEXT i& + CALL ARR_FLT.shift(work_arr##(), tmp##) + CALL ARR_FLT.copy(work_arr##(), dest_arr##()) + END SUB + + + '' + ' Filters a _FLOAT array to only elements between min and max + ' + ' @param _FLOAT ARRAY source_arr##() array to work on + ' @param _FLOAT ARRAY dest_arr##() array to store in + ' @param _FLOAT min## to be greater than or equal to be returned + ' @param _FLOAT max## to be less than or equal to be returned + ' + SUB ARR_FLT.between(source_arr##(), dest_arr##(), min##, max##) + DIM AS LONG lb, ub, i + DIM tmp AS _FLOAT + lb& = LBOUND(source_arr##) : ub& = UBOUND(source_arr##) + DIM work_arr(0) AS _FLOAT + FOR i& = lb& TO ub& + IF source_arr##(i&) >= min## _ + AND source_arr##(i&) <= max## THEN + CALL ARR_FLT.push(work_arr##(), source_arr##(i&)) + END IF + NEXT i& + CALL ARR_FLT.shift(work_arr##(), tmp##) + CALL ARR_FLT.copy(work_arr##(), dest_arr##()) + END SUB + + + '' + ' Sorts _FLOAT array in ascending order + ' + ' @param _FLOAT ARRAY source_arr##() array to sort + ' @param _FLOAT ARRAY dest_arr##() array to store sorted in + ' + SUB ARR_FLT.sort(source_arr##(), dest_arr##()) + DIM AS LONG lb, ub, count + lb& = LBOUND(source_arr##) : ub& = UBOUND(source_arr##) + count& = ub& - lb& + REDIM dest_arr(0 TO count&) AS _FLOAT + CALL ARR_FLT.copy(source_arr##(), dest_arr##()) + CALL ARR_FLT.quicksort(dest_arr##(), lb&, ub&, 0) + END SUB + + + '' + ' Sorts _FLOAT array in descending order + ' + ' @param _FLOAT ARRAY source_arr##() array to sort + ' @param _FLOAT ARRAY dest_arr##() array to store sorted in + ' + SUB ARR_FLT.rsort(source_arr##(), dest_arr##()) + DIM AS LONG lb, ub, count + lb& = LBOUND(source_arr##) : ub& = UBOUND(source_arr##) + count& = ub& - lb& + REDIM dest_arr(0 TO count&) AS _FLOAT + CALL ARR_FLT.copy(source_arr##(), dest_arr##()) + CALL ARR_FLT.quicksort(dest_arr##(), lb&, ub&, 1) + END SUB + + + '' + ' Quicksort array with pivot algorithm by logiclrd + ' + ' @link https://www.tek-tips.com/faqs.cfm?fid=336 + ' @param _FLOAT ARRAY array##() to sort + ' @param INTEGER start% of range to sort + ' @param INTEGER finish% range of sort + ' @param INTEGER order% to sort by (0 = asc / 1 = desc) + ' + SUB ARR_FLT.quicksort(arr##(), start%, finish%, order%) + DIM i AS LONG + DIM pivot AS INTEGER + DIM pivotvalue AS _FLOAT + + 'first, partition the array + pivot% = start% + pivotvalue## = arr##(pivot%) + FOR i& = start% + 1 TO finish% + IF order% = 0 THEN ' ascending order + IF arr##(i&) < pivotvalue## THEN + arr##(pivot%) = arr##(i&) + arr##(i&) = arr##(pivot% + 1) + arr##(pivot% + 1) = pivotvalue## + pivot% = pivot% + 1 + END IF + ELSEIF order% = 1 THEN ' descending order + IF arr##(i&) > pivotvalue## THEN + arr##(pivot%) = arr##(i&) + arr##(i&) = arr##(pivot% + 1) + arr##(pivot% + 1) = pivotvalue## + pivot% = pivot% + 1 + END IF + END IF + NEXT i& + + 'then, sort the subarrays to each side of the pivot + IF pivot% - start% >= 2 THEN + CALL ARR_FLT.quicksort(arr##(), start%, pivot% - 1, order%) + END IF + IF finish% - pivot% >= 2 THEN + CALL ARR_FLT.quicksort(arr##(), pivot% + 1, finish%, order%) + END IF + END SUB + $END IF + $IF GJ_LIB_ARR_INT_BAS = UNDEFINED THEN + '$DYNAMIC + $LET GJ_LIB_ARR_INT_BAS = 1 + + '' + ' Slice an array from source to destination starting at index and count slices + ' + ' @param INTEGER() source_arr% to slice from + ' @param INTEGER() dest_arr% to put slices into + ' @param INTEGER start_idx% starting index to use as slice range + ' @param INTEGER count% number of slices - if negative, backwards from index + ' + SUB ARR_INT.slice(source_arr%(), dest_arr%(), start_idx%, count%) + DIM AS LONG ub, lb, i, n + lb& = LBOUND(source_arr%) : ub& = UBOUND(source_arr%) + IF start_idx% < lb& OR start_idx% + count% > ub& THEN EXIT SUB ' out of range + IF ub& - lb& < count% THEN EXIT SUB ' too many and not enough + REDIM dest_arr(0 TO ABS(count%)) AS INTEGER + IF SGN(count%) = -1 THEN + IF ((start_idx% - 1) - ABS(count%)) < 0 THEN EXIT SUB ' out of range + n& = 0 + FOR i& = (start_idx% - 1) TO ((start_idx% - 1) - ABS(count%)) STEP -1 + dest_arr%(n&) = source_arr%(i&) + n& = n& + 1 + NEXT i& + ELSE + IF ((start_idx% + 1) + ABS(count%)) > (ub& - lb&) THEN EXIT SUB ' out of range + n& = 0 + FOR i& = start_idx% + 1 TO ((start_idx% + 1) + count%) STEP 1 + dest_arr%(n&) = source_arr%(i&) + n& = n& + 1 + NEXT i& + END IF + END SUB + + + '' + ' Push a int onto the end of a INTEGER array + ' + ' @param INTEGER arr%() array to push into + ' @param INTEGER value% of byte to push + ' + SUB ARR_INT.push(arr%(), value%) + DIM AS LONG ub, lb + lb& = LBOUND(arr%) : ub& = UBOUND(arr%) + REDIM _PRESERVE arr(lb& TO (ub& + 1)) AS INTEGER + arr%(ub& + 1) = value% + END SUB + + + '' + ' Pop a int from the end of a INTEGER array + ' + ' @param INTEGER arr%() array to pop from + ' @param INTEGER var% of int to store popped int + ' + SUB ARR_INT.pop(arr%(), var%) + DIM AS LONG ub, lb + lb& = LBOUND(arr%) : ub& = UBOUND(arr%) + var% = arr%(ub&) + REDIM _PRESERVE arr(lb& TO (ub& - 1)) AS INTEGER + END SUB + + + '' + ' Pop a int from the beginning of a INTEGER array + ' + ' @param INTEGER arr%() array to pop from + ' @param INTEGER var% of int to store popped int + ' + SUB ARR_INT.shift(arr%(), var%) + DIM AS LONG ub, lb, i + lb& = LBOUND(arr%) : ub& = UBOUND(arr%) + var% = arr%(lb&) + FOR i& = lb& TO ub& - 1 + arr%(i&) = arr%(i& + 1) + NEXT i& + REDIM _PRESERVE arr(lb& + 1 TO ub&) AS INTEGER + END SUB + + + '' + ' Copy an array of INTs to another INTEGER array + ' + ' @param INTEGER ARRAY source_arr%() source array to copy + ' @param INTEGER ARRAY dest_arr%() dest array to copy into + ' + SUB ARR_INT.copy(source_arr%(), dest_arr%()) + DIM AS LONG ub, lb, i + lb& = LBOUND(source_arr%) : ub& = UBOUND(source_arr%) + REDIM dest_arr(lb& TO ub&) AS INTEGER + FOR i& = lb& TO ub& + dest_arr%(i&) = source_arr%(i&) + NEXT i& + END SUB + + + '' + ' Push a int into the beginning of a INTEGER array + ' + ' @param INTEGER arr%() array to push into + ' @param INTEGER value% of int to push + ' + SUB ARR_INT.unshift(arr%(), value%) + DIM AS LONG ub, lb, i + lb& = LBOUND(arr%) : ub& = UBOUND(arr%) + DIM work(lb& TO ub&) AS INTEGER + CALL ARR_INT.copy(arr%(), work%()) + REDIM _PRESERVE arr(lb& TO (ub& + 1)) AS INTEGER + FOR i& = lb& + 1 TO ub& + 1 + arr%(i&) = work%(i& - 1) + NEXT i& + arr%(lb&) = value% + END SUB + + + '' + ' Joins an array of INTs as a string + ' + ' @param INTEGER ARRAY arr%() to get as a string + ' @param STRING s$ to store stringified array in + ' + SUB ARR_INT.join(arr%(), s$) + DIM AS LONG ub, lb, i + s$ = "" + lb& = LBOUND(arr%) : ub& = UBOUND(arr%) + FOR i& = lb& TO ub& + s$ = s$ + _TRIM$(STR$(arr%(i&))) + ", " + NEXT i& + ' remove trailing comma + s$ = MID$(s$, 1, (LEN(s$)-LEN(", "))) + END SUB + + + '' + ' Create a new INTEGER array using string of ints seperated by commas + ' + ' @param INTEGER ARRAY arr%() to store the ints in + ' @param STRING s$ string of comma separated ints + ' + SUB ARR_INT.new(arr%(), s$) + DIM AS LONG i, count + DIM t AS STRING + count& = 0 + FOR i& = 1 TO LEN(s$) + IF ASC(s$, i&) = ASC(",") THEN count& = count& + 1 + NEXT i& + REDIM arr(0 TO count&) AS INTEGER + IF count& = 0 THEN + arr%(0) = VAL(s$) + EXIT SUB + END IF + count& = 0 + FOR i& = 1 TO LEN(s$) + t$ = t$ + CHR$(ASC(s$, i&)) + IF ASC(s$, i&) = ASC(",") OR i& = LEN(s$) THEN + arr%(count&) = VAL(t$) + count& = count& + 1 + t$ = "" + END IF + NEXT i& + END SUB + + + '' + ' Return the visually longest element of a INTEGER array + ' + ' @param INTEGER ARRAY arr%() to check in + ' @return INTEGER value of visually longest element + ' + FUNCTION ARR_INT.longest%(arr%()) + DIM AS LONG lb, ub, i, res, lw + lb& = LBOUND(arr%) : ub& = UBOUND(arr%) + res& = 0 : lw& = 0 + FOR i& = lb& TO ub& + IF LEN(_TRIM$(STR$(arr%(i&)))) > lw& THEN + lw& = LEN(_TRIM$(STR$(arr%(i&)))) + res& = i& + END IF + NEXT i& + ARR_INT.longest% = arr%(res&) + END FUNCTION + + + '' + ' Perform some math on every element of a INTEGER array + ' + ' @param INTEGER ARRAY source_arr%() to do math on + ' @param INTEGER ARRAY dest_arr%() to store results in + ' @param STRING op$ one of: + ' and or xor shl shr + ' "+", "-", "*", "\", "&&", "||", "!!", "<<", ">>" + ' @param INTEGER value% to use for operand + ' + SUB ARR_INT.math(source_arr%(), dest_arr%(), op$, value%) + DIM AS LONG lb, ub, i + lb& = LBOUND(source_arr%) : ub& = UBOUND(source_arr%) + REDIM dest_arr(lb& TO ub&) AS INTEGER + FOR i& = lb& TO ub& + SELECT CASE op$ + CASE "+": + dest_arr%(i&) = source_arr%(i&) + value% + CASE "-": + dest_arr%(i&) = source_arr%(i&) - value% + CASE "*": + dest_arr%(i&) = source_arr%(i&) * value% + CASE "\": + IF value% > 0 THEN + dest_arr%(i&) = source_arr%(i&) \ value% + END IF + CASE "&&": + dest_arr%(i&) = source_arr%(i&) AND value% + CASE "||": + dest_arr%(i&) = source_arr%(i&) OR value% + CASE "!!": + dest_arr%(i&) = source_arr%(i&) XOR value% + CASE "<<": + dest_arr%(i&) = _SHL(source_arr%(i&), value%) + CASE ">>": + dest_arr%(i&) = _SHR(source_arr%(i&), value%) + END SELECT + NEXT i& + END SUB + + + '' + ' Return the minimum element value in INTEGER array + ' + ' @param INTEGER ARRAY arr%() to check in + ' @return INTEGER minimum value found + ' + FUNCTION ARR_INT.min%(arr%()) + DIM AS LONG lb, ub, i + DIM AS INTEGER s + lb& = LBOUND(arr%) : ub& = UBOUND(arr%) + s% = 127 + FOR i& = lb& TO ub& + IF arr%(i&) < s% THEN + s% = arr%(i&) + END IF + NEXT i& + ARR_INT.min% = s% + END FUNCTION + + + '' + ' Return the maximum element value in INTEGER array + ' + ' @param INTEGER ARRAY arr%() to check in + ' @return INTEGER maximum value found + ' + FUNCTION ARR_INT.max%(arr%()) + DIM AS LONG lb, ub, i + DIM AS INTEGER s + lb& = LBOUND(arr%) : ub& = UBOUND(arr%) + s% = 0 + FOR i& = lb& TO ub& + IF arr%(i&) > s% THEN + s% = arr%(i&) + END IF + NEXT i& + ARR_INT.max% = s% + END FUNCTION + + + '' + ' Return the visually shortest element of a INTEGER array + ' + ' @param INTEGER ARRAY arr%() to check in + ' @return INTEGER value of visually shortest element + ' + FUNCTION ARR_INT.shortest%(arr%()) + DIM AS LONG lb, ub, i, res, sw + lb& = LBOUND(arr%) : ub& = UBOUND(arr%) + res& = 0 : sw& = 0 + FOR i& = lb& TO ub& + IF LEN(_TRIM$(STR$(arr%(i&)))) < sw& THEN + sw& = LEN(_TRIM$(STR$(arr%(i&)))) + res& = i& + END IF + NEXT i& + ARR_INT.shortest% = arr%(res&) + END FUNCTION + + + '' + ' Return the first element of a INTEGER array + ' + ' @param INTEGER ARRAY arr%() to check in + ' @return INTEGER value of first element + ' + FUNCTION ARR_INT.first%(arr%()) + ARR_INT.first% = arr%(LBOUND(arr%)) + END FUNCTION + + + '' + ' Return the last element of a INTEGER array + ' + ' @param INTEGER ARRAY arr%() to check in + ' @return INTEGER value of last element + ' + FUNCTION ARR_INT.last%(arr%()) + ARR_INT.last% = arr%(UBOUND(arr%)) + END FUNCTION + + + '' + ' Return every nth array element of a INTEGER array + ' + ' @param INTEGER ARRAY source_arr%() to get from + ' @param INTEGER ARRAY dest_arr%() to store in + ' @param INTEGER nth% element + ' + SUB ARR_INT.nth(source_arr%(), dest_arr%(), nth%) + DIM AS LONG lb, ub, i, n + lb& = LBOUND(source_arr%) : ub& = UBOUND(source_arr%) + n& = (ub& - lb&) \ nth% + REDIM dest_arr(n&) AS INTEGER + n& = 0 + FOR i& = lb& to ub& + IF i& MOD nth% = 0 THEN + dest_arr%(n&) = source_arr%(i&) + n& = n& + 1 + END IF + NEXT i& + END SUB + + + '' + ' Checks if value exists in INTEGER array + ' + ' @param INTEGER ARRAY arr%() to check in + ' @param INTEGER value% value to check for + ' @return INTEGER -1 if found or 0 if not found + ' + FUNCTION ARR_INT.in%(arr%(), value%) + DIM AS LONG lb, ub, i + lb& = LBOUND(arr%) : ub& = UBOUND(arr%) + FOR i& = lb& TO ub& + IF arr%(i&) = value% THEN + ARR_INT.in% = -1 + EXIT FUNCTION + END IF + NEXT i& + ARR_INT.in% = 0 + END FUNCTION + + + '' + ' Checks if value exists in INTEGER array and returns index if found + ' + ' @param INTEGER ARRAY arr%() to check in + ' @param INTEGER value% value to check for + ' @return INTEGER index of element if found or -1 if not found + ' + FUNCTION ARR_INT.find%(arr%(), value%) + DIM AS LONG lb, ub, i + lb& = LBOUND(arr%) : ub& = UBOUND(arr%) + FOR i& = lb& TO ub& + IF arr%(i&) = value% THEN + ARR_INT.find% = i& + EXIT FUNCTION + END IF + NEXT i& + ARR_INT.find% = -1 + END FUNCTION + + + '' + ' Return the number of elements in a INTEGER array + ' + ' @param INTEGER ARRAY arr%() to count + ' @return INTEGER number of elements + ' + FUNCTION ARR_INT.count&(arr%()) + ARR_INT.count& = UBOUND(arr%) - LBOUND(arr%) + END FUNCTION + + + '' + ' Return the size of a INTEGER array + ' + ' @param INTEGER ARRAY arr%() to get size of + ' @return LONG size in bytes + ' + FUNCTION ARR_INT.size&(arr%()) + ARR_INT.size& = LEN(arr%()) + END FUNCTION + + + '' + ' Reverses the elements of a INTEGER array + ' + ' @param INTEGER ARRAY source_arr%() to reverse + ' @param INTEGER ARRAY dest_arr%() to store reversed array in + ' + SUB ARR_INT.reverse(source_arr%(), dest_arr%()) + DIM AS LONG lb, ub, i, n + lb& = LBOUND(source_arr%) : ub& = UBOUND(source_arr%) + REDIM dest_arr(0 TO (ub& - lb&)) AS INTEGER + n& = 0 + FOR i& = ub& TO lb& STEP -1 + dest_arr%(n&) = source_arr%(i&) + n& = n& + 1 + NEXT i& + END SUB + + + '' + ' Returns a random int from a INTEGER array + ' + ' @param INTEGER ARRAY arr%() array to get random element from + ' @return INTEGER random element + ' + FUNCTION ARR_INT.random%(arr%()) + DIM AS LONG lb, ub + lb& = LBOUND(arr%) : ub& = UBOUND(arr%) + RANDOMIZE TIMER + ARR_INT.random% = arr%(INT(RND * (ub& - lb&)) + 1) + END FUNCTION + + + '' + ' Returns the sum of all elements in a INTEGER array + ' + ' @param INTEGER ARRAY arr%() array to get some for + ' @return LONG sum of all elements + ' + FUNCTION ARR_INT.sum&(arr%()) + DIM AS LONG lb, ub, i + DIM sum AS LONG + lb& = LBOUND(arr%) : ub& = UBOUND(arr%) + FOR i& = lb& TO ub& + sum& = sum& + arr%(i&) + NEXT i& + ARR_INT.sum& = sum& + END FUNCTION + + + '' + ' Returns the average value of elements in a INTEGER array + ' + ' @param INTEGER ARRAY arr%() array to get average for + ' @return LONG average of elements + ' + FUNCTION ARR_INT.avg&(arr%()) + DIM AS LONG lb, ub, i + DIM sum AS LONG + lb& = LBOUND(arr%) : ub& = UBOUND(arr%) + FOR i& = lb& TO ub& + sum& = sum& + arr%(i&) + NEXT i& + ARR_INT.avg& = sum& / (ub& - lb&) + END FUNCTION + + + '' + ' Shuffle the elements of a INTEGER array + ' + ' @param INTEGER ARRAY source_arr%() to shuffle + ' @param INTEGER ARRAY dest_arr%() to store shuffled array in + ' + SUB ARR_INT.shuffle(source_arr%(), dest_arr%()) + DIM AS LONG lb, ub, i, count + lb& = LBOUND(source_arr%) : ub& = UBOUND(source_arr%) + count& = ub& - lb& + REDIM dest_arr(0 TO count&) AS INTEGER + CALL ARR_INT.copy(source_arr%(), dest_arr%()) + RANDOMIZE TIMER + FOR i& = 0 TO count& + SWAP dest_arr%(i&), dest_arr%(lb& + RND * (ub& - lb&)) + NEXT i& + END SUB + + + '' + ' Makes a INTEGER array contain only unique values + ' + ' @param INTEGER ARRAY source_arr%() array to get uniques for + ' @param INTEGER ARRAY dest_arr%() array to store uniques in + ' + SUB ARR_INT.unique(source_arr%(), dest_arr%()) + DIM AS LONG lb, ub, i + DIM tmp AS INTEGER + lb& = LBOUND(source_arr%) : ub& = UBOUND(source_arr%) + DIM work_arr(0) AS INTEGER + FOR i& = lb& TO ub& + IF NOT ARR_INT.in%(work_arr%(), source_arr%(i&)) THEN + CALL ARR_INT.push(work_arr%(), source_arr%(i&)) + END IF + NEXT i& + CALL ARR_INT.shift(work_arr%(), tmp%) + CALL ARR_INT.copy(work_arr%(), dest_arr%()) + END SUB + + + '' + ' Filters a INTEGER array to only elements greater than value + ' + ' @param INTEGER ARRAY source_arr%() array to work on + ' @param INTEGER ARRAY dest_arr%() array to store in + ' @param INTEGER value% to be greater than to be returned + ' + SUB ARR_INT.gt(source_arr%(), dest_arr%(), value%) + DIM AS LONG lb, ub, i + DIM tmp AS INTEGER + lb& = LBOUND(source_arr%) : ub& = UBOUND(source_arr%) + DIM work_arr(0) AS INTEGER + FOR i& = lb& TO ub& + IF source_arr%(i&) > value% THEN + CALL ARR_INT.push(work_arr%(), source_arr%(i&)) + END IF + NEXT i& + CALL ARR_INT.shift(work_arr%(), tmp%) + CALL ARR_INT.copy(work_arr%(), dest_arr%()) + END SUB + + + '' + ' Filters a INTEGER array to only elements greater than or equal to value + ' + ' @param INTEGER ARRAY source_arr%() array to work on + ' @param INTEGER ARRAY dest_arr%() array to store in + ' @param INTEGER value% to be greater than or equal to be returned + ' + SUB ARR_INT.gte(source_arr%(), dest_arr%(), value%) + DIM AS LONG lb, ub, i + DIM tmp AS INTEGER + lb& = LBOUND(source_arr%) : ub& = UBOUND(source_arr%) + DIM work_arr(0) AS INTEGER + FOR i& = lb& TO ub& + IF source_arr%(i&) >= value% THEN + CALL ARR_INT.push(work_arr%(), source_arr%(i&)) + END IF + NEXT i& + CALL ARR_INT.shift(work_arr%(), tmp%) + CALL ARR_INT.copy(work_arr%(), dest_arr%()) + END SUB + + + '' + ' Filters a INTEGER array to only elements less than value + ' + ' @param INTEGER ARRAY source_arr%() array to work on + ' @param INTEGER ARRAY dest_arr%() array to store in + ' @param INTEGER value% to be less than to be returned + ' + SUB ARR_INT.lt(source_arr%(), dest_arr%(), value%) + DIM AS LONG lb, ub, i + DIM tmp AS INTEGER + lb& = LBOUND(source_arr%) : ub& = UBOUND(source_arr%) + DIM work_arr(0) AS INTEGER + FOR i& = lb& TO ub& + IF source_arr%(i&) < value% THEN + CALL ARR_INT.push(work_arr%(), source_arr%(i&)) + END IF + NEXT i& + CALL ARR_INT.shift(work_arr%(), tmp%) + CALL ARR_INT.copy(work_arr%(), dest_arr%()) + END SUB + + + '' + ' Filters a INTEGER array to only elements less than or equal to value + ' + ' @param INTEGER ARRAY source_arr%() array to work on + ' @param INTEGER ARRAY dest_arr%() array to store in + ' @param INTEGER value% to be less than or equal to be returned + ' + SUB ARR_INT.lte(source_arr%(), dest_arr%(), value%) + DIM AS LONG lb, ub, i + DIM tmp AS INTEGER + lb& = LBOUND(source_arr%) : ub& = UBOUND(source_arr%) + DIM work_arr(0) AS INTEGER + FOR i& = lb& TO ub& + IF source_arr%(i&) <= value% THEN + CALL ARR_INT.push(work_arr%(), source_arr%(i&)) + END IF + NEXT i& + CALL ARR_INT.shift(work_arr%(), tmp%) + CALL ARR_INT.copy(work_arr%(), dest_arr%()) + END SUB + + + '' + ' Finds and replaces values across all elements in a INTEGER ARRAY + ' + ' @param INTEGER ARRAY arr%() to check in + ' @param INTEGER find% value to find + ' @param INTEGER replace% value to replace with if found + ' + SUB ARR_INT.replace(arr%(), find%, replace%) + DIM AS LONG lb, ub, i + lb& = LBOUND(arr%) : ub& = UBOUND(arr%) + FOR i& = lb& TO ub& + IF arr%(i&) = find% THEN + arr%(i&) = replace% + END IF + NEXT i& + END SUB + + + '' + ' Inserts a new element into INTEGER array after index + ' + ' @param INTEGER ARRAY arr%() array to work on + ' @param INTEGER value% to insert + ' @param INTEGER index% of element to insert at + ' + SUB ARR_INT.insert(arr%(), value%, index%) + DIM AS LONG lb, ub, i + DIM tmp AS INTEGER + DIM work_arr(0) AS INTEGER + lb& = LBOUND(arr%) : ub& = UBOUND(arr%) + IF index% >= lb& AND index% <= ub& THEN + ' build new array up to index + FOR i& = lb& TO index% + CALL ARR_INT.push(work_arr%(), arr%(i&)) + NEXT i& + ' insert new element + CALL ARR_INT.push(work_arr%(), value%) + ' finish building array from index + 1 + FOR i& = index% + 1 TO ub& + CALL ARR_INT.push(work_arr%(), arr%(i&)) + NEXT i& + CALL ARR_INT.shift(work_arr%(), tmp%) + CALL ARR_INT.copy(work_arr%(), arr%()) + END IF + END SUB + + + '' + ' Removes element from a INTEGER array by element index + ' + ' @param INTEGER ARRAY arr%() array to work on + ' @param INTEGER index% of element to remove + ' + SUB ARR_INT.remove(arr%(), index%) + DIM AS LONG lb, ub, i + DIM tmp AS INTEGER + DIM work_arr(0) AS INTEGER + lb& = LBOUND(arr%) : ub& = UBOUND(arr%) + IF index% >= lb& AND index% <= ub& THEN + ' build new array up to index + FOR i& = lb& TO index% - 1 + CALL ARR_INT.push(work_arr%(), arr%(i&)) + NEXT i& + ' skip elements + FOR i& = index% + 1 TO ub& + CALL ARR_INT.push(work_arr%(), arr%(i&)) + NEXT i& + CALL ARR_INT.shift(work_arr%(), tmp%) + CALL ARR_INT.copy(work_arr%(), arr%()) + END IF + END SUB + + + '' + ' Filters a INTEGER array to only elements that have odd values + ' + ' @param INTEGER ARRAY source_arr%() array to work on + ' @param INTEGER ARRAY dest_arr%() array to store in + ' + SUB ARR_INT.odd(source_arr%(), dest_arr%()) + DIM AS LONG lb, ub, i + DIM tmp AS INTEGER + lb& = LBOUND(source_arr%) : ub& = UBOUND(source_arr%) + DIM work_arr(0) AS INTEGER + FOR i& = lb& TO ub& + IF source_arr%(i&) MOD 2 <> 0 THEN + CALL ARR_INT.push(work_arr%(), source_arr%(i&)) + END IF + NEXT i& + CALL ARR_INT.shift(work_arr%(), tmp%) + CALL ARR_INT.copy(work_arr%(), dest_arr%()) + END SUB + + + '' + ' Filters a INTEGER array to only elements that have even values + ' + ' @param INTEGER ARRAY source_arr%() array to work on + ' @param INTEGER ARRAY dest_arr%() array to store in + ' + SUB ARR_INT.even(source_arr%(), dest_arr%()) + DIM AS LONG lb, ub, i + DIM tmp AS INTEGER + lb& = LBOUND(source_arr%) : ub& = UBOUND(source_arr%) + DIM work_arr(0) AS INTEGER + FOR i& = lb& TO ub& + IF source_arr%(i&) MOD 2 = 0 THEN + CALL ARR_INT.push(work_arr%(), source_arr%(i&)) + END IF + NEXT i& + CALL ARR_INT.shift(work_arr%(), tmp%) + CALL ARR_INT.copy(work_arr%(), dest_arr%()) + END SUB + + + '' + ' Filters a INTEGER array to only elements that have values evenly divisible by divisor + ' + ' @param INTEGER ARRAY source_arr%() array to work on + ' @param INTEGER ARRAY dest_arr%() array to store in + ' @param INTEGER divisor% for modulo + ' + SUB ARR_INT.mod(source_arr%(), dest_arr%(), divisor%) + DIM AS LONG lb, ub, i + DIM tmp AS INTEGER + lb& = LBOUND(source_arr%) : ub& = UBOUND(source_arr%) + DIM work_arr(0) AS INTEGER + FOR i& = lb& TO ub& + IF source_arr%(i&) MOD divisor% = 0 THEN + CALL ARR_INT.push(work_arr%(), source_arr%(i&)) + END IF + NEXT i& + CALL ARR_INT.shift(work_arr%(), tmp%) + CALL ARR_INT.copy(work_arr%(), dest_arr%()) + END SUB + + + '' + ' Filters a INTEGER array to only elements between min and max + ' + ' @param INTEGER ARRAY source_arr%() array to work on + ' @param INTEGER ARRAY dest_arr%() array to store in + ' @param INTEGER min% to be greater than or equal to be returned + ' @param INTEGER max% to be less than or equal to be returned + ' + SUB ARR_INT.between(source_arr%(), dest_arr%(), min%, max%) + DIM AS LONG lb, ub, i + DIM tmp AS INTEGER + lb& = LBOUND(source_arr%) : ub& = UBOUND(source_arr%) + DIM work_arr(0) AS INTEGER + FOR i& = lb& TO ub& + IF source_arr%(i&) >= min% _ + AND source_arr%(i&) <= max% THEN + CALL ARR_INT.push(work_arr%(), source_arr%(i&)) + END IF + NEXT i& + CALL ARR_INT.shift(work_arr%(), tmp%) + CALL ARR_INT.copy(work_arr%(), dest_arr%()) + END SUB + + + '' + ' Sorts INTEGER array in ascending order + ' + ' @param INTEGER ARRAY source_arr%() array to sort + ' @param INTEGER ARRAY dest_arr%() array to store sorted in + ' + SUB ARR_INT.sort(source_arr%(), dest_arr%()) + DIM AS LONG lb, ub, count + lb& = LBOUND(source_arr%) : ub& = UBOUND(source_arr%) + count& = ub& - lb& + REDIM dest_arr(0 TO count&) AS INTEGER + CALL ARR_INT.copy(source_arr%(), dest_arr%()) + CALL ARR_INT.quicksort(dest_arr%(), lb&, ub&, 0) + END SUB + + + '' + ' Sorts INTEGER array in descending order + ' + ' @param INTEGER ARRAY source_arr%() array to sort + ' @param INTEGER ARRAY dest_arr%() array to store sorted in + ' + SUB ARR_INT.rsort(source_arr%(), dest_arr%()) + DIM AS LONG lb, ub, count + lb& = LBOUND(source_arr%) : ub& = UBOUND(source_arr%) + count& = ub& - lb& + REDIM dest_arr(0 TO count&) AS INTEGER + CALL ARR_INT.copy(source_arr%(), dest_arr%()) + CALL ARR_INT.quicksort(dest_arr%(), lb&, ub&, 1) + END SUB + + + '' + ' Quicksort array with pivot algorithm by logiclrd + ' + ' @link https://www.tek-tips.com/faqs.cfm?fid=336 + ' @param INTEGER ARRAY array%() to sort + ' @param INTEGER start% of range to sort + ' @param INTEGER finish% range of sort + ' @param INTEGER order% to sort by (0 = asc / 1 = desc) + ' + SUB ARR_INT.quicksort(arr%(), start%, finish%, order%) + DIM i AS LONG + DIM pivot AS INTEGER + DIM pivotvalue AS INTEGER + + 'first, partition the array + pivot% = start% + pivotvalue% = arr%(pivot%) + FOR i& = start% + 1 TO finish% + IF order% = 0 THEN ' ascending order + IF arr%(i&) < pivotvalue% THEN + arr%(pivot%) = arr%(i&) + arr%(i&) = arr%(pivot% + 1) + arr%(pivot% + 1) = pivotvalue% + pivot% = pivot% + 1 + END IF + ELSEIF order% = 1 THEN ' descending order + IF arr%(i&) > pivotvalue% THEN + arr%(pivot%) = arr%(i&) + arr%(i&) = arr%(pivot% + 1) + arr%(pivot% + 1) = pivotvalue% + pivot% = pivot% + 1 + END IF + END IF + NEXT i& + + 'then, sort the subarrays to each side of the pivot + IF pivot% - start% >= 2 THEN + CALL ARR_INT.quicksort(arr%(), start%, pivot% - 1, order%) + END IF + IF finish% - pivot% >= 2 THEN + CALL ARR_INT.quicksort(arr%(), pivot% + 1, finish%, order%) + END IF + END SUB + $END IF + $IF GJ_LIB_ARR_INT64_BAS = UNDEFINED THEN + '$DYNAMIC + $LET GJ_LIB_ARR_INT64_BAS = 1 + + '' + ' Slice an array from source to destination starting at index and count slices + ' + ' @param _INTEGER64() source_arr&& to slice from + ' @param _INTEGER64() dest_arr&& to put slices into + ' @param INTEGER start_idx% starting index to use as slice range + ' @param INTEGER count% number of slices - if negative, backwards from index + ' + SUB ARR_INT64.slice(source_arr&&(), dest_arr&&(), start_idx%, count%) + DIM AS LONG ub, lb, i, n + lb& = LBOUND(source_arr&&) : ub& = UBOUND(source_arr&&) + IF start_idx% < lb& OR start_idx% + count% > ub& THEN EXIT SUB ' out of range + IF ub& - lb& < count% THEN EXIT SUB ' too many and not enough + REDIM dest_arr(0 TO ABS(count%)) AS _INTEGER64 + IF SGN(count%) = -1 THEN + IF ((start_idx% - 1) - ABS(count%)) < 0 THEN EXIT SUB ' out of range + n& = 0 + FOR i& = (start_idx% - 1) TO ((start_idx% - 1) - ABS(count%)) STEP -1 + dest_arr&&(n&) = source_arr&&(i&) + n& = n& + 1 + NEXT i& + ELSE + IF ((start_idx% + 1) + ABS(count%)) > (ub& - lb&) THEN EXIT SUB ' out of range + n& = 0 + FOR i& = start_idx% + 1 TO ((start_idx% + 1) + count%) STEP 1 + dest_arr&&(n&) = source_arr&&(i&) + n& = n& + 1 + NEXT i& + END IF + END SUB + + + '' + ' Push a int64 onto the end of a _INTEGER64 array + ' + ' @param _INTEGER64 arr&&() array to push into + ' @param _INTEGER64 value&& of byte to push + ' + SUB ARR_INT64.push(arr&&(), value&&) + DIM AS LONG ub, lb + lb& = LBOUND(arr&&) : ub& = UBOUND(arr&&) + REDIM _PRESERVE arr(lb& TO (ub& + 1)) AS _INTEGER64 + arr&&(ub& + 1) = value&& + END SUB + + + '' + ' Pop a int64 from the end of a _INTEGER64 array + ' + ' @param _INTEGER64 arr&&() array to pop from + ' @param _INTEGER64 var&& of int64 to store popped int64 + ' + SUB ARR_INT64.pop(arr&&(), var&&) + DIM AS LONG ub, lb + lb& = LBOUND(arr&&) : ub& = UBOUND(arr&&) + var&& = arr&&(ub&) + REDIM _PRESERVE arr(lb& TO (ub& - 1)) AS _INTEGER64 + END SUB + + + '' + ' Pop a int64 from the beginning of a _INTEGER64 array + ' + ' @param _INTEGER64 arr&&() array to pop from + ' @param _INTEGER64 var&& of int64 to store popped int64 + ' + SUB ARR_INT64.shift(arr&&(), var&&) + DIM AS LONG ub, lb, i + lb& = LBOUND(arr&&) : ub& = UBOUND(arr&&) + var&& = arr&&(lb&) + FOR i& = lb& TO ub& - 1 + arr&&(i&) = arr&&(i& + 1) + NEXT i& + REDIM _PRESERVE arr(lb& + 1 TO ub&) AS _INTEGER64 + END SUB + + + '' + ' Copy an array of INT64s to another _INTEGER64 array + ' + ' @param _INTEGER64 ARRAY source_arr&&() source array to copy + ' @param _INTEGER64 ARRAY dest_arr&&() dest array to copy into + ' + SUB ARR_INT64.copy(source_arr&&(), dest_arr&&()) + DIM AS LONG ub, lb, i + lb& = LBOUND(source_arr&&) : ub& = UBOUND(source_arr&&) + REDIM dest_arr(lb& TO ub&) AS _INTEGER64 + FOR i& = lb& TO ub& + dest_arr&&(i&) = source_arr&&(i&) + NEXT i& + END SUB + + + '' + ' Push a int64 into the beginning of a _INTEGER64 array + ' + ' @param _INTEGER64 arr&&() array to push into + ' @param _INTEGER64 value&& of int64 to push + ' + SUB ARR_INT64.unshift(arr&&(), value&&) + DIM AS LONG ub, lb, i + lb& = LBOUND(arr&&) : ub& = UBOUND(arr&&) + DIM work(lb& TO ub&) AS _INTEGER64 + CALL ARR_INT64.copy(arr&&(), work&&()) + REDIM _PRESERVE arr(lb& TO (ub& + 1)) AS _INTEGER64 + FOR i& = lb& + 1 TO ub& + 1 + arr&&(i&) = work&&(i& - 1) + NEXT i& + arr&&(lb&) = value&& + END SUB + + + '' + ' Joins an array of INT64s as a string + ' + ' @param _INTEGER64 ARRAY arr&&() to get as a string + ' @param STRING s$ to store stringified array in + ' + SUB ARR_INT64.join(arr&&(), s$) + DIM AS LONG ub, lb, i + s$ = "" + lb& = LBOUND(arr&&) : ub& = UBOUND(arr&&) + FOR i& = lb& TO ub& + s$ = s$ + _TRIM$(STR$(arr&&(i&))) + ", " + NEXT i& + ' remove trailing comma + s$ = MID$(s$, 1, (LEN(s$)-LEN(", "))) + END SUB + + + '' + ' Create a new _INTEGER64 array using string of int64s seperated by commas + ' + ' @param _INTEGER64 ARRAY arr&&() to store the int64s in + ' @param STRING s$ string of comma separated int64s + ' + SUB ARR_INT64.new(arr&&(), s$) + DIM AS LONG i, count + DIM t AS STRING + count& = 0 + FOR i& = 1 TO LEN(s$) + IF ASC(s$, i&) = ASC(",") THEN count& = count& + 1 + NEXT i& + REDIM arr(0 TO count&) AS _INTEGER64 + IF count& = 0 THEN + arr&&(0) = VAL(s$) + EXIT SUB + END IF + count& = 0 + FOR i& = 1 TO LEN(s$) + t$ = t$ + CHR$(ASC(s$, i&)) + IF ASC(s$, i&) = ASC(",") OR i& = LEN(s$) THEN + arr&&(count&) = VAL(t$) + count& = count& + 1 + t$ = "" + END IF + NEXT i& + END SUB + + + '' + ' Return the visually longest element of a _INTEGER64 array + ' + ' @param _INTEGER64 ARRAY arr&&() to check in + ' @return _INTEGER64 value of visually longest element + ' + FUNCTION ARR_INT64.longest&&(arr&&()) + DIM AS LONG lb, ub, i, res, lw + lb& = LBOUND(arr&&) : ub& = UBOUND(arr&&) + res& = 0 : lw& = 0 + FOR i& = lb& TO ub& + IF LEN(_TRIM$(STR$(arr&&(i&)))) > lw& THEN + lw& = LEN(_TRIM$(STR$(arr&&(i&)))) + res& = i& + END IF + NEXT i& + ARR_INT64.longest&& = arr&&(res&) + END FUNCTION + + + '' + ' Perform some math on every element of a _INTEGER64 array + ' + ' @param _INTEGER64 ARRAY source_arr&&() to do math on + ' @param _INTEGER64 ARRAY dest_arr&&() to store results in + ' @param STRING op$ one of: + ' and or xor shl shr + ' "+", "-", "*", "\", "&&", "||", "!!", "<<", ">>" + ' @param _INTEGER64 value&& to use for operand + ' + SUB ARR_INT64.math(source_arr&&(), dest_arr&&(), op$, value&&) + DIM AS LONG lb, ub, i + lb& = LBOUND(source_arr&&) : ub& = UBOUND(source_arr&&) + REDIM dest_arr(lb& TO ub&) AS _INTEGER64 + FOR i& = lb& TO ub& + SELECT CASE op$ + CASE "+": + dest_arr&&(i&) = source_arr&&(i&) + value&& + CASE "-": + dest_arr&&(i&) = source_arr&&(i&) - value&& + CASE "*": + dest_arr&&(i&) = source_arr&&(i&) * value&& + CASE "\": + IF value&& > 0 THEN + dest_arr&&(i&) = source_arr&&(i&) \ value&& + END IF + CASE "&&": + dest_arr&&(i&) = source_arr&&(i&) AND value&& + CASE "||": + dest_arr&&(i&) = source_arr&&(i&) OR value&& + CASE "!!": + dest_arr&&(i&) = source_arr&&(i&) XOR value&& + CASE "<<": + dest_arr&&(i&) = _SHL(source_arr&&(i&), value&&) + CASE ">>": + dest_arr&&(i&) = _SHR(source_arr&&(i&), value&&) + END SELECT + NEXT i& + END SUB + + + '' + ' Return the minimum element value in _INTEGER64 array + ' + ' @param _INTEGER64 ARRAY arr&&() to check in + ' @return _INTEGER64 minimum value found + ' + FUNCTION ARR_INT64.min&&(arr&&()) + DIM AS LONG lb, ub, i + DIM AS _INTEGER64 s + lb& = LBOUND(arr&&) : ub& = UBOUND(arr&&) + s&& = 127 + FOR i& = lb& TO ub& + IF arr&&(i&) < s&& THEN + s&& = arr&&(i&) + END IF + NEXT i& + ARR_INT64.min&& = s&& + END FUNCTION + + + '' + ' Return the maximum element value in _INTEGER64 array + ' + ' @param _INTEGER64 ARRAY arr&&() to check in + ' @return _INTEGER64 maximum value found + ' + FUNCTION ARR_INT64.max&&(arr&&()) + DIM AS LONG lb, ub, i + DIM AS _INTEGER64 s + lb& = LBOUND(arr&&) : ub& = UBOUND(arr&&) + s&& = 0 + FOR i& = lb& TO ub& + IF arr&&(i&) > s&& THEN + s&& = arr&&(i&) + END IF + NEXT i& + ARR_INT64.max&& = s&& + END FUNCTION + + + '' + ' Return the visually shortest element of a _INTEGER64 array + ' + ' @param _INTEGER64 ARRAY arr&&() to check in + ' @return _INTEGER64 value of visually shortest element + ' + FUNCTION ARR_INT64.shortest&&(arr&&()) + DIM AS LONG lb, ub, i, res, sw + lb& = LBOUND(arr&&) : ub& = UBOUND(arr&&) + res& = 0 : sw& = 0 + FOR i& = lb& TO ub& + IF LEN(_TRIM$(STR$(arr&&(i&)))) < sw& THEN + sw& = LEN(_TRIM$(STR$(arr&&(i&)))) + res& = i& + END IF + NEXT i& + ARR_INT64.shortest&& = arr&&(res&) + END FUNCTION + + + '' + ' Return the first element of a _INTEGER64 array + ' + ' @param _INTEGER64 ARRAY arr&&() to check in + ' @return _INTEGER64 value of first element + ' + FUNCTION ARR_INT64.first&&(arr&&()) + ARR_INT64.first&& = arr&&(LBOUND(arr&&)) + END FUNCTION + + + '' + ' Return the last element of a _INTEGER64 array + ' + ' @param _INTEGER64 ARRAY arr&&() to check in + ' @return _INTEGER64 value of last element + ' + FUNCTION ARR_INT64.last&&(arr&&()) + ARR_INT64.last&& = arr&&(UBOUND(arr&&)) + END FUNCTION + + + '' + ' Return every nth array element of a _INTEGER64 array + ' + ' @param _INTEGER64 ARRAY source_arr&&() to get from + ' @param _INTEGER64 ARRAY dest_arr&&() to store in + ' @param INTEGER nth% element + ' + SUB ARR_INT64.nth(source_arr&&(), dest_arr&&(), nth%) + DIM AS LONG lb, ub, i, n + lb& = LBOUND(source_arr&&) : ub& = UBOUND(source_arr&&) + n& = (ub& - lb&) \ nth% + REDIM dest_arr(n&) AS _INTEGER64 + n& = 0 + FOR i& = lb& to ub& + IF i& MOD nth% = 0 THEN + dest_arr&&(n&) = source_arr&&(i&) + n& = n& + 1 + END IF + NEXT i& + END SUB + + + '' + ' Checks if value exists in _INTEGER64 array + ' + ' @param _INTEGER64 ARRAY arr&&() to check in + ' @param _INTEGER64 value&& value to check for + ' @return INTEGER -1 if found or 0 if not found + ' + FUNCTION ARR_INT64.in%(arr&&(), value&&) + DIM AS LONG lb, ub, i + lb& = LBOUND(arr&&) : ub& = UBOUND(arr&&) + FOR i& = lb& TO ub& + IF arr&&(i&) = value&& THEN + ARR_INT64.in% = -1 + EXIT FUNCTION + END IF + NEXT i& + ARR_INT64.in% = 0 + END FUNCTION + + + '' + ' Checks if value exists in _INTEGER64 array and returns index if found + ' + ' @param _INTEGER64 ARRAY arr&&() to check in + ' @param _INTEGER64 value&& value to check for + ' @return INTEGER index of element if found or -1 if not found + ' + FUNCTION ARR_INT64.find%(arr&&(), value&&) + DIM AS LONG lb, ub, i + lb& = LBOUND(arr&&) : ub& = UBOUND(arr&&) + FOR i& = lb& TO ub& + IF arr&&(i&) = value&& THEN + ARR_INT64.find% = i& + EXIT FUNCTION + END IF + NEXT i& + ARR_INT64.find% = -1 + END FUNCTION + + + '' + ' Return the number of elements in a _INTEGER64 array + ' + ' @param _INTEGER64 ARRAY arr&&() to count + ' @return INTEGER number of elements + ' + FUNCTION ARR_INT64.count&(arr&&()) + ARR_INT64.count& = UBOUND(arr&&) - LBOUND(arr&&) + END FUNCTION + + + '' + ' Return the size of a _INTEGER64 array + ' + ' @param _INTEGER64 ARRAY arr&&() to get size of + ' @return LONG size in bytes + ' + FUNCTION ARR_INT64.size&(arr&&()) + ARR_INT64.size& = LEN(arr&&()) + END FUNCTION + + + '' + ' Reverses the elements of a _INTEGER64 array + ' + ' @param _INTEGER64 ARRAY source_arr&&() to reverse + ' @param _INTEGER64 ARRAY dest_arr&&() to store reversed array in + ' + SUB ARR_INT64.reverse(source_arr&&(), dest_arr&&()) + DIM AS LONG lb, ub, i, n + lb& = LBOUND(source_arr&&) : ub& = UBOUND(source_arr&&) + REDIM dest_arr(0 TO (ub& - lb&)) AS _INTEGER64 + n& = 0 + FOR i& = ub& TO lb& STEP -1 + dest_arr&&(n&) = source_arr&&(i&) + n& = n& + 1 + NEXT i& + END SUB + + + '' + ' Returns a random int64 from a _INTEGER64 array + ' + ' @param _INTEGER64 ARRAY arr&&() array to get random element from + ' @return _INTEGER64 random element + ' + FUNCTION ARR_INT64.random&&(arr&&()) + DIM AS LONG lb, ub + lb& = LBOUND(arr&&) : ub& = UBOUND(arr&&) + RANDOMIZE TIMER + ARR_INT64.random&& = arr&&(INT(RND * (ub& - lb&)) + 1) + END FUNCTION + + + '' + ' Returns the sum of all elements in a _INTEGER64 array + ' + ' @param _INTEGER64 ARRAY arr&&() array to get some for + ' @return LONG sum of all elements + ' + FUNCTION ARR_INT64.sum&(arr&&()) + DIM AS LONG lb, ub, i + DIM sum AS LONG + lb& = LBOUND(arr&&) : ub& = UBOUND(arr&&) + FOR i& = lb& TO ub& + sum& = sum& + arr&&(i&) + NEXT i& + ARR_INT64.sum& = sum& + END FUNCTION + + + '' + ' Returns the average value of elements in a _INTEGER64 array + ' + ' @param _INTEGER64 ARRAY arr&&() array to get average for + ' @return LONG average of elements + ' + FUNCTION ARR_INT64.avg&(arr&&()) + DIM AS LONG lb, ub, i + DIM sum AS LONG + lb& = LBOUND(arr&&) : ub& = UBOUND(arr&&) + FOR i& = lb& TO ub& + sum& = sum& + arr&&(i&) + NEXT i& + ARR_INT64.avg& = sum& / (ub& - lb&) + END FUNCTION + + + '' + ' Shuffle the elements of a _INTEGER64 array + ' + ' @param _INTEGER64 ARRAY source_arr&&() to shuffle + ' @param _INTEGER64 ARRAY dest_arr&&() to store shuffled array in + ' + SUB ARR_INT64.shuffle(source_arr&&(), dest_arr&&()) + DIM AS LONG lb, ub, i, count + lb& = LBOUND(source_arr&&) : ub& = UBOUND(source_arr&&) + count& = ub& - lb& + REDIM dest_arr(0 TO count&) AS _INTEGER64 + CALL ARR_INT64.copy(source_arr&&(), dest_arr&&()) + RANDOMIZE TIMER + FOR i& = 0 TO count& + SWAP dest_arr&&(i&), dest_arr&&(lb& + RND * (ub& - lb&)) + NEXT i& + END SUB + + + '' + ' Makes a _INTEGER64 array contain only unique values + ' + ' @param _INTEGER64 ARRAY source_arr&&() array to get uniques for + ' @param _INTEGER64 ARRAY dest_arr&&() array to store uniques in + ' + SUB ARR_INT64.unique(source_arr&&(), dest_arr&&()) + DIM AS LONG lb, ub, i + DIM tmp AS _INTEGER64 + lb& = LBOUND(source_arr&&) : ub& = UBOUND(source_arr&&) + DIM work_arr(0) AS _INTEGER64 + FOR i& = lb& TO ub& + IF NOT ARR_INT64.in%(work_arr&&(), source_arr&&(i&)) THEN + CALL ARR_INT64.push(work_arr&&(), source_arr&&(i&)) + END IF + NEXT i& + CALL ARR_INT64.shift(work_arr&&(), tmp&&) + CALL ARR_INT64.copy(work_arr&&(), dest_arr&&()) + END SUB + + + '' + ' Filters a _INTEGER64 array to only elements greater than value + ' + ' @param _INTEGER64 ARRAY source_arr&&() array to work on + ' @param _INTEGER64 ARRAY dest_arr&&() array to store in + ' @param _INTEGER64 value&& to be greater than to be returned + ' + SUB ARR_INT64.gt(source_arr&&(), dest_arr&&(), value&&) + DIM AS LONG lb, ub, i + DIM tmp AS _INTEGER64 + lb& = LBOUND(source_arr&&) : ub& = UBOUND(source_arr&&) + DIM work_arr(0) AS _INTEGER64 + FOR i& = lb& TO ub& + IF source_arr&&(i&) > value&& THEN + CALL ARR_INT64.push(work_arr&&(), source_arr&&(i&)) + END IF + NEXT i& + CALL ARR_INT64.shift(work_arr&&(), tmp&&) + CALL ARR_INT64.copy(work_arr&&(), dest_arr&&()) + END SUB + + + '' + ' Filters a _INTEGER64 array to only elements greater than or equal to value + ' + ' @param _INTEGER64 ARRAY source_arr&&() array to work on + ' @param _INTEGER64 ARRAY dest_arr&&() array to store in + ' @param _INTEGER64 value&& to be greater than or equal to be returned + ' + SUB ARR_INT64.gte(source_arr&&(), dest_arr&&(), value&&) + DIM AS LONG lb, ub, i + DIM tmp AS _INTEGER64 + lb& = LBOUND(source_arr&&) : ub& = UBOUND(source_arr&&) + DIM work_arr(0) AS _INTEGER64 + FOR i& = lb& TO ub& + IF source_arr&&(i&) >= value&& THEN + CALL ARR_INT64.push(work_arr&&(), source_arr&&(i&)) + END IF + NEXT i& + CALL ARR_INT64.shift(work_arr&&(), tmp&&) + CALL ARR_INT64.copy(work_arr&&(), dest_arr&&()) + END SUB + + + '' + ' Filters a _INTEGER64 array to only elements less than value + ' + ' @param _INTEGER64 ARRAY source_arr&&() array to work on + ' @param _INTEGER64 ARRAY dest_arr&&() array to store in + ' @param _INTEGER64 value&& to be less than to be returned + ' + SUB ARR_INT64.lt(source_arr&&(), dest_arr&&(), value&&) + DIM AS LONG lb, ub, i + DIM tmp AS _INTEGER64 + lb& = LBOUND(source_arr&&) : ub& = UBOUND(source_arr&&) + DIM work_arr(0) AS _INTEGER64 + FOR i& = lb& TO ub& + IF source_arr&&(i&) < value&& THEN + CALL ARR_INT64.push(work_arr&&(), source_arr&&(i&)) + END IF + NEXT i& + CALL ARR_INT64.shift(work_arr&&(), tmp&&) + CALL ARR_INT64.copy(work_arr&&(), dest_arr&&()) + END SUB + + + '' + ' Filters a _INTEGER64 array to only elements less than or equal to value + ' + ' @param _INTEGER64 ARRAY source_arr&&() array to work on + ' @param _INTEGER64 ARRAY dest_arr&&() array to store in + ' @param _INTEGER64 value&& to be less than or equal to be returned + ' + SUB ARR_INT64.lte(source_arr&&(), dest_arr&&(), value&&) + DIM AS LONG lb, ub, i + DIM tmp AS _INTEGER64 + lb& = LBOUND(source_arr&&) : ub& = UBOUND(source_arr&&) + DIM work_arr(0) AS _INTEGER64 + FOR i& = lb& TO ub& + IF source_arr&&(i&) <= value&& THEN + CALL ARR_INT64.push(work_arr&&(), source_arr&&(i&)) + END IF + NEXT i& + CALL ARR_INT64.shift(work_arr&&(), tmp&&) + CALL ARR_INT64.copy(work_arr&&(), dest_arr&&()) + END SUB + + + '' + ' Finds and replaces values across all elements in a _INTEGER64 ARRAY + ' + ' @param _INTEGER64 ARRAY arr&&() to check in + ' @param _INTEGER64 find&& value to find + ' @param _INTEGER64 replace&& value to replace with if found + ' + SUB ARR_INT64.replace(arr&&(), find&&, replace&&) + DIM AS LONG lb, ub, i + lb& = LBOUND(arr&&) : ub& = UBOUND(arr&&) + FOR i& = lb& TO ub& + IF arr&&(i&) = find&& THEN + arr&&(i&) = replace&& + END IF + NEXT i& + END SUB + + + '' + ' Inserts a new element into _INTEGER64 array after index + ' + ' @param _INTEGER64 ARRAY arr&&() array to work on + ' @param _INTEGER64 value&& to insert + ' @param INTEGER index% of element to insert at + ' + SUB ARR_INT64.insert(arr&&(), value&&, index%) + DIM AS LONG lb, ub, i + DIM tmp AS _INTEGER64 + DIM work_arr(0) AS _INTEGER64 + lb& = LBOUND(arr&&) : ub& = UBOUND(arr&&) + IF index% >= lb& AND index% <= ub& THEN + ' build new array up to index + FOR i& = lb& TO index% + CALL ARR_INT64.push(work_arr&&(), arr&&(i&)) + NEXT i& + ' insert new element + CALL ARR_INT64.push(work_arr&&(), value&&) + ' finish building array from index + 1 + FOR i& = index% + 1 TO ub& + CALL ARR_INT64.push(work_arr&&(), arr&&(i&)) + NEXT i& + CALL ARR_INT64.shift(work_arr&&(), tmp&&) + CALL ARR_INT64.copy(work_arr&&(), arr&&()) + END IF + END SUB + + + '' + ' Removes element from a _INTEGER64 array by element index + ' + ' @param _INTEGER64 ARRAY arr&&() array to work on + ' @param INTEGER index% of element to remove + ' + SUB ARR_INT64.remove(arr&&(), index%) + DIM AS LONG lb, ub, i + DIM tmp AS _INTEGER64 + DIM work_arr(0) AS _INTEGER64 + lb& = LBOUND(arr&&) : ub& = UBOUND(arr&&) + IF index% >= lb& AND index% <= ub& THEN + ' build new array up to index + FOR i& = lb& TO index% - 1 + CALL ARR_INT64.push(work_arr&&(), arr&&(i&)) + NEXT i& + ' skip elements + FOR i& = index% + 1 TO ub& + CALL ARR_INT64.push(work_arr&&(), arr&&(i&)) + NEXT i& + CALL ARR_INT64.shift(work_arr&&(), tmp&&) + CALL ARR_INT64.copy(work_arr&&(), arr&&()) + END IF + END SUB + + + '' + ' Filters a _INTEGER64 array to only elements that have odd values + ' + ' @param _INTEGER64 ARRAY source_arr&&() array to work on + ' @param _INTEGER64 ARRAY dest_arr&&() array to store in + ' + SUB ARR_INT64.odd(source_arr&&(), dest_arr&&()) + DIM AS LONG lb, ub, i + DIM tmp AS _INTEGER64 + lb& = LBOUND(source_arr&&) : ub& = UBOUND(source_arr&&) + DIM work_arr(0) AS _INTEGER64 + FOR i& = lb& TO ub& + IF source_arr&&(i&) MOD 2 <> 0 THEN + CALL ARR_INT64.push(work_arr&&(), source_arr&&(i&)) + END IF + NEXT i& + CALL ARR_INT64.shift(work_arr&&(), tmp&&) + CALL ARR_INT64.copy(work_arr&&(), dest_arr&&()) + END SUB + + + '' + ' Filters a _INTEGER64 array to only elements that have even values + ' + ' @param _INTEGER64 ARRAY source_arr&&() array to work on + ' @param _INTEGER64 ARRAY dest_arr&&() array to store in + ' + SUB ARR_INT64.even(source_arr&&(), dest_arr&&()) + DIM AS LONG lb, ub, i + DIM tmp AS _INTEGER64 + lb& = LBOUND(source_arr&&) : ub& = UBOUND(source_arr&&) + DIM work_arr(0) AS _INTEGER64 + FOR i& = lb& TO ub& + IF source_arr&&(i&) MOD 2 = 0 THEN + CALL ARR_INT64.push(work_arr&&(), source_arr&&(i&)) + END IF + NEXT i& + CALL ARR_INT64.shift(work_arr&&(), tmp&&) + CALL ARR_INT64.copy(work_arr&&(), dest_arr&&()) + END SUB + + + '' + ' Filters a _INTEGER64 array to only elements that have values evenly divisible by divisor + ' + ' @param _INTEGER64 ARRAY source_arr&&() array to work on + ' @param _INTEGER64 ARRAY dest_arr&&() array to store in + ' @param _INTEGER64 divisor&& for modulo + ' + SUB ARR_INT64.mod(source_arr&&(), dest_arr&&(), divisor&&) + DIM AS LONG lb, ub, i + DIM tmp AS _INTEGER64 + lb& = LBOUND(source_arr&&) : ub& = UBOUND(source_arr&&) + DIM work_arr(0) AS _INTEGER64 + FOR i& = lb& TO ub& + IF source_arr&&(i&) MOD divisor&& = 0 THEN + CALL ARR_INT64.push(work_arr&&(), source_arr&&(i&)) + END IF + NEXT i& + CALL ARR_INT64.shift(work_arr&&(), tmp&&) + CALL ARR_INT64.copy(work_arr&&(), dest_arr&&()) + END SUB + + + '' + ' Filters a _INTEGER64 array to only elements between min and max + ' + ' @param _INTEGER64 ARRAY source_arr&&() array to work on + ' @param _INTEGER64 ARRAY dest_arr&&() array to store in + ' @param _INTEGER64 min&& to be greater than or equal to be returned + ' @param _INTEGER64 max&& to be less than or equal to be returned + ' + SUB ARR_INT64.between(source_arr&&(), dest_arr&&(), min&&, max&&) + DIM AS LONG lb, ub, i + DIM tmp AS _INTEGER64 + lb& = LBOUND(source_arr&&) : ub& = UBOUND(source_arr&&) + DIM work_arr(0) AS _INTEGER64 + FOR i& = lb& TO ub& + IF source_arr&&(i&) >= min&& _ + AND source_arr&&(i&) <= max&& THEN + CALL ARR_INT64.push(work_arr&&(), source_arr&&(i&)) + END IF + NEXT i& + CALL ARR_INT64.shift(work_arr&&(), tmp&&) + CALL ARR_INT64.copy(work_arr&&(), dest_arr&&()) + END SUB + + + '' + ' Sorts _INTEGER64 array in ascending order + ' + ' @param _INTEGER64 ARRAY source_arr&&() array to sort + ' @param _INTEGER64 ARRAY dest_arr&&() array to store sorted in + ' + SUB ARR_INT64.sort(source_arr&&(), dest_arr&&()) + DIM AS LONG lb, ub, count + lb& = LBOUND(source_arr&&) : ub& = UBOUND(source_arr&&) + count& = ub& - lb& + REDIM dest_arr(0 TO count&) AS _INTEGER64 + CALL ARR_INT64.copy(source_arr&&(), dest_arr&&()) + CALL ARR_INT64.quicksort(dest_arr&&(), lb&, ub&, 0) + END SUB + + + '' + ' Sorts _INTEGER64 array in descending order + ' + ' @param _INTEGER64 ARRAY source_arr&&() array to sort + ' @param _INTEGER64 ARRAY dest_arr&&() array to store sorted in + ' + SUB ARR_INT64.rsort(source_arr&&(), dest_arr&&()) + DIM AS LONG lb, ub, count + lb& = LBOUND(source_arr&&) : ub& = UBOUND(source_arr&&) + count& = ub& - lb& + REDIM dest_arr(0 TO count&) AS _INTEGER64 + CALL ARR_INT64.copy(source_arr&&(), dest_arr&&()) + CALL ARR_INT64.quicksort(dest_arr&&(), lb&, ub&, 1) + END SUB + + + '' + ' Quicksort array with pivot algorithm by logiclrd + ' + ' @link https://www.tek-tips.com/faqs.cfm?fid=336 + ' @param _INTEGER64 ARRAY array&&() to sort + ' @param INTEGER start% of range to sort + ' @param INTEGER finish% range of sort + ' @param INTEGER order% to sort by (0 = asc / 1 = desc) + ' + SUB ARR_INT64.quicksort(arr&&(), start%, finish%, order%) + DIM i AS LONG + DIM pivot AS INTEGER + DIM pivotvalue AS _INTEGER64 + + 'first, partition the array + pivot% = start% + pivotvalue&& = arr&&(pivot%) + FOR i& = start% + 1 TO finish% + IF order% = 0 THEN ' ascending order + IF arr&&(i&) < pivotvalue&& THEN + arr&&(pivot%) = arr&&(i&) + arr&&(i&) = arr&&(pivot% + 1) + arr&&(pivot% + 1) = pivotvalue&& + pivot% = pivot% + 1 + END IF + ELSEIF order% = 1 THEN ' descending order + IF arr&&(i&) > pivotvalue&& THEN + arr&&(pivot%) = arr&&(i&) + arr&&(i&) = arr&&(pivot% + 1) + arr&&(pivot% + 1) = pivotvalue&& + pivot% = pivot% + 1 + END IF + END IF + NEXT i& + + 'then, sort the subarrays to each side of the pivot + IF pivot% - start% >= 2 THEN + CALL ARR_INT64.quicksort(arr&&(), start%, pivot% - 1, order%) + END IF + IF finish% - pivot% >= 2 THEN + CALL ARR_INT64.quicksort(arr&&(), pivot% + 1, finish%, order%) + END IF + END SUB + $END IF + $IF GJ_LIB_ARR_LONG_BAS = UNDEFINED THEN + '$DYNAMIC + $LET GJ_LIB_ARR_LONG_BAS = 1 + + '' + ' Slice an array from source to destination starting at index and count slices + ' + ' @param LONG() source_arr& to slice from + ' @param LONG() dest_arr& to put slices into + ' @param INTEGER start_idx% starting index to use as slice range + ' @param INTEGER count% number of slices - if negative, backwards from index + ' + SUB ARR_LONG.slice(source_arr&(), dest_arr&(), start_idx%, count%) + DIM AS LONG ub, lb, i, n + lb& = LBOUND(source_arr&) : ub& = UBOUND(source_arr&) + IF start_idx% < lb& OR start_idx% + count% > ub& THEN EXIT SUB ' out of range + IF ub& - lb& < count% THEN EXIT SUB ' too many and not enough + REDIM dest_arr(0 TO ABS(count%)) AS LONG + IF SGN(count%) = -1 THEN + IF ((start_idx% - 1) - ABS(count%)) < 0 THEN EXIT SUB ' out of range + n& = 0 + FOR i& = (start_idx% - 1) TO ((start_idx% - 1) - ABS(count%)) STEP -1 + dest_arr&(n&) = source_arr&(i&) + n& = n& + 1 + NEXT i& + ELSE + IF ((start_idx% + 1) + ABS(count%)) > (ub& - lb&) THEN EXIT SUB ' out of range + n& = 0 + FOR i& = start_idx% + 1 TO ((start_idx% + 1) + count%) STEP 1 + dest_arr&(n&) = source_arr&(i&) + n& = n& + 1 + NEXT i& + END IF + END SUB + + + '' + ' Push a long onto the end of a LONG array + ' + ' @param LONG arr&() array to push into + ' @param LONG value& of byte to push + ' + SUB ARR_LONG.push(arr&(), value&) + DIM AS LONG ub, lb + lb& = LBOUND(arr&) : ub& = UBOUND(arr&) + REDIM _PRESERVE arr(lb& TO (ub& + 1)) AS LONG + arr&(ub& + 1) = value& + END SUB + + + '' + ' Pop a long from the end of a LONG array + ' + ' @param LONG arr&() array to pop from + ' @param LONG var& of long to store popped long + ' + SUB ARR_LONG.pop(arr&(), var&) + DIM AS LONG ub, lb + lb& = LBOUND(arr&) : ub& = UBOUND(arr&) + var& = arr&(ub&) + REDIM _PRESERVE arr(lb& TO (ub& - 1)) AS LONG + END SUB + + + '' + ' Pop a long from the beginning of a LONG array + ' + ' @param LONG arr&() array to pop from + ' @param LONG var& of long to store popped long + ' + SUB ARR_LONG.shift(arr&(), var&) + DIM AS LONG ub, lb, i + lb& = LBOUND(arr&) : ub& = UBOUND(arr&) + var& = arr&(lb&) + FOR i& = lb& TO ub& - 1 + arr&(i&) = arr&(i& + 1) + NEXT i& + REDIM _PRESERVE arr(lb& + 1 TO ub&) AS LONG + END SUB + + + '' + ' Copy an array of LONGs to another LONG array + ' + ' @param LONG ARRAY source_arr&() source array to copy + ' @param LONG ARRAY dest_arr&() dest array to copy into + ' + SUB ARR_LONG.copy(source_arr&(), dest_arr&()) + DIM AS LONG ub, lb, i + lb& = LBOUND(source_arr&) : ub& = UBOUND(source_arr&) + REDIM dest_arr(lb& TO ub&) AS LONG + FOR i& = lb& TO ub& + dest_arr&(i&) = source_arr&(i&) + NEXT i& + END SUB + + + '' + ' Push a long into the beginning of a LONG array + ' + ' @param LONG arr&() array to push into + ' @param LONG value& of long to push + ' + SUB ARR_LONG.unshift(arr&(), value&) + DIM AS LONG ub, lb, i + lb& = LBOUND(arr&) : ub& = UBOUND(arr&) + DIM work(lb& TO ub&) AS LONG + CALL ARR_LONG.copy(arr&(), work&()) + REDIM _PRESERVE arr(lb& TO (ub& + 1)) AS LONG + FOR i& = lb& + 1 TO ub& + 1 + arr&(i&) = work&(i& - 1) + NEXT i& + arr&(lb&) = value& + END SUB + + + '' + ' Joins an array of LONGs as a string + ' + ' @param LONG ARRAY arr&() to get as a string + ' @param STRING s$ to store stringified array in + ' + SUB ARR_LONG.join(arr&(), s$) + DIM AS LONG ub, lb, i + s$ = "" + lb& = LBOUND(arr&) : ub& = UBOUND(arr&) + FOR i& = lb& TO ub& + s$ = s$ + _TRIM$(STR$(arr&(i&))) + ", " + NEXT i& + ' remove trailing comma + s$ = MID$(s$, 1, (LEN(s$)-LEN(", "))) + END SUB + + + '' + ' Create a new LONG array using string of longs seperated by commas + ' + ' @param LONG ARRAY arr&() to store the longs in + ' @param STRING s$ string of comma separated longs + ' + SUB ARR_LONG.new(arr&(), s$) + DIM AS LONG i, count + DIM t AS STRING + count& = 0 + FOR i& = 1 TO LEN(s$) + IF ASC(s$, i&) = ASC(",") THEN count& = count& + 1 + NEXT i& + REDIM arr(0 TO count&) AS LONG + IF count& = 0 THEN + arr&(0) = VAL(s$) + EXIT SUB + END IF + count& = 0 + FOR i& = 1 TO LEN(s$) + t$ = t$ + CHR$(ASC(s$, i&)) + IF ASC(s$, i&) = ASC(",") OR i& = LEN(s$) THEN + arr&(count&) = VAL(t$) + count& = count& + 1 + t$ = "" + END IF + NEXT i& + END SUB + + + '' + ' Return the visually longest element of a LONG array + ' + ' @param LONG ARRAY arr&() to check in + ' @return LONG value of visually longest element + ' + FUNCTION ARR_LONG.longest&(arr&()) + DIM AS LONG lb, ub, i, res, lw + lb& = LBOUND(arr&) : ub& = UBOUND(arr&) + res& = 0 : lw& = 0 + FOR i& = lb& TO ub& + IF LEN(_TRIM$(STR$(arr&(i&)))) > lw& THEN + lw& = LEN(_TRIM$(STR$(arr&(i&)))) + res& = i& + END IF + NEXT i& + ARR_LONG.longest& = arr&(res&) + END FUNCTION + + + '' + ' Perform some math on every element of a LONG array + ' + ' @param LONG ARRAY source_arr&() to do math on + ' @param LONG ARRAY dest_arr&() to store results in + ' @param STRING op$ one of: + ' and or xor shl shr + ' "+", "-", "*", "\", "&&", "||", "!!", "<<", ">>" + ' @param LONG value& to use for operand + ' + SUB ARR_LONG.math(source_arr&(), dest_arr&(), op$, value&) + DIM AS LONG lb, ub, i + lb& = LBOUND(source_arr&) : ub& = UBOUND(source_arr&) + REDIM dest_arr(lb& TO ub&) AS LONG + FOR i& = lb& TO ub& + SELECT CASE op$ + CASE "+": + dest_arr&(i&) = source_arr&(i&) + value& + CASE "-": + dest_arr&(i&) = source_arr&(i&) - value& + CASE "*": + dest_arr&(i&) = source_arr&(i&) * value& + CASE "\": + IF value& > 0 THEN + dest_arr&(i&) = source_arr&(i&) \ value& + END IF + CASE "&&": + dest_arr&(i&) = source_arr&(i&) AND value& + CASE "||": + dest_arr&(i&) = source_arr&(i&) OR value& + CASE "!!": + dest_arr&(i&) = source_arr&(i&) XOR value& + CASE "<<": + dest_arr&(i&) = _SHL(source_arr&(i&), value&) + CASE ">>": + dest_arr&(i&) = _SHR(source_arr&(i&), value&) + END SELECT + NEXT i& + END SUB + + + '' + ' Return the minimum element value in LONG array + ' + ' @param LONG ARRAY arr&() to check in + ' @return LONG minimum value found + ' + FUNCTION ARR_LONG.min&(arr&()) + DIM AS LONG lb, ub, i + DIM AS LONG s + lb& = LBOUND(arr&) : ub& = UBOUND(arr&) + s& = 127 + FOR i& = lb& TO ub& + IF arr&(i&) < s& THEN + s& = arr&(i&) + END IF + NEXT i& + ARR_LONG.min& = s& + END FUNCTION + + + '' + ' Return the maximum element value in LONG array + ' + ' @param LONG ARRAY arr&() to check in + ' @return LONG maximum value found + ' + FUNCTION ARR_LONG.max&(arr&()) + DIM AS LONG lb, ub, i + DIM AS LONG s + lb& = LBOUND(arr&) : ub& = UBOUND(arr&) + s& = 0 + FOR i& = lb& TO ub& + IF arr&(i&) > s& THEN + s& = arr&(i&) + END IF + NEXT i& + ARR_LONG.max& = s& + END FUNCTION + + + '' + ' Return the visually shortest element of a LONG array + ' + ' @param LONG ARRAY arr&() to check in + ' @return LONG value of visually shortest element + ' + FUNCTION ARR_LONG.shortest&(arr&()) + DIM AS LONG lb, ub, i, res, sw + lb& = LBOUND(arr&) : ub& = UBOUND(arr&) + res& = 0 : sw& = 0 + FOR i& = lb& TO ub& + IF LEN(_TRIM$(STR$(arr&(i&)))) < sw& THEN + sw& = LEN(_TRIM$(STR$(arr&(i&)))) + res& = i& + END IF + NEXT i& + ARR_LONG.shortest& = arr&(res&) + END FUNCTION + + + '' + ' Return the first element of a LONG array + ' + ' @param LONG ARRAY arr&() to check in + ' @return LONG value of first element + ' + FUNCTION ARR_LONG.first&(arr&()) + ARR_LONG.first& = arr&(LBOUND(arr&)) + END FUNCTION + + + '' + ' Return the last element of a LONG array + ' + ' @param LONG ARRAY arr&() to check in + ' @return LONG value of last element + ' + FUNCTION ARR_LONG.last&(arr&()) + ARR_LONG.last& = arr&(UBOUND(arr&)) + END FUNCTION + + + '' + ' Return every nth array element of a LONG array + ' + ' @param LONG ARRAY source_arr&() to get from + ' @param LONG ARRAY dest_arr&() to store in + ' @param INTEGER nth% element + ' + SUB ARR_LONG.nth(source_arr&(), dest_arr&(), nth%) + DIM AS LONG lb, ub, i, n + lb& = LBOUND(source_arr&) : ub& = UBOUND(source_arr&) + n& = (ub& - lb&) \ nth% + REDIM dest_arr(n&) AS LONG + n& = 0 + FOR i& = lb& to ub& + IF i& MOD nth% = 0 THEN + dest_arr&(n&) = source_arr&(i&) + n& = n& + 1 + END IF + NEXT i& + END SUB + + + '' + ' Checks if value exists in LONG array + ' + ' @param LONG ARRAY arr&() to check in + ' @param LONG value& value to check for + ' @return INTEGER -1 if found or 0 if not found + ' + FUNCTION ARR_LONG.in%(arr&(), value&) + DIM AS LONG lb, ub, i + lb& = LBOUND(arr&) : ub& = UBOUND(arr&) + FOR i& = lb& TO ub& + IF arr&(i&) = value& THEN + ARR_LONG.in% = -1 + EXIT FUNCTION + END IF + NEXT i& + ARR_LONG.in% = 0 + END FUNCTION + + + '' + ' Checks if value exists in LONG array and returns index if found + ' + ' @param LONG ARRAY arr&() to check in + ' @param LONG value& value to check for + ' @return INTEGER index of element if found or -1 if not found + ' + FUNCTION ARR_LONG.find%(arr&(), value&) + DIM AS LONG lb, ub, i + lb& = LBOUND(arr&) : ub& = UBOUND(arr&) + FOR i& = lb& TO ub& + IF arr&(i&) = value& THEN + ARR_LONG.find% = i& + EXIT FUNCTION + END IF + NEXT i& + ARR_LONG.find% = -1 + END FUNCTION + + + '' + ' Return the number of elements in a LONG array + ' + ' @param LONG ARRAY arr&() to count + ' @return INTEGER number of elements + ' + FUNCTION ARR_LONG.count&(arr&()) + ARR_LONG.count& = UBOUND(arr&) - LBOUND(arr&) + END FUNCTION + + + '' + ' Return the size of a LONG array + ' + ' @param LONG ARRAY arr&() to get size of + ' @return LONG size in bytes + ' + FUNCTION ARR_LONG.size&(arr&()) + ARR_LONG.size& = LEN(arr&()) + END FUNCTION + + + '' + ' Reverses the elements of a LONG array + ' + ' @param LONG ARRAY source_arr&() to reverse + ' @param LONG ARRAY dest_arr&() to store reversed array in + ' + SUB ARR_LONG.reverse(source_arr&(), dest_arr&()) + DIM AS LONG lb, ub, i, n + lb& = LBOUND(source_arr&) : ub& = UBOUND(source_arr&) + REDIM dest_arr(0 TO (ub& - lb&)) AS LONG + n& = 0 + FOR i& = ub& TO lb& STEP -1 + dest_arr&(n&) = source_arr&(i&) + n& = n& + 1 + NEXT i& + END SUB + + + '' + ' Returns a random long from a LONG array + ' + ' @param LONG ARRAY arr&() array to get random element from + ' @return LONG random element + ' + FUNCTION ARR_LONG.random&(arr&()) + DIM AS LONG lb, ub + lb& = LBOUND(arr&) : ub& = UBOUND(arr&) + RANDOMIZE TIMER + ARR_LONG.random& = arr&(INT(RND * (ub& - lb&)) + 1) + END FUNCTION + + + '' + ' Returns the sum of all elements in a LONG array + ' + ' @param LONG ARRAY arr&() array to get some for + ' @return LONG sum of all elements + ' + FUNCTION ARR_LONG.sum&(arr&()) + DIM AS LONG lb, ub, i + DIM sum AS LONG + lb& = LBOUND(arr&) : ub& = UBOUND(arr&) + FOR i& = lb& TO ub& + sum& = sum& + arr&(i&) + NEXT i& + ARR_LONG.sum& = sum& + END FUNCTION + + + '' + ' Returns the average value of elements in a LONG array + ' + ' @param LONG ARRAY arr&() array to get average for + ' @return LONG average of elements + ' + FUNCTION ARR_LONG.avg&(arr&()) + DIM AS LONG lb, ub, i + DIM sum AS LONG + lb& = LBOUND(arr&) : ub& = UBOUND(arr&) + FOR i& = lb& TO ub& + sum& = sum& + arr&(i&) + NEXT i& + ARR_LONG.avg& = sum& / (ub& - lb&) + END FUNCTION + + + '' + ' Shuffle the elements of a LONG array + ' + ' @param LONG ARRAY source_arr&() to shuffle + ' @param LONG ARRAY dest_arr&() to store shuffled array in + ' + SUB ARR_LONG.shuffle(source_arr&(), dest_arr&()) + DIM AS LONG lb, ub, i, count + lb& = LBOUND(source_arr&) : ub& = UBOUND(source_arr&) + count& = ub& - lb& + REDIM dest_arr(0 TO count&) AS LONG + CALL ARR_LONG.copy(source_arr&(), dest_arr&()) + RANDOMIZE TIMER + FOR i& = 0 TO count& + SWAP dest_arr&(i&), dest_arr&(lb& + RND * (ub& - lb&)) + NEXT i& + END SUB + + + '' + ' Makes a LONG array contain only unique values + ' + ' @param LONG ARRAY source_arr&() array to get uniques for + ' @param LONG ARRAY dest_arr&() array to store uniques in + ' + SUB ARR_LONG.unique(source_arr&(), dest_arr&()) + DIM AS LONG lb, ub, i + DIM tmp AS LONG + lb& = LBOUND(source_arr&) : ub& = UBOUND(source_arr&) + DIM work_arr(0) AS LONG + FOR i& = lb& TO ub& + IF NOT ARR_LONG.in%(work_arr&(), source_arr&(i&)) THEN + CALL ARR_LONG.push(work_arr&(), source_arr&(i&)) + END IF + NEXT i& + CALL ARR_LONG.shift(work_arr&(), tmp&) + CALL ARR_LONG.copy(work_arr&(), dest_arr&()) + END SUB + + + '' + ' Filters a LONG array to only elements greater than value + ' + ' @param LONG ARRAY source_arr&() array to work on + ' @param LONG ARRAY dest_arr&() array to store in + ' @param LONG value& to be greater than to be returned + ' + SUB ARR_LONG.gt(source_arr&(), dest_arr&(), value&) + DIM AS LONG lb, ub, i + DIM tmp AS LONG + lb& = LBOUND(source_arr&) : ub& = UBOUND(source_arr&) + DIM work_arr(0) AS LONG + FOR i& = lb& TO ub& + IF source_arr&(i&) > value& THEN + CALL ARR_LONG.push(work_arr&(), source_arr&(i&)) + END IF + NEXT i& + CALL ARR_LONG.shift(work_arr&(), tmp&) + CALL ARR_LONG.copy(work_arr&(), dest_arr&()) + END SUB + + + '' + ' Filters a LONG array to only elements greater than or equal to value + ' + ' @param LONG ARRAY source_arr&() array to work on + ' @param LONG ARRAY dest_arr&() array to store in + ' @param LONG value& to be greater than or equal to be returned + ' + SUB ARR_LONG.gte(source_arr&(), dest_arr&(), value&) + DIM AS LONG lb, ub, i + DIM tmp AS LONG + lb& = LBOUND(source_arr&) : ub& = UBOUND(source_arr&) + DIM work_arr(0) AS LONG + FOR i& = lb& TO ub& + IF source_arr&(i&) >= value& THEN + CALL ARR_LONG.push(work_arr&(), source_arr&(i&)) + END IF + NEXT i& + CALL ARR_LONG.shift(work_arr&(), tmp&) + CALL ARR_LONG.copy(work_arr&(), dest_arr&()) + END SUB + + + '' + ' Filters a LONG array to only elements less than value + ' + ' @param LONG ARRAY source_arr&() array to work on + ' @param LONG ARRAY dest_arr&() array to store in + ' @param LONG value& to be less than to be returned + ' + SUB ARR_LONG.lt(source_arr&(), dest_arr&(), value&) + DIM AS LONG lb, ub, i + DIM tmp AS LONG + lb& = LBOUND(source_arr&) : ub& = UBOUND(source_arr&) + DIM work_arr(0) AS LONG + FOR i& = lb& TO ub& + IF source_arr&(i&) < value& THEN + CALL ARR_LONG.push(work_arr&(), source_arr&(i&)) + END IF + NEXT i& + CALL ARR_LONG.shift(work_arr&(), tmp&) + CALL ARR_LONG.copy(work_arr&(), dest_arr&()) + END SUB + + + '' + ' Filters a LONG array to only elements less than or equal to value + ' + ' @param LONG ARRAY source_arr&() array to work on + ' @param LONG ARRAY dest_arr&() array to store in + ' @param LONG value& to be less than or equal to be returned + ' + SUB ARR_LONG.lte(source_arr&(), dest_arr&(), value&) + DIM AS LONG lb, ub, i + DIM tmp AS LONG + lb& = LBOUND(source_arr&) : ub& = UBOUND(source_arr&) + DIM work_arr(0) AS LONG + FOR i& = lb& TO ub& + IF source_arr&(i&) <= value& THEN + CALL ARR_LONG.push(work_arr&(), source_arr&(i&)) + END IF + NEXT i& + CALL ARR_LONG.shift(work_arr&(), tmp&) + CALL ARR_LONG.copy(work_arr&(), dest_arr&()) + END SUB + + + '' + ' Finds and replaces values across all elements in a LONG ARRAY + ' + ' @param LONG ARRAY arr&() to check in + ' @param LONG find& value to find + ' @param LONG replace& value to replace with if found + ' + SUB ARR_LONG.replace(arr&(), find&, replace&) + DIM AS LONG lb, ub, i + lb& = LBOUND(arr&) : ub& = UBOUND(arr&) + FOR i& = lb& TO ub& + IF arr&(i&) = find& THEN + arr&(i&) = replace& + END IF + NEXT i& + END SUB + + + '' + ' Inserts a new element into LONG array after index + ' + ' @param LONG ARRAY arr&() array to work on + ' @param LONG value& to insert + ' @param INTEGER index% of element to insert at + ' + SUB ARR_LONG.insert(arr&(), value&, index%) + DIM AS LONG lb, ub, i + DIM tmp AS LONG + DIM work_arr(0) AS LONG + lb& = LBOUND(arr&) : ub& = UBOUND(arr&) + IF index% >= lb& AND index% <= ub& THEN + ' build new array up to index + FOR i& = lb& TO index% + CALL ARR_LONG.push(work_arr&(), arr&(i&)) + NEXT i& + ' insert new element + CALL ARR_LONG.push(work_arr&(), value&) + ' finish building array from index + 1 + FOR i& = index% + 1 TO ub& + CALL ARR_LONG.push(work_arr&(), arr&(i&)) + NEXT i& + CALL ARR_LONG.shift(work_arr&(), tmp&) + CALL ARR_LONG.copy(work_arr&(), arr&()) + END IF + END SUB + + + '' + ' Removes element from a LONG array by element index + ' + ' @param LONG ARRAY arr&() array to work on + ' @param INTEGER index% of element to remove + ' + SUB ARR_LONG.remove(arr&(), index%) + DIM AS LONG lb, ub, i + DIM tmp AS LONG + DIM work_arr(0) AS LONG + lb& = LBOUND(arr&) : ub& = UBOUND(arr&) + IF index% >= lb& AND index% <= ub& THEN + ' build new array up to index + FOR i& = lb& TO index% - 1 + CALL ARR_LONG.push(work_arr&(), arr&(i&)) + NEXT i& + ' skip elements + FOR i& = index% + 1 TO ub& + CALL ARR_LONG.push(work_arr&(), arr&(i&)) + NEXT i& + CALL ARR_LONG.shift(work_arr&(), tmp&) + CALL ARR_LONG.copy(work_arr&(), arr&()) + END IF + END SUB + + + '' + ' Filters a LONG array to only elements that have odd values + ' + ' @param LONG ARRAY source_arr&() array to work on + ' @param LONG ARRAY dest_arr&() array to store in + ' + SUB ARR_LONG.odd(source_arr&(), dest_arr&()) + DIM AS LONG lb, ub, i + DIM tmp AS LONG + lb& = LBOUND(source_arr&) : ub& = UBOUND(source_arr&) + DIM work_arr(0) AS LONG + FOR i& = lb& TO ub& + IF source_arr&(i&) MOD 2 <> 0 THEN + CALL ARR_LONG.push(work_arr&(), source_arr&(i&)) + END IF + NEXT i& + CALL ARR_LONG.shift(work_arr&(), tmp&) + CALL ARR_LONG.copy(work_arr&(), dest_arr&()) + END SUB + + + '' + ' Filters a LONG array to only elements that have even values + ' + ' @param LONG ARRAY source_arr&() array to work on + ' @param LONG ARRAY dest_arr&() array to store in + ' + SUB ARR_LONG.even(source_arr&(), dest_arr&()) + DIM AS LONG lb, ub, i + DIM tmp AS LONG + lb& = LBOUND(source_arr&) : ub& = UBOUND(source_arr&) + DIM work_arr(0) AS LONG + FOR i& = lb& TO ub& + IF source_arr&(i&) MOD 2 = 0 THEN + CALL ARR_LONG.push(work_arr&(), source_arr&(i&)) + END IF + NEXT i& + CALL ARR_LONG.shift(work_arr&(), tmp&) + CALL ARR_LONG.copy(work_arr&(), dest_arr&()) + END SUB + + + '' + ' Filters a LONG array to only elements that have values evenly divisible by divisor + ' + ' @param LONG ARRAY source_arr&() array to work on + ' @param LONG ARRAY dest_arr&() array to store in + ' @param LONG divisor& for modulo + ' + SUB ARR_LONG.mod(source_arr&(), dest_arr&(), divisor&) + DIM AS LONG lb, ub, i + DIM tmp AS LONG + lb& = LBOUND(source_arr&) : ub& = UBOUND(source_arr&) + DIM work_arr(0) AS LONG + FOR i& = lb& TO ub& + IF source_arr&(i&) MOD divisor& = 0 THEN + CALL ARR_LONG.push(work_arr&(), source_arr&(i&)) + END IF + NEXT i& + CALL ARR_LONG.shift(work_arr&(), tmp&) + CALL ARR_LONG.copy(work_arr&(), dest_arr&()) + END SUB + + + '' + ' Filters a LONG array to only elements between min and max + ' + ' @param LONG ARRAY source_arr&() array to work on + ' @param LONG ARRAY dest_arr&() array to store in + ' @param LONG min& to be greater than or equal to be returned + ' @param LONG max& to be less than or equal to be returned + ' + SUB ARR_LONG.between(source_arr&(), dest_arr&(), min&, max&) + DIM AS LONG lb, ub, i + DIM tmp AS LONG + lb& = LBOUND(source_arr&) : ub& = UBOUND(source_arr&) + DIM work_arr(0) AS LONG + FOR i& = lb& TO ub& + IF source_arr&(i&) >= min& _ + AND source_arr&(i&) <= max& THEN + CALL ARR_LONG.push(work_arr&(), source_arr&(i&)) + END IF + NEXT i& + CALL ARR_LONG.shift(work_arr&(), tmp&) + CALL ARR_LONG.copy(work_arr&(), dest_arr&()) + END SUB + + + '' + ' Sorts LONG array in ascending order + ' + ' @param LONG ARRAY source_arr&() array to sort + ' @param LONG ARRAY dest_arr&() array to store sorted in + ' + SUB ARR_LONG.sort(source_arr&(), dest_arr&()) + DIM AS LONG lb, ub, count + lb& = LBOUND(source_arr&) : ub& = UBOUND(source_arr&) + count& = ub& - lb& + REDIM dest_arr(0 TO count&) AS LONG + CALL ARR_LONG.copy(source_arr&(), dest_arr&()) + CALL ARR_LONG.quicksort(dest_arr&(), lb&, ub&, 0) + END SUB + + + '' + ' Sorts LONG array in descending order + ' + ' @param LONG ARRAY source_arr&() array to sort + ' @param LONG ARRAY dest_arr&() array to store sorted in + ' + SUB ARR_LONG.rsort(source_arr&(), dest_arr&()) + DIM AS LONG lb, ub, count + lb& = LBOUND(source_arr&) : ub& = UBOUND(source_arr&) + count& = ub& - lb& + REDIM dest_arr(0 TO count&) AS LONG + CALL ARR_LONG.copy(source_arr&(), dest_arr&()) + CALL ARR_LONG.quicksort(dest_arr&(), lb&, ub&, 1) + END SUB + + + '' + ' Quicksort array with pivot algorithm by logiclrd + ' + ' @link https://www.tek-tips.com/faqs.cfm?fid=336 + ' @param LONG ARRAY array&() to sort + ' @param INTEGER start% of range to sort + ' @param INTEGER finish% range of sort + ' @param INTEGER order% to sort by (0 = asc / 1 = desc) + ' + SUB ARR_LONG.quicksort(arr&(), start%, finish%, order%) + DIM i AS LONG + DIM pivot AS INTEGER + DIM pivotvalue AS LONG + + 'first, partition the array + pivot% = start% + pivotvalue& = arr&(pivot%) + FOR i& = start% + 1 TO finish% + IF order% = 0 THEN ' ascending order + IF arr&(i&) < pivotvalue& THEN + arr&(pivot%) = arr&(i&) + arr&(i&) = arr&(pivot% + 1) + arr&(pivot% + 1) = pivotvalue& + pivot% = pivot% + 1 + END IF + ELSEIF order% = 1 THEN ' descending order + IF arr&(i&) > pivotvalue& THEN + arr&(pivot%) = arr&(i&) + arr&(i&) = arr&(pivot% + 1) + arr&(pivot% + 1) = pivotvalue& + pivot% = pivot% + 1 + END IF + END IF + NEXT i& + + 'then, sort the subarrays to each side of the pivot + IF pivot% - start% >= 2 THEN + CALL ARR_LONG.quicksort(arr&(), start%, pivot% - 1, order%) + END IF + IF finish% - pivot% >= 2 THEN + CALL ARR_LONG.quicksort(arr&(), pivot% + 1, finish%, order%) + END IF + END SUB + $END IF + $IF GJ_LIB_ARR_SNG_BAS = UNDEFINED THEN + '$DYNAMIC + $LET GJ_LIB_ARR_SNG_BAS = 1 + + '' + ' Slice an array from source to destination starting at index and count slices + ' + ' @param SINGLE() source_arr! to slice from + ' @param SINGLE() dest_arr! to put slices into + ' @param INTEGER start_idx% starting index to use as slice range + ' @param INTEGER count% number of slices - if negative, backwards from index + ' + SUB ARR_SNG.slice(source_arr!(), dest_arr!(), start_idx%, count%) + DIM AS LONG ub, lb, i, n + lb& = LBOUND(source_arr!) : ub& = UBOUND(source_arr!) + IF start_idx% < lb& OR start_idx% + count% > ub& THEN EXIT SUB ' out of range + IF ub& - lb& < count% THEN EXIT SUB ' too many and not enough + REDIM dest_arr(0 TO ABS(count%)) AS SINGLE + IF SGN(count%) = -1 THEN + IF ((start_idx% - 1) - ABS(count%)) < 0 THEN EXIT SUB ' out of range + n& = 0 + FOR i& = (start_idx% - 1) TO ((start_idx% - 1) - ABS(count%)) STEP -1 + dest_arr!(n&) = source_arr!(i&) + n& = n& + 1 + NEXT i& + ELSE + IF ((start_idx% + 1) + ABS(count%)) > (ub& - lb&) THEN EXIT SUB ' out of range + n& = 0 + FOR i& = start_idx% + 1 TO ((start_idx% + 1) + count%) STEP 1 + dest_arr!(n&) = source_arr!(i&) + n& = n& + 1 + NEXT i& + END IF + END SUB + + + '' + ' Push a sng onto the end of a SINGLE array + ' + ' @param SINGLE arr!() array to push into + ' @param SINGLE value! of byte to push + ' + SUB ARR_SNG.push(arr!(), value!) + DIM AS LONG ub, lb + lb& = LBOUND(arr!) : ub& = UBOUND(arr!) + REDIM _PRESERVE arr(lb& TO (ub& + 1)) AS SINGLE + arr!(ub& + 1) = value! + END SUB + + + '' + ' Pop a sng from the end of a SINGLE array + ' + ' @param SINGLE arr!() array to pop from + ' @param SINGLE var! of sng to store popped sng + ' + SUB ARR_SNG.pop(arr!(), var!) + DIM AS LONG ub, lb + lb& = LBOUND(arr!) : ub& = UBOUND(arr!) + var! = arr!(ub&) + REDIM _PRESERVE arr(lb& TO (ub& - 1)) AS SINGLE + END SUB + + + '' + ' Pop a sng from the beginning of a SINGLE array + ' + ' @param SINGLE arr!() array to pop from + ' @param SINGLE var! of sng to store popped sng + ' + SUB ARR_SNG.shift(arr!(), var!) + DIM AS LONG ub, lb, i + lb& = LBOUND(arr!) : ub& = UBOUND(arr!) + var! = arr!(lb&) + FOR i& = lb& TO ub& - 1 + arr!(i&) = arr!(i& + 1) + NEXT i& + REDIM _PRESERVE arr(lb& + 1 TO ub&) AS SINGLE + END SUB + + + '' + ' Copy an array of SNGs to another SINGLE array + ' + ' @param SINGLE ARRAY source_arr!() source array to copy + ' @param SINGLE ARRAY dest_arr!() dest array to copy into + ' + SUB ARR_SNG.copy(source_arr!(), dest_arr!()) + DIM AS LONG ub, lb, i + lb& = LBOUND(source_arr!) : ub& = UBOUND(source_arr!) + REDIM dest_arr(lb& TO ub&) AS SINGLE + FOR i& = lb& TO ub& + dest_arr!(i&) = source_arr!(i&) + NEXT i& + END SUB + + + '' + ' Push a sng into the beginning of a SINGLE array + ' + ' @param SINGLE arr!() array to push into + ' @param SINGLE value! of sng to push + ' + SUB ARR_SNG.unshift(arr!(), value!) + DIM AS LONG ub, lb, i + lb& = LBOUND(arr!) : ub& = UBOUND(arr!) + DIM work(lb& TO ub&) AS SINGLE + CALL ARR_SNG.copy(arr!(), work!()) + REDIM _PRESERVE arr(lb& TO (ub& + 1)) AS SINGLE + FOR i& = lb& + 1 TO ub& + 1 + arr!(i&) = work!(i& - 1) + NEXT i& + arr!(lb&) = value! + END SUB + + + '' + ' Joins an array of SNGs as a string + ' + ' @param SINGLE ARRAY arr!() to get as a string + ' @param STRING s$ to store stringified array in + ' + SUB ARR_SNG.join(arr!(), s$) + DIM AS LONG ub, lb, i + s$ = "" + lb& = LBOUND(arr!) : ub& = UBOUND(arr!) + FOR i& = lb& TO ub& + s$ = s$ + _TRIM$(STR$(arr!(i&))) + ", " + NEXT i& + ' remove trailing comma + s$ = MID$(s$, 1, (LEN(s$)-LEN(", "))) + END SUB + + + '' + ' Create a new SINGLE array using string of sngs seperated by commas + ' + ' @param SINGLE ARRAY arr!() to store the sngs in + ' @param STRING s$ string of comma separated sngs + ' + SUB ARR_SNG.new(arr!(), s$) + DIM AS LONG i, count + DIM t AS STRING + count& = 0 + FOR i& = 1 TO LEN(s$) + IF ASC(s$, i&) = ASC(",") THEN count& = count& + 1 + NEXT i& + REDIM arr(0 TO count&) AS SINGLE + IF count& = 0 THEN + arr!(0) = VAL(s$) + EXIT SUB + END IF + count& = 0 + FOR i& = 1 TO LEN(s$) + t$ = t$ + CHR$(ASC(s$, i&)) + IF ASC(s$, i&) = ASC(",") OR i& = LEN(s$) THEN + arr!(count&) = VAL(t$) + count& = count& + 1 + t$ = "" + END IF + NEXT i& + END SUB + + + '' + ' Return the visually longest element of a SINGLE array + ' + ' @param SINGLE ARRAY arr!() to check in + ' @return SINGLE value of visually longest element + ' + FUNCTION ARR_SNG.longest!(arr!()) + DIM AS LONG lb, ub, i, res, lw + lb& = LBOUND(arr!) : ub& = UBOUND(arr!) + res& = 0 : lw& = 0 + FOR i& = lb& TO ub& + IF LEN(_TRIM$(STR$(arr!(i&)))) > lw& THEN + lw& = LEN(_TRIM$(STR$(arr!(i&)))) + res& = i& + END IF + NEXT i& + ARR_SNG.longest! = arr!(res&) + END FUNCTION + + + '' + ' Perform some math on every element of a SINGLE array + ' + ' @param SINGLE ARRAY source_arr!() to do math on + ' @param SINGLE ARRAY dest_arr!() to store results in + ' @param STRING op$ one of: + ' and or xor shl shr + ' "+", "-", "*", "\", "&&", "||", "!!", "<<", ">>" + ' @param SINGLE value! to use for operand + ' + SUB ARR_SNG.math(source_arr!(), dest_arr!(), op$, value!) + DIM AS LONG lb, ub, i + lb& = LBOUND(source_arr!) : ub& = UBOUND(source_arr!) + REDIM dest_arr(lb& TO ub&) AS SINGLE + FOR i& = lb& TO ub& + SELECT CASE op$ + CASE "+": + dest_arr!(i&) = source_arr!(i&) + value! + CASE "-": + dest_arr!(i&) = source_arr!(i&) - value! + CASE "*": + dest_arr!(i&) = source_arr!(i&) * value! + CASE "\": + IF value! > 0 THEN + dest_arr!(i&) = source_arr!(i&) \ value! + END IF + CASE "&&": + dest_arr!(i&) = source_arr!(i&) AND value! + CASE "||": + dest_arr!(i&) = source_arr!(i&) OR value! + CASE "!!": + dest_arr!(i&) = source_arr!(i&) XOR value! + CASE "<<": + dest_arr!(i&) = _SHL(source_arr!(i&), value!) + CASE ">>": + dest_arr!(i&) = _SHR(source_arr!(i&), value!) + END SELECT + NEXT i& + END SUB + + + '' + ' Return the minimum element value in SINGLE array + ' + ' @param SINGLE ARRAY arr!() to check in + ' @return SINGLE minimum value found + ' + FUNCTION ARR_SNG.min!(arr!()) + DIM AS LONG lb, ub, i + DIM AS SINGLE s + lb& = LBOUND(arr!) : ub& = UBOUND(arr!) + s! = 127 + FOR i& = lb& TO ub& + IF arr!(i&) < s! THEN + s! = arr!(i&) + END IF + NEXT i& + ARR_SNG.min! = s! + END FUNCTION + + + '' + ' Return the maximum element value in SINGLE array + ' + ' @param SINGLE ARRAY arr!() to check in + ' @return SINGLE maximum value found + ' + FUNCTION ARR_SNG.max!(arr!()) + DIM AS LONG lb, ub, i + DIM AS SINGLE s + lb& = LBOUND(arr!) : ub& = UBOUND(arr!) + s! = 0 + FOR i& = lb& TO ub& + IF arr!(i&) > s! THEN + s! = arr!(i&) + END IF + NEXT i& + ARR_SNG.max! = s! + END FUNCTION + + + '' + ' Return the visually shortest element of a SINGLE array + ' + ' @param SINGLE ARRAY arr!() to check in + ' @return SINGLE value of visually shortest element + ' + FUNCTION ARR_SNG.shortest!(arr!()) + DIM AS LONG lb, ub, i, res, sw + lb& = LBOUND(arr!) : ub& = UBOUND(arr!) + res& = 0 : sw& = 0 + FOR i& = lb& TO ub& + IF LEN(_TRIM$(STR$(arr!(i&)))) < sw& THEN + sw& = LEN(_TRIM$(STR$(arr!(i&)))) + res& = i& + END IF + NEXT i& + ARR_SNG.shortest! = arr!(res&) + END FUNCTION + + + '' + ' Return the first element of a SINGLE array + ' + ' @param SINGLE ARRAY arr!() to check in + ' @return SINGLE value of first element + ' + FUNCTION ARR_SNG.first!(arr!()) + ARR_SNG.first! = arr!(LBOUND(arr!)) + END FUNCTION + + + '' + ' Return the last element of a SINGLE array + ' + ' @param SINGLE ARRAY arr!() to check in + ' @return SINGLE value of last element + ' + FUNCTION ARR_SNG.last!(arr!()) + ARR_SNG.last! = arr!(UBOUND(arr!)) + END FUNCTION + + + '' + ' Return every nth array element of a SINGLE array + ' + ' @param SINGLE ARRAY source_arr!() to get from + ' @param SINGLE ARRAY dest_arr!() to store in + ' @param INTEGER nth% element + ' + SUB ARR_SNG.nth(source_arr!(), dest_arr!(), nth%) + DIM AS LONG lb, ub, i, n + lb& = LBOUND(source_arr!) : ub& = UBOUND(source_arr!) + n& = (ub& - lb&) \ nth% + REDIM dest_arr(n&) AS SINGLE + n& = 0 + FOR i& = lb& to ub& + IF i& MOD nth% = 0 THEN + dest_arr!(n&) = source_arr!(i&) + n& = n& + 1 + END IF + NEXT i& + END SUB + + + '' + ' Checks if value exists in SINGLE array + ' + ' @param SINGLE ARRAY arr!() to check in + ' @param SINGLE value! value to check for + ' @return INTEGER -1 if found or 0 if not found + ' + FUNCTION ARR_SNG.in%(arr!(), value!) + DIM AS LONG lb, ub, i + lb& = LBOUND(arr!) : ub& = UBOUND(arr!) + FOR i& = lb& TO ub& + IF arr!(i&) = value! THEN + ARR_SNG.in% = -1 + EXIT FUNCTION + END IF + NEXT i& + ARR_SNG.in% = 0 + END FUNCTION + + + '' + ' Checks if value exists in SINGLE array and returns index if found + ' + ' @param SINGLE ARRAY arr!() to check in + ' @param SINGLE value! value to check for + ' @return INTEGER index of element if found or -1 if not found + ' + FUNCTION ARR_SNG.find%(arr!(), value!) + DIM AS LONG lb, ub, i + lb& = LBOUND(arr!) : ub& = UBOUND(arr!) + FOR i& = lb& TO ub& + IF arr!(i&) = value! THEN + ARR_SNG.find% = i& + EXIT FUNCTION + END IF + NEXT i& + ARR_SNG.find% = -1 + END FUNCTION + + + '' + ' Return the number of elements in a SINGLE array + ' + ' @param SINGLE ARRAY arr!() to count + ' @return INTEGER number of elements + ' + FUNCTION ARR_SNG.count&(arr!()) + ARR_SNG.count& = UBOUND(arr!) - LBOUND(arr!) + END FUNCTION + + + '' + ' Return the size of a SINGLE array + ' + ' @param SINGLE ARRAY arr!() to get size of + ' @return LONG size in bytes + ' + FUNCTION ARR_SNG.size&(arr!()) + ARR_SNG.size& = LEN(arr!()) + END FUNCTION + + + '' + ' Reverses the elements of a SINGLE array + ' + ' @param SINGLE ARRAY source_arr!() to reverse + ' @param SINGLE ARRAY dest_arr!() to store reversed array in + ' + SUB ARR_SNG.reverse(source_arr!(), dest_arr!()) + DIM AS LONG lb, ub, i, n + lb& = LBOUND(source_arr!) : ub& = UBOUND(source_arr!) + REDIM dest_arr(0 TO (ub& - lb&)) AS SINGLE + n& = 0 + FOR i& = ub& TO lb& STEP -1 + dest_arr!(n&) = source_arr!(i&) + n& = n& + 1 + NEXT i& + END SUB + + + '' + ' Returns a random sng from a SINGLE array + ' + ' @param SINGLE ARRAY arr!() array to get random element from + ' @return SINGLE random element + ' + FUNCTION ARR_SNG.random!(arr!()) + DIM AS LONG lb, ub + lb& = LBOUND(arr!) : ub& = UBOUND(arr!) + RANDOMIZE TIMER + ARR_SNG.random! = arr!(INT(RND * (ub& - lb&)) + 1) + END FUNCTION + + + '' + ' Returns the sum of all elements in a SINGLE array + ' + ' @param SINGLE ARRAY arr!() array to get some for + ' @return LONG sum of all elements + ' + FUNCTION ARR_SNG.sum&(arr!()) + DIM AS LONG lb, ub, i + DIM sum AS LONG + lb& = LBOUND(arr!) : ub& = UBOUND(arr!) + FOR i& = lb& TO ub& + sum& = sum& + arr!(i&) + NEXT i& + ARR_SNG.sum& = sum& + END FUNCTION + + + '' + ' Returns the average value of elements in a SINGLE array + ' + ' @param SINGLE ARRAY arr!() array to get average for + ' @return LONG average of elements + ' + FUNCTION ARR_SNG.avg&(arr!()) + DIM AS LONG lb, ub, i + DIM sum AS LONG + lb& = LBOUND(arr!) : ub& = UBOUND(arr!) + FOR i& = lb& TO ub& + sum& = sum& + arr!(i&) + NEXT i& + ARR_SNG.avg& = sum& / (ub& - lb&) + END FUNCTION + + + '' + ' Shuffle the elements of a SINGLE array + ' + ' @param SINGLE ARRAY source_arr!() to shuffle + ' @param SINGLE ARRAY dest_arr!() to store shuffled array in + ' + SUB ARR_SNG.shuffle(source_arr!(), dest_arr!()) + DIM AS LONG lb, ub, i, count + lb& = LBOUND(source_arr!) : ub& = UBOUND(source_arr!) + count& = ub& - lb& + REDIM dest_arr(0 TO count&) AS SINGLE + CALL ARR_SNG.copy(source_arr!(), dest_arr!()) + RANDOMIZE TIMER + FOR i& = 0 TO count& + SWAP dest_arr!(i&), dest_arr!(lb& + RND * (ub& - lb&)) + NEXT i& + END SUB + + + '' + ' Makes a SINGLE array contain only unique values + ' + ' @param SINGLE ARRAY source_arr!() array to get uniques for + ' @param SINGLE ARRAY dest_arr!() array to store uniques in + ' + SUB ARR_SNG.unique(source_arr!(), dest_arr!()) + DIM AS LONG lb, ub, i + DIM tmp AS SINGLE + lb& = LBOUND(source_arr!) : ub& = UBOUND(source_arr!) + DIM work_arr(0) AS SINGLE + FOR i& = lb& TO ub& + IF NOT ARR_SNG.in%(work_arr!(), source_arr!(i&)) THEN + CALL ARR_SNG.push(work_arr!(), source_arr!(i&)) + END IF + NEXT i& + CALL ARR_SNG.shift(work_arr!(), tmp!) + CALL ARR_SNG.copy(work_arr!(), dest_arr!()) + END SUB + + + '' + ' Filters a SINGLE array to only elements greater than value + ' + ' @param SINGLE ARRAY source_arr!() array to work on + ' @param SINGLE ARRAY dest_arr!() array to store in + ' @param SINGLE value! to be greater than to be returned + ' + SUB ARR_SNG.gt(source_arr!(), dest_arr!(), value!) + DIM AS LONG lb, ub, i + DIM tmp AS SINGLE + lb& = LBOUND(source_arr!) : ub& = UBOUND(source_arr!) + DIM work_arr(0) AS SINGLE + FOR i& = lb& TO ub& + IF source_arr!(i&) > value! THEN + CALL ARR_SNG.push(work_arr!(), source_arr!(i&)) + END IF + NEXT i& + CALL ARR_SNG.shift(work_arr!(), tmp!) + CALL ARR_SNG.copy(work_arr!(), dest_arr!()) + END SUB + + + '' + ' Filters a SINGLE array to only elements greater than or equal to value + ' + ' @param SINGLE ARRAY source_arr!() array to work on + ' @param SINGLE ARRAY dest_arr!() array to store in + ' @param SINGLE value! to be greater than or equal to be returned + ' + SUB ARR_SNG.gte(source_arr!(), dest_arr!(), value!) + DIM AS LONG lb, ub, i + DIM tmp AS SINGLE + lb& = LBOUND(source_arr!) : ub& = UBOUND(source_arr!) + DIM work_arr(0) AS SINGLE + FOR i& = lb& TO ub& + IF source_arr!(i&) >= value! THEN + CALL ARR_SNG.push(work_arr!(), source_arr!(i&)) + END IF + NEXT i& + CALL ARR_SNG.shift(work_arr!(), tmp!) + CALL ARR_SNG.copy(work_arr!(), dest_arr!()) + END SUB + + + '' + ' Filters a SINGLE array to only elements less than value + ' + ' @param SINGLE ARRAY source_arr!() array to work on + ' @param SINGLE ARRAY dest_arr!() array to store in + ' @param SINGLE value! to be less than to be returned + ' + SUB ARR_SNG.lt(source_arr!(), dest_arr!(), value!) + DIM AS LONG lb, ub, i + DIM tmp AS SINGLE + lb& = LBOUND(source_arr!) : ub& = UBOUND(source_arr!) + DIM work_arr(0) AS SINGLE + FOR i& = lb& TO ub& + IF source_arr!(i&) < value! THEN + CALL ARR_SNG.push(work_arr!(), source_arr!(i&)) + END IF + NEXT i& + CALL ARR_SNG.shift(work_arr!(), tmp!) + CALL ARR_SNG.copy(work_arr!(), dest_arr!()) + END SUB + + + '' + ' Filters a SINGLE array to only elements less than or equal to value + ' + ' @param SINGLE ARRAY source_arr!() array to work on + ' @param SINGLE ARRAY dest_arr!() array to store in + ' @param SINGLE value! to be less than or equal to be returned + ' + SUB ARR_SNG.lte(source_arr!(), dest_arr!(), value!) + DIM AS LONG lb, ub, i + DIM tmp AS SINGLE + lb& = LBOUND(source_arr!) : ub& = UBOUND(source_arr!) + DIM work_arr(0) AS SINGLE + FOR i& = lb& TO ub& + IF source_arr!(i&) <= value! THEN + CALL ARR_SNG.push(work_arr!(), source_arr!(i&)) + END IF + NEXT i& + CALL ARR_SNG.shift(work_arr!(), tmp!) + CALL ARR_SNG.copy(work_arr!(), dest_arr!()) + END SUB + + + '' + ' Finds and replaces values across all elements in a SINGLE ARRAY + ' + ' @param SINGLE ARRAY arr!() to check in + ' @param SINGLE find! value to find + ' @param SINGLE replace! value to replace with if found + ' + SUB ARR_SNG.replace(arr!(), find!, replace!) + DIM AS LONG lb, ub, i + lb& = LBOUND(arr!) : ub& = UBOUND(arr!) + FOR i& = lb& TO ub& + IF arr!(i&) = find! THEN + arr!(i&) = replace! + END IF + NEXT i& + END SUB + + + '' + ' Inserts a new element into SINGLE array after index + ' + ' @param SINGLE ARRAY arr!() array to work on + ' @param SINGLE value! to insert + ' @param INTEGER index% of element to insert at + ' + SUB ARR_SNG.insert(arr!(), value!, index%) + DIM AS LONG lb, ub, i + DIM tmp AS SINGLE + DIM work_arr(0) AS SINGLE + lb& = LBOUND(arr!) : ub& = UBOUND(arr!) + IF index% >= lb& AND index% <= ub& THEN + ' build new array up to index + FOR i& = lb& TO index% + CALL ARR_SNG.push(work_arr!(), arr!(i&)) + NEXT i& + ' insert new element + CALL ARR_SNG.push(work_arr!(), value!) + ' finish building array from index + 1 + FOR i& = index% + 1 TO ub& + CALL ARR_SNG.push(work_arr!(), arr!(i&)) + NEXT i& + CALL ARR_SNG.shift(work_arr!(), tmp!) + CALL ARR_SNG.copy(work_arr!(), arr!()) + END IF + END SUB + + + '' + ' Removes element from a SINGLE array by element index + ' + ' @param SINGLE ARRAY arr!() array to work on + ' @param INTEGER index% of element to remove + ' + SUB ARR_SNG.remove(arr!(), index%) + DIM AS LONG lb, ub, i + DIM tmp AS SINGLE + DIM work_arr(0) AS SINGLE + lb& = LBOUND(arr!) : ub& = UBOUND(arr!) + IF index% >= lb& AND index% <= ub& THEN + ' build new array up to index + FOR i& = lb& TO index% - 1 + CALL ARR_SNG.push(work_arr!(), arr!(i&)) + NEXT i& + ' skip elements + FOR i& = index% + 1 TO ub& + CALL ARR_SNG.push(work_arr!(), arr!(i&)) + NEXT i& + CALL ARR_SNG.shift(work_arr!(), tmp!) + CALL ARR_SNG.copy(work_arr!(), arr!()) + END IF + END SUB + + + '' + ' Filters a SINGLE array to only elements that have odd values + ' + ' @param SINGLE ARRAY source_arr!() array to work on + ' @param SINGLE ARRAY dest_arr!() array to store in + ' + SUB ARR_SNG.odd(source_arr!(), dest_arr!()) + DIM AS LONG lb, ub, i + DIM tmp AS SINGLE + lb& = LBOUND(source_arr!) : ub& = UBOUND(source_arr!) + DIM work_arr(0) AS SINGLE + FOR i& = lb& TO ub& + IF source_arr!(i&) MOD 2 <> 0 THEN + CALL ARR_SNG.push(work_arr!(), source_arr!(i&)) + END IF + NEXT i& + CALL ARR_SNG.shift(work_arr!(), tmp!) + CALL ARR_SNG.copy(work_arr!(), dest_arr!()) + END SUB + + + '' + ' Filters a SINGLE array to only elements that have even values + ' + ' @param SINGLE ARRAY source_arr!() array to work on + ' @param SINGLE ARRAY dest_arr!() array to store in + ' + SUB ARR_SNG.even(source_arr!(), dest_arr!()) + DIM AS LONG lb, ub, i + DIM tmp AS SINGLE + lb& = LBOUND(source_arr!) : ub& = UBOUND(source_arr!) + DIM work_arr(0) AS SINGLE + FOR i& = lb& TO ub& + IF source_arr!(i&) MOD 2 = 0 THEN + CALL ARR_SNG.push(work_arr!(), source_arr!(i&)) + END IF + NEXT i& + CALL ARR_SNG.shift(work_arr!(), tmp!) + CALL ARR_SNG.copy(work_arr!(), dest_arr!()) + END SUB + + + '' + ' Filters a SINGLE array to only elements that have values evenly divisible by divisor + ' + ' @param SINGLE ARRAY source_arr!() array to work on + ' @param SINGLE ARRAY dest_arr!() array to store in + ' @param SINGLE divisor! for modulo + ' + SUB ARR_SNG.mod(source_arr!(), dest_arr!(), divisor!) + DIM AS LONG lb, ub, i + DIM tmp AS SINGLE + lb& = LBOUND(source_arr!) : ub& = UBOUND(source_arr!) + DIM work_arr(0) AS SINGLE + FOR i& = lb& TO ub& + IF source_arr!(i&) MOD divisor! = 0 THEN + CALL ARR_SNG.push(work_arr!(), source_arr!(i&)) + END IF + NEXT i& + CALL ARR_SNG.shift(work_arr!(), tmp!) + CALL ARR_SNG.copy(work_arr!(), dest_arr!()) + END SUB + + + '' + ' Filters a SINGLE array to only elements between min and max + ' + ' @param SINGLE ARRAY source_arr!() array to work on + ' @param SINGLE ARRAY dest_arr!() array to store in + ' @param SINGLE min! to be greater than or equal to be returned + ' @param SINGLE max! to be less than or equal to be returned + ' + SUB ARR_SNG.between(source_arr!(), dest_arr!(), min!, max!) + DIM AS LONG lb, ub, i + DIM tmp AS SINGLE + lb& = LBOUND(source_arr!) : ub& = UBOUND(source_arr!) + DIM work_arr(0) AS SINGLE + FOR i& = lb& TO ub& + IF source_arr!(i&) >= min! _ + AND source_arr!(i&) <= max! THEN + CALL ARR_SNG.push(work_arr!(), source_arr!(i&)) + END IF + NEXT i& + CALL ARR_SNG.shift(work_arr!(), tmp!) + CALL ARR_SNG.copy(work_arr!(), dest_arr!()) + END SUB + + + '' + ' Sorts SINGLE array in ascending order + ' + ' @param SINGLE ARRAY source_arr!() array to sort + ' @param SINGLE ARRAY dest_arr!() array to store sorted in + ' + SUB ARR_SNG.sort(source_arr!(), dest_arr!()) + DIM AS LONG lb, ub, count + lb& = LBOUND(source_arr!) : ub& = UBOUND(source_arr!) + count& = ub& - lb& + REDIM dest_arr(0 TO count&) AS SINGLE + CALL ARR_SNG.copy(source_arr!(), dest_arr!()) + CALL ARR_SNG.quicksort(dest_arr!(), lb&, ub&, 0) + END SUB + + + '' + ' Sorts SINGLE array in descending order + ' + ' @param SINGLE ARRAY source_arr!() array to sort + ' @param SINGLE ARRAY dest_arr!() array to store sorted in + ' + SUB ARR_SNG.rsort(source_arr!(), dest_arr!()) + DIM AS LONG lb, ub, count + lb& = LBOUND(source_arr!) : ub& = UBOUND(source_arr!) + count& = ub& - lb& + REDIM dest_arr(0 TO count&) AS SINGLE + CALL ARR_SNG.copy(source_arr!(), dest_arr!()) + CALL ARR_SNG.quicksort(dest_arr!(), lb&, ub&, 1) + END SUB + + + '' + ' Quicksort array with pivot algorithm by logiclrd + ' + ' @link https://www.tek-tips.com/faqs.cfm?fid=336 + ' @param SINGLE ARRAY array!() to sort + ' @param INTEGER start% of range to sort + ' @param INTEGER finish% range of sort + ' @param INTEGER order% to sort by (0 = asc / 1 = desc) + ' + SUB ARR_SNG.quicksort(arr!(), start%, finish%, order%) + DIM i AS LONG + DIM pivot AS INTEGER + DIM pivotvalue AS SINGLE + + 'first, partition the array + pivot% = start% + pivotvalue! = arr!(pivot%) + FOR i& = start% + 1 TO finish% + IF order% = 0 THEN ' ascending order + IF arr!(i&) < pivotvalue! THEN + arr!(pivot%) = arr!(i&) + arr!(i&) = arr!(pivot% + 1) + arr!(pivot% + 1) = pivotvalue! + pivot% = pivot% + 1 + END IF + ELSEIF order% = 1 THEN ' descending order + IF arr!(i&) > pivotvalue! THEN + arr!(pivot%) = arr!(i&) + arr!(i&) = arr!(pivot% + 1) + arr!(pivot% + 1) = pivotvalue! + pivot% = pivot% + 1 + END IF + END IF + NEXT i& + + 'then, sort the subarrays to each side of the pivot + IF pivot% - start% >= 2 THEN + CALL ARR_SNG.quicksort(arr!(), start%, pivot% - 1, order%) + END IF + IF finish% - pivot% >= 2 THEN + CALL ARR_SNG.quicksort(arr!(), pivot% + 1, finish%, order%) + END IF + END SUB + $END IF + $IF GJ_LIB_ARR_STR_BAS = UNDEFINED THEN + '$DYNAMIC + $LET GJ_LIB_ARR_STR_BAS = 1 + + '' + ' Slice an array from source to destination starting at index and count slices + ' + ' @param STRING source_arr$() to slice from + ' @param STRING dest_arr$() to put slices into + ' @param INTEGER start_idx% starting index to use as slice range + ' @param INTEGER count% number of slices - if negative, backwards from index + ' + SUB ARR_STR.slice(source_arr$(), dest_arr$(), start_idx%, count%) + DIM AS LONG ub, lb, i, n + lb& = LBOUND(source_arr$) : ub& = UBOUND(source_arr$) + IF start_idx% < lb& OR start_idx% + count% > ub& THEN EXIT SUB ' out of range + IF ub& - lb& < count% THEN EXIT SUB ' too many and not enough + REDIM dest_arr(0 TO ABS(count%)) AS STRING + IF SGN(count%) = -1 THEN + IF ((start_idx% - 1) - ABS(count%)) < 0 THEN EXIT SUB ' out of range + n& = 0 + FOR i& = (start_idx% - 1) TO ((start_idx% - 1) - ABS(count%)) STEP -1 + dest_arr$(n&) = source_arr$(i&) + n& = n& + 1 + NEXT i& + ELSE + IF ((start_idx% + 1) + ABS(count%)) > (ub& - lb&) THEN EXIT SUB ' out of range + n& = 0 + FOR i& = start_idx% + 1 TO ((start_idx% + 1) + count%) STEP 1 + dest_arr$(n&) = source_arr$(i&) + n& = n& + 1 + NEXT i& + END IF + END SUB + + + '' + ' Push a str onto the end of a STRING array + ' + ' @param STRING arr$() array to push into + ' @param STRING value$ of byte to push + ' + SUB ARR_STR.push(arr$(), value$) + DIM AS LONG ub, lb + lb& = LBOUND(arr$) : ub& = UBOUND(arr$) + REDIM _PRESERVE arr(lb& TO (ub& + 1)) AS STRING + arr$(ub& + 1) = value$ + END SUB + + + '' + ' Pop a str from the end of a STRING array + ' + ' @param STRING arr$() array to pop from + ' @param STRING var$ of str to store popped str + ' + SUB ARR_STR.pop(arr$(), var$) + DIM AS LONG ub, lb + lb& = LBOUND(arr$) : ub& = UBOUND(arr$) + var$ = arr$(ub&) + REDIM _PRESERVE arr(lb& TO (ub& - 1)) AS STRING + END SUB + + + '' + ' Pop a str from the beginning of a STRING array + ' + ' @param STRING arr$() array to pop from + ' @param STRING var$ of str to store popped str + ' + SUB ARR_STR.shift(arr$(), var$) + DIM AS LONG ub, lb, i + lb& = LBOUND(arr$) : ub& = UBOUND(arr$) + var$ = arr$(lb&) + FOR i& = lb& TO ub& - 1 + arr$(i&) = arr$(i& + 1) + NEXT i& + REDIM _PRESERVE arr(lb& + 1 TO ub&) AS STRING + END SUB + + + '' + ' Copy an array of STRs to another STRING array + ' + ' @param STRING ARRAY source_arr$() source array to copy + ' @param STRING ARRAY dest_arr$() dest array to copy into + ' + SUB ARR_STR.copy(source_arr$(), dest_arr$()) + DIM AS LONG ub, lb, i + lb& = LBOUND(source_arr$) : ub& = UBOUND(source_arr$) + REDIM dest_arr(lb& TO ub&) AS STRING + FOR i& = lb& TO ub& + dest_arr$(i&) = source_arr$(i&) + NEXT i& + END SUB + + + '' + ' Push a str into the beginning of a STRING array + ' + ' @param STRING arr$() array to push into + ' @param STRING value$ of str to push + ' + SUB ARR_STR.unshift(arr$(), value$) + DIM AS LONG ub, lb, i + lb& = LBOUND(arr$) : ub& = UBOUND(arr$) + DIM work(lb& TO ub&) AS STRING + CALL ARR_STR.copy(arr$(), work$()) + REDIM _PRESERVE arr(lb& TO (ub& + 1)) AS STRING + FOR i& = lb& + 1 TO ub& + 1 + arr$(i&) = work$(i& - 1) + NEXT i& + arr$(lb&) = value$ + END SUB + + + '' + ' Joins an array of STRs as a string + ' + ' @param STRING ARRAY arr$() to get as a string + ' @param STRING s$ to store stringified array in + ' + SUB ARR_STR.join(arr$(), s$) + DIM AS LONG ub, lb, i + s$ = "" + lb& = LBOUND(arr$) : ub& = UBOUND(arr$) + FOR i& = lb& TO ub& + s$ = s$ + _TRIM$(arr$(i&)) + ", " + NEXT i& + ' remove trailing comma + s$ = MID$(s$, 1, (LEN(s$)-LEN(", "))) + END SUB + + + '' + ' Create a new STRING array using string of strs seperated by commas + ' + ' @param STRING ARRAY arr$() to store the strs in + ' @param STRING s$ string of comma separated strs + ' + SUB ARR_STR.new(arr$(), s$) + DIM AS LONG i, count + DIM t AS STRING + count& = 0 + FOR i& = 1 TO LEN(s$) + IF ASC(s$, i&) = ASC(",") THEN count& = count& + 1 + NEXT i& + REDIM arr(0 TO count&) AS STRING + IF count& = 0 THEN + arr$(0) = s$ + EXIT SUB + END IF + count& = 0 + FOR i& = 1 TO LEN(s$) + t$ = t$ + CHR$(ASC(s$, i&)) + IF ASC(s$, i&) = ASC(",") THEN + IF i& <> LEN(s$) THEN + arr$(count&) = LEFT$(t$, LEN(t$) - 1) + END IF + count& = count& + 1 + t$ = "" + END IF + arr$(count&) = t$ + NEXT i& + END SUB + + + '' + ' Return the visually longest element of a STRING array + ' + ' @param STRING ARRAY arr$() to check in + ' @return STRING value of visually longest element + ' + FUNCTION ARR_STR.longest$(arr$()) + DIM AS LONG lb, ub, i, res, lw + lb& = LBOUND(arr$) : ub& = UBOUND(arr$) + res& = 0 : lw& = 0 + FOR i& = lb& TO ub& + IF LEN(_TRIM$(arr$(i&))) > lw& THEN + lw& = LEN(_TRIM$(arr$(i&))) + res& = i& + END IF + NEXT i& + ARR_STR.longest$ = arr$(res&) + END FUNCTION + + + '' + ' Return the visually shortest element of a STRING array + ' + ' @param STRING ARRAY arr$() to check in + ' @return STRING value of visually shortest element + ' + FUNCTION ARR_STR.shortest$(arr$()) + DIM AS LONG lb, ub, i, res, sw + lb& = LBOUND(arr$) : ub& = UBOUND(arr$) + res& = 0 : sw& = 255 + FOR i& = lb& TO ub& + IF LEN(_TRIM$(arr$(i&))) < sw& THEN + sw& = LEN(_TRIM$(arr$(i&))) + res& = i& + END IF + NEXT i& + ARR_STR.shortest$ = arr$(res&) + END FUNCTION + + + '' + ' Return the first element of a STRING array + ' + ' @param STRING ARRAY arr$() to check in + ' @return STRING value of first element + ' + FUNCTION ARR_STR.first$(arr$()) + ARR_STR.first$ = arr$(LBOUND(arr$)) + END FUNCTION + + + '' + ' Return the last element of a STRING array + ' + ' @param STRING ARRAY arr$() to check in + ' @return STRING value of last element + ' + FUNCTION ARR_STR.last$(arr$()) + ARR_STR.last$ = arr$(UBOUND(arr$)) + END FUNCTION + + + '' + ' Return every nth array element of a STRING array + ' + ' @param STRING ARRAY source_arr$() to get from + ' @param STRING ARRAY dest_arr$() to store in + ' @param INTEGER nth% element + ' + SUB ARR_STR.nth(source_arr$(), dest_arr$(), nth%) + DIM AS LONG lb, ub, i, n + lb& = LBOUND(source_arr$) : ub& = UBOUND(source_arr$) + n& = (ub& - lb&) \ nth% + REDIM dest_arr(n&) AS STRING + n& = 0 + FOR i& = lb& to ub& + IF i& MOD nth% = 0 THEN + dest_arr$(n&) = source_arr$(i&) + n& = n& + 1 + END IF + NEXT i& + END SUB + + + '' + ' Checks if value exists in STRING array + ' + ' @param STRING ARRAY arr$() to check in + ' @param STRING value$ value to check for + ' @return INTEGER -1 if found or 0 if not found + ' + FUNCTION ARR_STR.in%(arr$(), value$) + DIM AS LONG lb, ub, i + lb& = LBOUND(arr$) : ub& = UBOUND(arr$) + FOR i& = lb& TO ub& + IF arr$(i&) = value$ THEN + ARR_STR.in% = -1 + EXIT FUNCTION + END IF + NEXT i& + ARR_STR.in% = 0 + END FUNCTION + + + '' + ' Checks if value exists in STRING array and returns index if found + ' + ' @param STRING ARRAY arr$() to check in + ' @param STRING value$ value to check for + ' @return INTEGER index of element if found or -1 if not found + ' + FUNCTION ARR_STR.find%(arr$(), value$) + DIM AS LONG lb, ub, i + lb& = LBOUND(arr$) : ub& = UBOUND(arr$) + FOR i& = lb& TO ub& + IF arr$(i&) = value$ THEN + ARR_STR.find% = i& + EXIT FUNCTION + END IF + NEXT i& + ARR_STR.find% = -1 + END FUNCTION + + + '' + ' Return the number of elements in a STRING array + ' + ' @param STRING ARRAY arr$() to count + ' @return INTEGER number of elements + ' + FUNCTION ARR_STR.count&(arr$()) + ARR_STR.count& = UBOUND(arr$) - LBOUND(arr$) + END FUNCTION + + + '' + ' Return the size of a STRING array + ' + ' @param STRING ARRAY arr$() to get size of + ' @return LONG size in bytes + ' + FUNCTION ARR_STR.size&(arr$()) + DIM AS LONG lb, ub, i, n + lb& = LBOUND(arr$) : ub& = UBOUND(arr$) + n& = 0 + FOR i& = lb& TO ub& + n& = n& + LEN(arr$(i&)) + NEXT i& + ARR_STR.size& = n& + END FUNCTION + + + '' + ' Reverses the elements of a STRING array + ' + ' @param STRING ARRAY source_arr$() to reverse + ' @param STRING ARRAY dest_arr$() to store reversed array in + ' + SUB ARR_STR.reverse(source_arr$(), dest_arr$()) + DIM AS LONG lb, ub, i, n + lb& = LBOUND(source_arr$) : ub& = UBOUND(source_arr$) + REDIM dest_arr(0 TO (ub& - lb&)) AS STRING + n& = 0 + FOR i& = ub& TO lb& STEP -1 + dest_arr$(n&) = source_arr$(i&) + n& = n& + 1 + NEXT i& + END SUB + + + '' + ' Returns a random str from a STRING array + ' + ' @param STRING ARRAY arr$() array to get random element from + ' @return STRING random element + ' + FUNCTION ARR_STR.random$(arr$()) + DIM AS LONG lb, ub + lb& = LBOUND(arr$) : ub& = UBOUND(arr$) + RANDOMIZE TIMER + ARR_STR.random$ = arr$(INT(RND * (ub& - lb&)) + 1) + END FUNCTION + + + '' + ' Shuffle the elements of a STRING array + ' + ' @param STRING ARRAY source_arr$() to shuffle + ' @param STRING ARRAY dest_arr$() to store shuffled array in + ' + SUB ARR_STR.shuffle(source_arr$(), dest_arr$()) + DIM AS LONG lb, ub, i, count + lb& = LBOUND(source_arr$) : ub& = UBOUND(source_arr$) + count& = ub& - lb& + REDIM dest_arr(0 TO count&) AS STRING + CALL ARR_STR.copy(source_arr$(), dest_arr$()) + RANDOMIZE TIMER + FOR i& = 0 TO count& + SWAP dest_arr$(i&), dest_arr$(lb& + RND * (ub& - lb&)) + NEXT i& + END SUB + + + '' + ' Makes a STRING array contain only unique values + ' + ' @param STRING ARRAY source_arr$() array to get uniques for + ' @param STRING ARRAY dest_arr$() array to store uniques in + ' + SUB ARR_STR.unique(source_arr$(), dest_arr$()) + DIM AS LONG lb, ub, i + DIM tmp AS STRING + lb& = LBOUND(source_arr$) : ub& = UBOUND(source_arr$) + DIM work_arr(0) AS STRING + FOR i& = lb& TO ub& + IF NOT ARR_STR.in%(work_arr$(), source_arr$(i&)) THEN + CALL ARR_STR.push(work_arr$(), source_arr$(i&)) + END IF + NEXT i& + CALL ARR_STR.shift(work_arr$(), tmp$) + CALL ARR_STR.copy(work_arr$(), dest_arr$()) + END SUB + + + '' + ' Finds and replaces values across all elements in a STRING ARRAY + ' + ' @param STRING ARRAY arr$() to check in + ' @param STRING find$ value to find + ' @param STRING replace$ value to replace with if found + ' + SUB ARR_STR.replace(arr$(), find$, replace$) + DIM AS LONG lb, ub, i + lb& = LBOUND(arr$) : ub& = UBOUND(arr$) + FOR i& = lb& TO ub& + IF arr$(i&) = find$ THEN + arr$(i&) = replace$ + END IF + NEXT i& + END SUB + + + '' + ' Inserts a new element into STRING array after index + ' + ' @param STRING ARRAY arr$() array to work on + ' @param STRING value$ to insert + ' @param INTEGER index% of element to insert at + ' + SUB ARR_STR.insert(arr$(), value$, index%) + DIM AS LONG lb, ub, i + DIM tmp AS STRING + DIM work_arr(0) AS STRING + lb& = LBOUND(arr$) : ub& = UBOUND(arr$) + IF index% >= lb& AND index% <= ub& THEN + ' build new array up to index + FOR i& = lb& TO index% + CALL ARR_STR.push(work_arr$(), arr$(i&)) + NEXT i& + ' insert new element + CALL ARR_STR.push(work_arr$(), value$) + ' finish building array from index + 1 + FOR i& = index% + 1 TO ub& + CALL ARR_STR.push(work_arr$(), arr$(i&)) + NEXT i& + CALL ARR_STR.shift(work_arr$(), tmp$) + CALL ARR_STR.copy(work_arr$(), arr$()) + END IF + END SUB + + + '' + ' Removes element from a STRING array by element index + ' + ' @param STRING ARRAY arr$() array to work on + ' @param INTEGER index% of element to remove + ' + SUB ARR_STR.remove(arr$(), index%) + DIM AS LONG lb, ub, i + DIM tmp AS STRING + DIM work_arr(0) AS STRING + lb& = LBOUND(arr$) : ub& = UBOUND(arr$) + IF index% >= lb& AND index% <= ub& THEN + ' build new array up to index + FOR i& = lb& TO index% - 1 + CALL ARR_STR.push(work_arr$(), arr$(i&)) + NEXT i& + ' skip elements + FOR i& = index% + 1 TO ub& + CALL ARR_STR.push(work_arr$(), arr$(i&)) + NEXT i& + CALL ARR_STR.shift(work_arr$(), tmp$) + CALL ARR_STR.copy(work_arr$(), arr$()) + END IF + END SUB + + + '' + ' Filters a STRING array to only elements that have odd values + ' + ' @param STRING ARRAY source_arr$() array to work on + ' @param STRING ARRAY dest_arr$() array to store in + ' + SUB ARR_STR.odd(source_arr$(), dest_arr$()) + DIM AS LONG lb, ub, i + DIM tmp AS STRING + lb& = LBOUND(source_arr$) : ub& = UBOUND(source_arr$) + DIM work_arr(0) AS STRING + FOR i& = lb& TO ub& + IF i& MOD 2 <> 0 THEN + CALL ARR_STR.push(work_arr$(), source_arr$(i&)) + END IF + NEXT i& + CALL ARR_STR.shift(work_arr$(), tmp$) + CALL ARR_STR.copy(work_arr$(), dest_arr$()) + END SUB + + + '' + ' Filters a STRING array to only elements that have even values + ' + ' @param STRING ARRAY source_arr$() array to work on + ' @param STRING ARRAY dest_arr$() array to store in + ' + SUB ARR_STR.even(source_arr$(), dest_arr$()) + DIM AS LONG lb, ub, i + DIM tmp AS STRING + lb& = LBOUND(source_arr$) : ub& = UBOUND(source_arr$) + DIM work_arr(0) AS STRING + FOR i& = lb& TO ub& + IF i& MOD 2 = 0 THEN + CALL ARR_STR.push(work_arr$(), source_arr$(i&)) + END IF + NEXT i& + CALL ARR_STR.shift(work_arr$(), tmp$) + CALL ARR_STR.copy(work_arr$(), dest_arr$()) + END SUB + + + '' + ' Filters a STRING array to only elements that have values evenly divisible by divisor + ' + ' @param STRING ARRAY source_arr$() array to work on + ' @param STRING ARRAY dest_arr$() array to store in + ' @param INTEGER divisor% for modulo + ' + SUB ARR_STR.mod(source_arr$(), dest_arr$(), divisor%) + DIM AS LONG lb, ub, i + DIM tmp AS STRING + lb& = LBOUND(source_arr$) : ub& = UBOUND(source_arr$) + DIM work_arr(0) AS STRING + FOR i& = lb& TO ub& + IF i& MOD divisor% = 0 THEN + CALL ARR_STR.push(work_arr$(), source_arr$(i&)) + END IF + NEXT i& + CALL ARR_STR.shift(work_arr$(), tmp$) + CALL ARR_STR.copy(work_arr$(), dest_arr$()) + END SUB + + + '' + ' Filters a STRING array to only elements between min and max + ' + ' @param STRING ARRAY source_arr$() array to work on + ' @param STRING ARRAY dest_arr$() array to store in + ' @param INTEGER min% to be greater than or equal to be returned + ' @param INTEGER max% to be less than or equal to be returned + ' + SUB ARR_STR.between(source_arr$(), dest_arr$(), min%, max%) + DIM AS LONG lb, ub, i + DIM tmp AS STRING + lb& = LBOUND(source_arr$) : ub& = UBOUND(source_arr$) + DIM work_arr(0) AS STRING + FOR i& = lb& TO ub& + IF VAL(source_arr$(i&)) >= min% _ + AND VAL(source_arr$(i&)) <= max% THEN + CALL ARR_STR.push(work_arr$(), source_arr$(i&)) + END IF + NEXT i& + CALL ARR_STR.shift(work_arr$(), tmp$) + CALL ARR_STR.copy(work_arr$(), dest_arr$()) + END SUB + + + '' + ' Sorts STRING array in ascending order + ' + ' @param STRING ARRAY source_arr$() array to sort + ' @param STRING ARRAY dest_arr$() array to store sorted in + ' + SUB ARR_STR.sort(source_arr$(), dest_arr$()) + DIM AS LONG lb, ub, count + lb& = LBOUND(source_arr$) : ub& = UBOUND(source_arr$) + count& = ub& - lb& + REDIM dest_arr(0 TO count&) AS STRING + CALL ARR_STR.copy(source_arr$(), dest_arr$()) + CALL ARR_STR.quicksort(dest_arr$(), lb&, ub&, 0) + END SUB + + + '' + ' Sorts STRING array in descending order + ' + ' @param STRING ARRAY source_arr$() array to sort + ' @param STRING ARRAY dest_arr$() array to store sorted in + ' + SUB ARR_STR.rsort(source_arr$(), dest_arr$()) + DIM AS LONG lb, ub, count + lb& = LBOUND(source_arr$) : ub& = UBOUND(source_arr$) + count& = ub& - lb& + REDIM dest_arr(0 TO count&) AS STRING + CALL ARR_STR.copy(source_arr$(), dest_arr$()) + CALL ARR_STR.quicksort(dest_arr$(), lb&, ub&, 1) + END SUB + + + '' + ' Quicksort array with pivot algorithm by logiclrd + ' + ' @link https://www.tek-tips.com/faqs.cfm?fid=336 + ' @param STRING ARRAY array$() to sort + ' @param INTEGER start% of range to sort + ' @param INTEGER finish% range of sort + ' @param INTEGER order% to sort by (0 = asc / 1 = desc) + ' + SUB ARR_STR.quicksort(arr$(), start%, finish%, order%) + DIM i AS LONG + DIM pivot AS INTEGER + DIM pivotvalue AS STRING + + 'first, partition the array + pivot% = start% + pivotvalue$ = arr$(pivot%) + FOR i& = start% + 1 TO finish% + IF order% = 0 THEN ' ascending order + IF arr$(i&) < pivotvalue$ THEN + arr$(pivot%) = arr$(i&) + arr$(i&) = arr$(pivot% + 1) + arr$(pivot% + 1) = pivotvalue$ + pivot% = pivot% + 1 + END IF + ELSEIF order% = 1 THEN ' descending order + IF arr$(i&) > pivotvalue$ THEN + arr$(pivot%) = arr$(i&) + arr$(i&) = arr$(pivot% + 1) + arr$(pivot% + 1) = pivotvalue$ + pivot% = pivot% + 1 + END IF + END IF + NEXT i& + + 'then, sort the subarrays to each side of the pivot + IF pivot% - start% >= 2 THEN + CALL ARR_STR.quicksort(arr$(), start%, pivot% - 1, order%) + END IF + IF finish% - pivot% >= 2 THEN + CALL ARR_STR.quicksort(arr$(), pivot% + 1, finish%, order%) + END IF + END SUB + $END IF + $IF GJ_LIB_ARR_UBYTE_BAS = UNDEFINED THEN + '$DYNAMIC + $LET GJ_LIB_ARR_UBYTE_BAS = 1 + + '' + ' Slice an array from source to destination starting at index and count slices + ' + ' @param _UNSIGNED _BYTE() source_arr~%% to slice from + ' @param _UNSIGNED _BYTE() dest_arr~%% to put slices into + ' @param INTEGER start_idx% starting index to use as slice range + ' @param INTEGER count% number of slices - if negative, backwards from index + ' + SUB ARR_UBYTE.slice(source_arr~%%(), dest_arr~%%(), start_idx%, count%) + DIM AS LONG ub, lb, i, n + lb& = LBOUND(source_arr~%%) : ub& = UBOUND(source_arr~%%) + IF start_idx% < lb& OR start_idx% + count% > ub& THEN EXIT SUB ' out of range + IF ub& - lb& < count% THEN EXIT SUB ' too many and not enough + REDIM dest_arr(0 TO ABS(count%)) AS _UNSIGNED _BYTE + IF SGN(count%) = -1 THEN + IF ((start_idx% - 1) - ABS(count%)) < 0 THEN EXIT SUB ' out of range + n& = 0 + FOR i& = (start_idx% - 1) TO ((start_idx% - 1) - ABS(count%)) STEP -1 + dest_arr~%%(n&) = source_arr~%%(i&) + n& = n& + 1 + NEXT i& + ELSE + IF ((start_idx% + 1) + ABS(count%)) > (ub& - lb&) THEN EXIT SUB ' out of range + n& = 0 + FOR i& = start_idx% + 1 TO ((start_idx% + 1) + count%) STEP 1 + dest_arr~%%(n&) = source_arr~%%(i&) + n& = n& + 1 + NEXT i& + END IF + END SUB + + + '' + ' Push a ubyte onto the end of a _UNSIGNED _BYTE array + ' + ' @param _UNSIGNED _BYTE arr~%%() array to push into + ' @param _UNSIGNED _BYTE value~%% of byte to push + ' + SUB ARR_UBYTE.push(arr~%%(), value~%%) + DIM AS LONG ub, lb + lb& = LBOUND(arr~%%) : ub& = UBOUND(arr~%%) + REDIM _PRESERVE arr(lb& TO (ub& + 1)) AS _UNSIGNED _BYTE + arr~%%(ub& + 1) = value~%% + END SUB + + + '' + ' Pop a ubyte from the end of a _UNSIGNED _BYTE array + ' + ' @param _UNSIGNED _BYTE arr~%%() array to pop from + ' @param _UNSIGNED _BYTE var~%% of ubyte to store popped ubyte + ' + SUB ARR_UBYTE.pop(arr~%%(), var~%%) + DIM AS LONG ub, lb + lb& = LBOUND(arr~%%) : ub& = UBOUND(arr~%%) + var~%% = arr~%%(ub&) + REDIM _PRESERVE arr(lb& TO (ub& - 1)) AS _UNSIGNED _BYTE + END SUB + + + '' + ' Pop a ubyte from the beginning of a _UNSIGNED _BYTE array + ' + ' @param _UNSIGNED _BYTE arr~%%() array to pop from + ' @param _UNSIGNED _BYTE var~%% of ubyte to store popped ubyte + ' + SUB ARR_UBYTE.shift(arr~%%(), var~%%) + DIM AS LONG ub, lb, i + lb& = LBOUND(arr~%%) : ub& = UBOUND(arr~%%) + var~%% = arr~%%(lb&) + FOR i& = lb& TO ub& - 1 + arr~%%(i&) = arr~%%(i& + 1) + NEXT i& + REDIM _PRESERVE arr(lb& + 1 TO ub&) AS _UNSIGNED _BYTE + END SUB + + + '' + ' Copy an array of UBYTEs to another _UNSIGNED _BYTE array + ' + ' @param _UNSIGNED _BYTE ARRAY source_arr~%%() source array to copy + ' @param _UNSIGNED _BYTE ARRAY dest_arr~%%() dest array to copy into + ' + SUB ARR_UBYTE.copy(source_arr~%%(), dest_arr~%%()) + DIM AS LONG ub, lb, i + lb& = LBOUND(source_arr~%%) : ub& = UBOUND(source_arr~%%) + REDIM dest_arr(lb& TO ub&) AS _UNSIGNED _BYTE + FOR i& = lb& TO ub& + dest_arr~%%(i&) = source_arr~%%(i&) + NEXT i& + END SUB + + + '' + ' Push a ubyte into the beginning of a _UNSIGNED _BYTE array + ' + ' @param _UNSIGNED _BYTE arr~%%() array to push into + ' @param _UNSIGNED _BYTE value~%% of ubyte to push + ' + SUB ARR_UBYTE.unshift(arr~%%(), value~%%) + DIM AS LONG ub, lb, i + lb& = LBOUND(arr~%%) : ub& = UBOUND(arr~%%) + DIM work(lb& TO ub&) AS _UNSIGNED _BYTE + CALL ARR_UBYTE.copy(arr~%%(), work~%%()) + REDIM _PRESERVE arr(lb& TO (ub& + 1)) AS _UNSIGNED _BYTE + FOR i& = lb& + 1 TO ub& + 1 + arr~%%(i&) = work~%%(i& - 1) + NEXT i& + arr~%%(lb&) = value~%% + END SUB + + + '' + ' Joins an array of UBYTEs as a string + ' + ' @param _UNSIGNED _BYTE ARRAY arr~%%() to get as a string + ' @param STRING s$ to store stringified array in + ' + SUB ARR_UBYTE.join(arr~%%(), s$) + DIM AS LONG ub, lb, i + s$ = "" + lb& = LBOUND(arr~%%) : ub& = UBOUND(arr~%%) + FOR i& = lb& TO ub& + s$ = s$ + _TRIM$(STR$(arr~%%(i&))) + ", " + NEXT i& + ' remove trailing comma + s$ = MID$(s$, 1, (LEN(s$)-LEN(", "))) + END SUB + + + '' + ' Create a new _UNSIGNED _BYTE array using string of ubytes seperated by commas + ' + ' @param _UNSIGNED _BYTE ARRAY arr~%%() to store the ubytes in + ' @param STRING s$ string of comma separated ubytes + ' + SUB ARR_UBYTE.new(arr~%%(), s$) + DIM AS LONG i, count + DIM t AS STRING + count& = 0 + FOR i& = 1 TO LEN(s$) + IF ASC(s$, i&) = ASC(",") THEN count& = count& + 1 + NEXT i& + REDIM arr(0 TO count&) AS _UNSIGNED _BYTE + IF count& = 0 THEN + arr~%%(0) = VAL(s$) + EXIT SUB + END IF + count& = 0 + FOR i& = 1 TO LEN(s$) + t$ = t$ + CHR$(ASC(s$, i&)) + IF ASC(s$, i&) = ASC(",") OR i& = LEN(s$) THEN + arr~%%(count&) = VAL(t$) + count& = count& + 1 + t$ = "" + END IF + NEXT i& + END SUB + + + '' + ' Return the visually longest element of a _UNSIGNED _BYTE array + ' + ' @param _UNSIGNED _BYTE ARRAY arr~%%() to check in + ' @return _UNSIGNED _BYTE value of visually longest element + ' + FUNCTION ARR_UBYTE.longest~%%(arr~%%()) + DIM AS LONG lb, ub, i, res, lw + lb& = LBOUND(arr~%%) : ub& = UBOUND(arr~%%) + res& = 0 : lw& = 0 + FOR i& = lb& TO ub& + IF LEN(_TRIM$(STR$(arr~%%(i&)))) > lw& THEN + lw& = LEN(_TRIM$(STR$(arr~%%(i&)))) + res& = i& + END IF + NEXT i& + ARR_UBYTE.longest~%% = arr~%%(res&) + END FUNCTION + + + '' + ' Perform some math on every element of a _UNSIGNED _BYTE array + ' + ' @param _UNSIGNED _BYTE ARRAY source_arr~%%() to do math on + ' @param _UNSIGNED _BYTE ARRAY dest_arr~%%() to store results in + ' @param STRING op$ one of: + ' and or xor shl shr + ' "+", "-", "*", "\", "&&", "||", "!!", "<<", ">>" + ' @param _UNSIGNED _BYTE value~%% to use for operand + ' + SUB ARR_UBYTE.math(source_arr~%%(), dest_arr~%%(), op$, value~%%) + DIM AS LONG lb, ub, i + lb& = LBOUND(source_arr~%%) : ub& = UBOUND(source_arr~%%) + REDIM dest_arr(lb& TO ub&) AS _UNSIGNED _BYTE + FOR i& = lb& TO ub& + SELECT CASE op$ + CASE "+": + dest_arr~%%(i&) = source_arr~%%(i&) + value~%% + CASE "-": + dest_arr~%%(i&) = source_arr~%%(i&) - value~%% + CASE "*": + dest_arr~%%(i&) = source_arr~%%(i&) * value~%% + CASE "\": + IF value~%% > 0 THEN + dest_arr~%%(i&) = source_arr~%%(i&) \ value~%% + END IF + CASE "&&": + dest_arr~%%(i&) = source_arr~%%(i&) AND value~%% + CASE "||": + dest_arr~%%(i&) = source_arr~%%(i&) OR value~%% + CASE "!!": + dest_arr~%%(i&) = source_arr~%%(i&) XOR value~%% + CASE "<<": + dest_arr~%%(i&) = _SHL(source_arr~%%(i&), value~%%) + CASE ">>": + dest_arr~%%(i&) = _SHR(source_arr~%%(i&), value~%%) + END SELECT + NEXT i& + END SUB + + + '' + ' Return the minimum element value in _UNSIGNED _BYTE array + ' + ' @param _UNSIGNED _BYTE ARRAY arr~%%() to check in + ' @return _UNSIGNED _BYTE minimum value found + ' + FUNCTION ARR_UBYTE.min~%%(arr~%%()) + DIM AS LONG lb, ub, i + DIM AS _UNSIGNED _BYTE s + lb& = LBOUND(arr~%%) : ub& = UBOUND(arr~%%) + s~%% = 127 + FOR i& = lb& TO ub& + IF arr~%%(i&) < s~%% THEN + s~%% = arr~%%(i&) + END IF + NEXT i& + ARR_UBYTE.min~%% = s~%% + END FUNCTION + + + '' + ' Return the maximum element value in _UNSIGNED _BYTE array + ' + ' @param _UNSIGNED _BYTE ARRAY arr~%%() to check in + ' @return _UNSIGNED _BYTE maximum value found + ' + FUNCTION ARR_UBYTE.max~%%(arr~%%()) + DIM AS LONG lb, ub, i + DIM AS _UNSIGNED _BYTE s + lb& = LBOUND(arr~%%) : ub& = UBOUND(arr~%%) + s~%% = 0 + FOR i& = lb& TO ub& + IF arr~%%(i&) > s~%% THEN + s~%% = arr~%%(i&) + END IF + NEXT i& + ARR_UBYTE.max~%% = s~%% + END FUNCTION + + + '' + ' Return the visually shortest element of a _UNSIGNED _BYTE array + ' + ' @param _UNSIGNED _BYTE ARRAY arr~%%() to check in + ' @return _UNSIGNED _BYTE value of visually shortest element + ' + FUNCTION ARR_UBYTE.shortest~%%(arr~%%()) + DIM AS LONG lb, ub, i, res, sw + lb& = LBOUND(arr~%%) : ub& = UBOUND(arr~%%) + res& = 0 : sw& = 0 + FOR i& = lb& TO ub& + IF LEN(_TRIM$(STR$(arr~%%(i&)))) < sw& THEN + sw& = LEN(_TRIM$(STR$(arr~%%(i&)))) + res& = i& + END IF + NEXT i& + ARR_UBYTE.shortest~%% = arr~%%(res&) + END FUNCTION + + + '' + ' Return the first element of a _UNSIGNED _BYTE array + ' + ' @param _UNSIGNED _BYTE ARRAY arr~%%() to check in + ' @return _UNSIGNED _BYTE value of first element + ' + FUNCTION ARR_UBYTE.first~%%(arr~%%()) + ARR_UBYTE.first~%% = arr~%%(LBOUND(arr~%%)) + END FUNCTION + + + '' + ' Return the last element of a _UNSIGNED _BYTE array + ' + ' @param _UNSIGNED _BYTE ARRAY arr~%%() to check in + ' @return _UNSIGNED _BYTE value of last element + ' + FUNCTION ARR_UBYTE.last~%%(arr~%%()) + ARR_UBYTE.last~%% = arr~%%(UBOUND(arr~%%)) + END FUNCTION + + + '' + ' Return every nth array element of a _UNSIGNED _BYTE array + ' + ' @param _UNSIGNED _BYTE ARRAY source_arr~%%() to get from + ' @param _UNSIGNED _BYTE ARRAY dest_arr~%%() to store in + ' @param INTEGER nth% element + ' + SUB ARR_UBYTE.nth(source_arr~%%(), dest_arr~%%(), nth%) + DIM AS LONG lb, ub, i, n + lb& = LBOUND(source_arr~%%) : ub& = UBOUND(source_arr~%%) + n& = (ub& - lb&) \ nth% + REDIM dest_arr(n&) AS _UNSIGNED _BYTE + n& = 0 + FOR i& = lb& to ub& + IF i& MOD nth% = 0 THEN + dest_arr~%%(n&) = source_arr~%%(i&) + n& = n& + 1 + END IF + NEXT i& + END SUB + + + '' + ' Checks if value exists in _UNSIGNED _BYTE array + ' + ' @param _UNSIGNED _BYTE ARRAY arr~%%() to check in + ' @param _UNSIGNED _BYTE value~%% value to check for + ' @return INTEGER -1 if found or 0 if not found + ' + FUNCTION ARR_UBYTE.in%(arr~%%(), value~%%) + DIM AS LONG lb, ub, i + lb& = LBOUND(arr~%%) : ub& = UBOUND(arr~%%) + FOR i& = lb& TO ub& + IF arr~%%(i&) = value~%% THEN + ARR_UBYTE.in% = -1 + EXIT FUNCTION + END IF + NEXT i& + ARR_UBYTE.in% = 0 + END FUNCTION + + + '' + ' Checks if value exists in _UNSIGNED _BYTE array and returns index if found + ' + ' @param _UNSIGNED _BYTE ARRAY arr~%%() to check in + ' @param _UNSIGNED _BYTE value~%% value to check for + ' @return INTEGER index of element if found or -1 if not found + ' + FUNCTION ARR_UBYTE.find%(arr~%%(), value~%%) + DIM AS LONG lb, ub, i + lb& = LBOUND(arr~%%) : ub& = UBOUND(arr~%%) + FOR i& = lb& TO ub& + IF arr~%%(i&) = value~%% THEN + ARR_UBYTE.find% = i& + EXIT FUNCTION + END IF + NEXT i& + ARR_UBYTE.find% = -1 + END FUNCTION + + + '' + ' Return the number of elements in a _UNSIGNED _BYTE array + ' + ' @param _UNSIGNED _BYTE ARRAY arr~%%() to count + ' @return INTEGER number of elements + ' + FUNCTION ARR_UBYTE.count&(arr~%%()) + ARR_UBYTE.count& = UBOUND(arr~%%) - LBOUND(arr~%%) + END FUNCTION + + + '' + ' Return the size of a _UNSIGNED _BYTE array + ' + ' @param _UNSIGNED _BYTE ARRAY arr~%%() to get size of + ' @return LONG size in bytes + ' + FUNCTION ARR_UBYTE.size&(arr~%%()) + ARR_UBYTE.size& = LEN(arr~%%()) + END FUNCTION + + + '' + ' Reverses the elements of a _UNSIGNED _BYTE array + ' + ' @param _UNSIGNED _BYTE ARRAY source_arr~%%() to reverse + ' @param _UNSIGNED _BYTE ARRAY dest_arr~%%() to store reversed array in + ' + SUB ARR_UBYTE.reverse(source_arr~%%(), dest_arr~%%()) + DIM AS LONG lb, ub, i, n + lb& = LBOUND(source_arr~%%) : ub& = UBOUND(source_arr~%%) + REDIM dest_arr(0 TO (ub& - lb&)) AS _UNSIGNED _BYTE + n& = 0 + FOR i& = ub& TO lb& STEP -1 + dest_arr~%%(n&) = source_arr~%%(i&) + n& = n& + 1 + NEXT i& + END SUB + + + '' + ' Returns a random ubyte from a _UNSIGNED _BYTE array + ' + ' @param _UNSIGNED _BYTE ARRAY arr~%%() array to get random element from + ' @return _UNSIGNED _BYTE random element + ' + FUNCTION ARR_UBYTE.random~%%(arr~%%()) + DIM AS LONG lb, ub + lb& = LBOUND(arr~%%) : ub& = UBOUND(arr~%%) + RANDOMIZE TIMER + ARR_UBYTE.random~%% = arr~%%(INT(RND * (ub& - lb&)) + 1) + END FUNCTION + + + '' + ' Returns the sum of all elements in a _UNSIGNED _BYTE array + ' + ' @param _UNSIGNED _BYTE ARRAY arr~%%() array to get some for + ' @return LONG sum of all elements + ' + FUNCTION ARR_UBYTE.sum&(arr~%%()) + DIM AS LONG lb, ub, i + DIM sum AS LONG + lb& = LBOUND(arr~%%) : ub& = UBOUND(arr~%%) + FOR i& = lb& TO ub& + sum& = sum& + arr~%%(i&) + NEXT i& + ARR_UBYTE.sum& = sum& + END FUNCTION + + + '' + ' Returns the average value of elements in a _UNSIGNED _BYTE array + ' + ' @param _UNSIGNED _BYTE ARRAY arr~%%() array to get average for + ' @return LONG average of elements + ' + FUNCTION ARR_UBYTE.avg&(arr~%%()) + DIM AS LONG lb, ub, i + DIM sum AS LONG + lb& = LBOUND(arr~%%) : ub& = UBOUND(arr~%%) + FOR i& = lb& TO ub& + sum& = sum& + arr~%%(i&) + NEXT i& + ARR_UBYTE.avg& = sum& / (ub& - lb&) + END FUNCTION + + + '' + ' Shuffle the elements of a _UNSIGNED _BYTE array + ' + ' @param _UNSIGNED _BYTE ARRAY source_arr~%%() to shuffle + ' @param _UNSIGNED _BYTE ARRAY dest_arr~%%() to store shuffled array in + ' + SUB ARR_UBYTE.shuffle(source_arr~%%(), dest_arr~%%()) + DIM AS LONG lb, ub, i, count + lb& = LBOUND(source_arr~%%) : ub& = UBOUND(source_arr~%%) + count& = ub& - lb& + REDIM dest_arr(0 TO count&) AS _UNSIGNED _BYTE + CALL ARR_UBYTE.copy(source_arr~%%(), dest_arr~%%()) + RANDOMIZE TIMER + FOR i& = 0 TO count& + SWAP dest_arr~%%(i&), dest_arr~%%(lb& + RND * (ub& - lb&)) + NEXT i& + END SUB + + + '' + ' Makes a _UNSIGNED _BYTE array contain only unique values + ' + ' @param _UNSIGNED _BYTE ARRAY source_arr~%%() array to get uniques for + ' @param _UNSIGNED _BYTE ARRAY dest_arr~%%() array to store uniques in + ' + SUB ARR_UBYTE.unique(source_arr~%%(), dest_arr~%%()) + DIM AS LONG lb, ub, i + DIM tmp AS _UNSIGNED _BYTE + lb& = LBOUND(source_arr~%%) : ub& = UBOUND(source_arr~%%) + DIM work_arr(0) AS _UNSIGNED _BYTE + FOR i& = lb& TO ub& + IF NOT ARR_UBYTE.in%(work_arr~%%(), source_arr~%%(i&)) THEN + CALL ARR_UBYTE.push(work_arr~%%(), source_arr~%%(i&)) + END IF + NEXT i& + CALL ARR_UBYTE.shift(work_arr~%%(), tmp~%%) + CALL ARR_UBYTE.copy(work_arr~%%(), dest_arr~%%()) + END SUB + + + '' + ' Filters a _UNSIGNED _BYTE array to only elements greater than value + ' + ' @param _UNSIGNED _BYTE ARRAY source_arr~%%() array to work on + ' @param _UNSIGNED _BYTE ARRAY dest_arr~%%() array to store in + ' @param _UNSIGNED _BYTE value~%% to be greater than to be returned + ' + SUB ARR_UBYTE.gt(source_arr~%%(), dest_arr~%%(), value~%%) + DIM AS LONG lb, ub, i + DIM tmp AS _UNSIGNED _BYTE + lb& = LBOUND(source_arr~%%) : ub& = UBOUND(source_arr~%%) + DIM work_arr(0) AS _UNSIGNED _BYTE + FOR i& = lb& TO ub& + IF source_arr~%%(i&) > value~%% THEN + CALL ARR_UBYTE.push(work_arr~%%(), source_arr~%%(i&)) + END IF + NEXT i& + CALL ARR_UBYTE.shift(work_arr~%%(), tmp~%%) + CALL ARR_UBYTE.copy(work_arr~%%(), dest_arr~%%()) + END SUB + + + '' + ' Filters a _UNSIGNED _BYTE array to only elements greater than or equal to value + ' + ' @param _UNSIGNED _BYTE ARRAY source_arr~%%() array to work on + ' @param _UNSIGNED _BYTE ARRAY dest_arr~%%() array to store in + ' @param _UNSIGNED _BYTE value~%% to be greater than or equal to be returned + ' + SUB ARR_UBYTE.gte(source_arr~%%(), dest_arr~%%(), value~%%) + DIM AS LONG lb, ub, i + DIM tmp AS _UNSIGNED _BYTE + lb& = LBOUND(source_arr~%%) : ub& = UBOUND(source_arr~%%) + DIM work_arr(0) AS _UNSIGNED _BYTE + FOR i& = lb& TO ub& + IF source_arr~%%(i&) >= value~%% THEN + CALL ARR_UBYTE.push(work_arr~%%(), source_arr~%%(i&)) + END IF + NEXT i& + CALL ARR_UBYTE.shift(work_arr~%%(), tmp~%%) + CALL ARR_UBYTE.copy(work_arr~%%(), dest_arr~%%()) + END SUB + + + '' + ' Filters a _UNSIGNED _BYTE array to only elements less than value + ' + ' @param _UNSIGNED _BYTE ARRAY source_arr~%%() array to work on + ' @param _UNSIGNED _BYTE ARRAY dest_arr~%%() array to store in + ' @param _UNSIGNED _BYTE value~%% to be less than to be returned + ' + SUB ARR_UBYTE.lt(source_arr~%%(), dest_arr~%%(), value~%%) + DIM AS LONG lb, ub, i + DIM tmp AS _UNSIGNED _BYTE + lb& = LBOUND(source_arr~%%) : ub& = UBOUND(source_arr~%%) + DIM work_arr(0) AS _UNSIGNED _BYTE + FOR i& = lb& TO ub& + IF source_arr~%%(i&) < value~%% THEN + CALL ARR_UBYTE.push(work_arr~%%(), source_arr~%%(i&)) + END IF + NEXT i& + CALL ARR_UBYTE.shift(work_arr~%%(), tmp~%%) + CALL ARR_UBYTE.copy(work_arr~%%(), dest_arr~%%()) + END SUB + + + '' + ' Filters a _UNSIGNED _BYTE array to only elements less than or equal to value + ' + ' @param _UNSIGNED _BYTE ARRAY source_arr~%%() array to work on + ' @param _UNSIGNED _BYTE ARRAY dest_arr~%%() array to store in + ' @param _UNSIGNED _BYTE value~%% to be less than or equal to be returned + ' + SUB ARR_UBYTE.lte(source_arr~%%(), dest_arr~%%(), value~%%) + DIM AS LONG lb, ub, i + DIM tmp AS _UNSIGNED _BYTE + lb& = LBOUND(source_arr~%%) : ub& = UBOUND(source_arr~%%) + DIM work_arr(0) AS _UNSIGNED _BYTE + FOR i& = lb& TO ub& + IF source_arr~%%(i&) <= value~%% THEN + CALL ARR_UBYTE.push(work_arr~%%(), source_arr~%%(i&)) + END IF + NEXT i& + CALL ARR_UBYTE.shift(work_arr~%%(), tmp~%%) + CALL ARR_UBYTE.copy(work_arr~%%(), dest_arr~%%()) + END SUB + + + '' + ' Finds and replaces values across all elements in a _UNSIGNED _BYTE ARRAY + ' + ' @param _UNSIGNED _BYTE ARRAY arr~%%() to check in + ' @param _UNSIGNED _BYTE find~%% value to find + ' @param _UNSIGNED _BYTE replace~%% value to replace with if found + ' + SUB ARR_UBYTE.replace(arr~%%(), find~%%, replace~%%) + DIM AS LONG lb, ub, i + lb& = LBOUND(arr~%%) : ub& = UBOUND(arr~%%) + FOR i& = lb& TO ub& + IF arr~%%(i&) = find~%% THEN + arr~%%(i&) = replace~%% + END IF + NEXT i& + END SUB + + + '' + ' Inserts a new element into _UNSIGNED _BYTE array after index + ' + ' @param _UNSIGNED _BYTE ARRAY arr~%%() array to work on + ' @param _UNSIGNED _BYTE value~%% to insert + ' @param INTEGER index% of element to insert at + ' + SUB ARR_UBYTE.insert(arr~%%(), value~%%, index%) + DIM AS LONG lb, ub, i + DIM tmp AS _UNSIGNED _BYTE + DIM work_arr(0) AS _UNSIGNED _BYTE + lb& = LBOUND(arr~%%) : ub& = UBOUND(arr~%%) + IF index% >= lb& AND index% <= ub& THEN + ' build new array up to index + FOR i& = lb& TO index% + CALL ARR_UBYTE.push(work_arr~%%(), arr~%%(i&)) + NEXT i& + ' insert new element + CALL ARR_UBYTE.push(work_arr~%%(), value~%%) + ' finish building array from index + 1 + FOR i& = index% + 1 TO ub& + CALL ARR_UBYTE.push(work_arr~%%(), arr~%%(i&)) + NEXT i& + CALL ARR_UBYTE.shift(work_arr~%%(), tmp~%%) + CALL ARR_UBYTE.copy(work_arr~%%(), arr~%%()) + END IF + END SUB + + + '' + ' Removes element from a _UNSIGNED _BYTE array by element index + ' + ' @param _UNSIGNED _BYTE ARRAY arr~%%() array to work on + ' @param INTEGER index% of element to remove + ' + SUB ARR_UBYTE.remove(arr~%%(), index%) + DIM AS LONG lb, ub, i + DIM tmp AS _UNSIGNED _BYTE + DIM work_arr(0) AS _UNSIGNED _BYTE + lb& = LBOUND(arr~%%) : ub& = UBOUND(arr~%%) + IF index% >= lb& AND index% <= ub& THEN + ' build new array up to index + FOR i& = lb& TO index% - 1 + CALL ARR_UBYTE.push(work_arr~%%(), arr~%%(i&)) + NEXT i& + ' skip elements + FOR i& = index% + 1 TO ub& + CALL ARR_UBYTE.push(work_arr~%%(), arr~%%(i&)) + NEXT i& + CALL ARR_UBYTE.shift(work_arr~%%(), tmp~%%) + CALL ARR_UBYTE.copy(work_arr~%%(), arr~%%()) + END IF + END SUB + + + '' + ' Filters a _UNSIGNED _BYTE array to only elements that have odd values + ' + ' @param _UNSIGNED _BYTE ARRAY source_arr~%%() array to work on + ' @param _UNSIGNED _BYTE ARRAY dest_arr~%%() array to store in + ' + SUB ARR_UBYTE.odd(source_arr~%%(), dest_arr~%%()) + DIM AS LONG lb, ub, i + DIM tmp AS _UNSIGNED _BYTE + lb& = LBOUND(source_arr~%%) : ub& = UBOUND(source_arr~%%) + DIM work_arr(0) AS _UNSIGNED _BYTE + FOR i& = lb& TO ub& + IF source_arr~%%(i&) MOD 2 <> 0 THEN + CALL ARR_UBYTE.push(work_arr~%%(), source_arr~%%(i&)) + END IF + NEXT i& + CALL ARR_UBYTE.shift(work_arr~%%(), tmp~%%) + CALL ARR_UBYTE.copy(work_arr~%%(), dest_arr~%%()) + END SUB + + + '' + ' Filters a _UNSIGNED _BYTE array to only elements that have even values + ' + ' @param _UNSIGNED _BYTE ARRAY source_arr~%%() array to work on + ' @param _UNSIGNED _BYTE ARRAY dest_arr~%%() array to store in + ' + SUB ARR_UBYTE.even(source_arr~%%(), dest_arr~%%()) + DIM AS LONG lb, ub, i + DIM tmp AS _UNSIGNED _BYTE + lb& = LBOUND(source_arr~%%) : ub& = UBOUND(source_arr~%%) + DIM work_arr(0) AS _UNSIGNED _BYTE + FOR i& = lb& TO ub& + IF source_arr~%%(i&) MOD 2 = 0 THEN + CALL ARR_UBYTE.push(work_arr~%%(), source_arr~%%(i&)) + END IF + NEXT i& + CALL ARR_UBYTE.shift(work_arr~%%(), tmp~%%) + CALL ARR_UBYTE.copy(work_arr~%%(), dest_arr~%%()) + END SUB + + + '' + ' Filters a _UNSIGNED _BYTE array to only elements that have values evenly divisible by divisor + ' + ' @param _UNSIGNED _BYTE ARRAY source_arr~%%() array to work on + ' @param _UNSIGNED _BYTE ARRAY dest_arr~%%() array to store in + ' @param _UNSIGNED _BYTE divisor~%% for modulo + ' + SUB ARR_UBYTE.mod(source_arr~%%(), dest_arr~%%(), divisor~%%) + DIM AS LONG lb, ub, i + DIM tmp AS _UNSIGNED _BYTE + lb& = LBOUND(source_arr~%%) : ub& = UBOUND(source_arr~%%) + DIM work_arr(0) AS _UNSIGNED _BYTE + FOR i& = lb& TO ub& + IF source_arr~%%(i&) MOD divisor~%% = 0 THEN + CALL ARR_UBYTE.push(work_arr~%%(), source_arr~%%(i&)) + END IF + NEXT i& + CALL ARR_UBYTE.shift(work_arr~%%(), tmp~%%) + CALL ARR_UBYTE.copy(work_arr~%%(), dest_arr~%%()) + END SUB + + + '' + ' Filters a _UNSIGNED _BYTE array to only elements between min and max + ' + ' @param _UNSIGNED _BYTE ARRAY source_arr~%%() array to work on + ' @param _UNSIGNED _BYTE ARRAY dest_arr~%%() array to store in + ' @param _UNSIGNED _BYTE min~%% to be greater than or equal to be returned + ' @param _UNSIGNED _BYTE max~%% to be less than or equal to be returned + ' + SUB ARR_UBYTE.between(source_arr~%%(), dest_arr~%%(), min~%%, max~%%) + DIM AS LONG lb, ub, i + DIM tmp AS _UNSIGNED _BYTE + lb& = LBOUND(source_arr~%%) : ub& = UBOUND(source_arr~%%) + DIM work_arr(0) AS _UNSIGNED _BYTE + FOR i& = lb& TO ub& + IF source_arr~%%(i&) >= min~%% _ + AND source_arr~%%(i&) <= max~%% THEN + CALL ARR_UBYTE.push(work_arr~%%(), source_arr~%%(i&)) + END IF + NEXT i& + CALL ARR_UBYTE.shift(work_arr~%%(), tmp~%%) + CALL ARR_UBYTE.copy(work_arr~%%(), dest_arr~%%()) + END SUB + + + '' + ' Sorts _UNSIGNED _BYTE array in ascending order + ' + ' @param _UNSIGNED _BYTE ARRAY source_arr~%%() array to sort + ' @param _UNSIGNED _BYTE ARRAY dest_arr~%%() array to store sorted in + ' + SUB ARR_UBYTE.sort(source_arr~%%(), dest_arr~%%()) + DIM AS LONG lb, ub, count + lb& = LBOUND(source_arr~%%) : ub& = UBOUND(source_arr~%%) + count& = ub& - lb& + REDIM dest_arr(0 TO count&) AS _UNSIGNED _BYTE + CALL ARR_UBYTE.copy(source_arr~%%(), dest_arr~%%()) + CALL ARR_UBYTE.quicksort(dest_arr~%%(), lb&, ub&, 0) + END SUB + + + '' + ' Sorts _UNSIGNED _BYTE array in descending order + ' + ' @param _UNSIGNED _BYTE ARRAY source_arr~%%() array to sort + ' @param _UNSIGNED _BYTE ARRAY dest_arr~%%() array to store sorted in + ' + SUB ARR_UBYTE.rsort(source_arr~%%(), dest_arr~%%()) + DIM AS LONG lb, ub, count + lb& = LBOUND(source_arr~%%) : ub& = UBOUND(source_arr~%%) + count& = ub& - lb& + REDIM dest_arr(0 TO count&) AS _UNSIGNED _BYTE + CALL ARR_UBYTE.copy(source_arr~%%(), dest_arr~%%()) + CALL ARR_UBYTE.quicksort(dest_arr~%%(), lb&, ub&, 1) + END SUB + + + '' + ' Quicksort array with pivot algorithm by logiclrd + ' + ' @link https://www.tek-tips.com/faqs.cfm?fid=336 + ' @param _UNSIGNED _BYTE ARRAY array~%%() to sort + ' @param INTEGER start% of range to sort + ' @param INTEGER finish% range of sort + ' @param INTEGER order% to sort by (0 = asc / 1 = desc) + ' + SUB ARR_UBYTE.quicksort(arr~%%(), start%, finish%, order%) + DIM i AS LONG + DIM pivot AS INTEGER + DIM pivotvalue AS _UNSIGNED _BYTE + + 'first, partition the array + pivot% = start% + pivotvalue~%% = arr~%%(pivot%) + FOR i& = start% + 1 TO finish% + IF order% = 0 THEN ' ascending order + IF arr~%%(i&) < pivotvalue~%% THEN + arr~%%(pivot%) = arr~%%(i&) + arr~%%(i&) = arr~%%(pivot% + 1) + arr~%%(pivot% + 1) = pivotvalue~%% + pivot% = pivot% + 1 + END IF + ELSEIF order% = 1 THEN ' descending order + IF arr~%%(i&) > pivotvalue~%% THEN + arr~%%(pivot%) = arr~%%(i&) + arr~%%(i&) = arr~%%(pivot% + 1) + arr~%%(pivot% + 1) = pivotvalue~%% + pivot% = pivot% + 1 + END IF + END IF + NEXT i& + + 'then, sort the subarrays to each side of the pivot + IF pivot% - start% >= 2 THEN + CALL ARR_UBYTE.quicksort(arr~%%(), start%, pivot% - 1, order%) + END IF + IF finish% - pivot% >= 2 THEN + CALL ARR_UBYTE.quicksort(arr~%%(), pivot% + 1, finish%, order%) + END IF + END SUB + $END IF + $IF GJ_LIB_ARR_UINT_BAS = UNDEFINED THEN + '$DYNAMIC + $LET GJ_LIB_ARR_UINT_BAS = 1 + + '' + ' Slice an array from source to destination starting at index and count slices + ' + ' @param _UNSIGNED INTEGER() source_arr~% to slice from + ' @param _UNSIGNED INTEGER() dest_arr~% to put slices into + ' @param INTEGER start_idx% starting index to use as slice range + ' @param INTEGER count% number of slices - if negative, backwards from index + ' + SUB ARR_UINT.slice(source_arr~%(), dest_arr~%(), start_idx%, count%) + DIM AS LONG ub, lb, i, n + lb& = LBOUND(source_arr~%) : ub& = UBOUND(source_arr~%) + IF start_idx% < lb& OR start_idx% + count% > ub& THEN EXIT SUB ' out of range + IF ub& - lb& < count% THEN EXIT SUB ' too many and not enough + REDIM dest_arr(0 TO ABS(count%)) AS _UNSIGNED INTEGER + IF SGN(count%) = -1 THEN + IF ((start_idx% - 1) - ABS(count%)) < 0 THEN EXIT SUB ' out of range + n& = 0 + FOR i& = (start_idx% - 1) TO ((start_idx% - 1) - ABS(count%)) STEP -1 + dest_arr~%(n&) = source_arr~%(i&) + n& = n& + 1 + NEXT i& + ELSE + IF ((start_idx% + 1) + ABS(count%)) > (ub& - lb&) THEN EXIT SUB ' out of range + n& = 0 + FOR i& = start_idx% + 1 TO ((start_idx% + 1) + count%) STEP 1 + dest_arr~%(n&) = source_arr~%(i&) + n& = n& + 1 + NEXT i& + END IF + END SUB + + + '' + ' Push a uint onto the end of a _UNSIGNED INTEGER array + ' + ' @param _UNSIGNED INTEGER arr~%() array to push into + ' @param _UNSIGNED INTEGER value~% of byte to push + ' + SUB ARR_UINT.push(arr~%(), value~%) + DIM AS LONG ub, lb + lb& = LBOUND(arr~%) : ub& = UBOUND(arr~%) + REDIM _PRESERVE arr(lb& TO (ub& + 1)) AS _UNSIGNED INTEGER + arr~%(ub& + 1) = value~% + END SUB + + + '' + ' Pop a uint from the end of a _UNSIGNED INTEGER array + ' + ' @param _UNSIGNED INTEGER arr~%() array to pop from + ' @param _UNSIGNED INTEGER var~% of uint to store popped uint + ' + SUB ARR_UINT.pop(arr~%(), var~%) + DIM AS LONG ub, lb + lb& = LBOUND(arr~%) : ub& = UBOUND(arr~%) + var~% = arr~%(ub&) + REDIM _PRESERVE arr(lb& TO (ub& - 1)) AS _UNSIGNED INTEGER + END SUB + + + '' + ' Pop a uint from the beginning of a _UNSIGNED INTEGER array + ' + ' @param _UNSIGNED INTEGER arr~%() array to pop from + ' @param _UNSIGNED INTEGER var~% of uint to store popped uint + ' + SUB ARR_UINT.shift(arr~%(), var~%) + DIM AS LONG ub, lb, i + lb& = LBOUND(arr~%) : ub& = UBOUND(arr~%) + var~% = arr~%(lb&) + FOR i& = lb& TO ub& - 1 + arr~%(i&) = arr~%(i& + 1) + NEXT i& + REDIM _PRESERVE arr(lb& + 1 TO ub&) AS _UNSIGNED INTEGER + END SUB + + + '' + ' Copy an array of UINTs to another _UNSIGNED INTEGER array + ' + ' @param _UNSIGNED INTEGER ARRAY source_arr~%() source array to copy + ' @param _UNSIGNED INTEGER ARRAY dest_arr~%() dest array to copy into + ' + SUB ARR_UINT.copy(source_arr~%(), dest_arr~%()) + DIM AS LONG ub, lb, i + lb& = LBOUND(source_arr~%) : ub& = UBOUND(source_arr~%) + REDIM dest_arr(lb& TO ub&) AS _UNSIGNED INTEGER + FOR i& = lb& TO ub& + dest_arr~%(i&) = source_arr~%(i&) + NEXT i& + END SUB + + + '' + ' Push a uint into the beginning of a _UNSIGNED INTEGER array + ' + ' @param _UNSIGNED INTEGER arr~%() array to push into + ' @param _UNSIGNED INTEGER value~% of uint to push + ' + SUB ARR_UINT.unshift(arr~%(), value~%) + DIM AS LONG ub, lb, i + lb& = LBOUND(arr~%) : ub& = UBOUND(arr~%) + DIM work(lb& TO ub&) AS _UNSIGNED INTEGER + CALL ARR_UINT.copy(arr~%(), work~%()) + REDIM _PRESERVE arr(lb& TO (ub& + 1)) AS _UNSIGNED INTEGER + FOR i& = lb& + 1 TO ub& + 1 + arr~%(i&) = work~%(i& - 1) + NEXT i& + arr~%(lb&) = value~% + END SUB + + + '' + ' Joins an array of UINTs as a string + ' + ' @param _UNSIGNED INTEGER ARRAY arr~%() to get as a string + ' @param STRING s$ to store stringified array in + ' + SUB ARR_UINT.join(arr~%(), s$) + DIM AS LONG ub, lb, i + s$ = "" + lb& = LBOUND(arr~%) : ub& = UBOUND(arr~%) + FOR i& = lb& TO ub& + s$ = s$ + _TRIM$(STR$(arr~%(i&))) + ", " + NEXT i& + ' remove trailing comma + s$ = MID$(s$, 1, (LEN(s$)-LEN(", "))) + END SUB + + + '' + ' Create a new _UNSIGNED INTEGER array using string of uints seperated by commas + ' + ' @param _UNSIGNED INTEGER ARRAY arr~%() to store the uints in + ' @param STRING s$ string of comma separated uints + ' + SUB ARR_UINT.new(arr~%(), s$) + DIM AS LONG i, count + DIM t AS STRING + count& = 0 + FOR i& = 1 TO LEN(s$) + IF ASC(s$, i&) = ASC(",") THEN count& = count& + 1 + NEXT i& + REDIM arr(0 TO count&) AS _UNSIGNED INTEGER + IF count& = 0 THEN + arr~%(0) = VAL(s$) + EXIT SUB + END IF + count& = 0 + FOR i& = 1 TO LEN(s$) + t$ = t$ + CHR$(ASC(s$, i&)) + IF ASC(s$, i&) = ASC(",") OR i& = LEN(s$) THEN + arr~%(count&) = VAL(t$) + count& = count& + 1 + t$ = "" + END IF + NEXT i& + END SUB + + + '' + ' Return the visually longest element of a _UNSIGNED INTEGER array + ' + ' @param _UNSIGNED INTEGER ARRAY arr~%() to check in + ' @return _UNSIGNED INTEGER value of visually longest element + ' + FUNCTION ARR_UINT.longest~%(arr~%()) + DIM AS LONG lb, ub, i, res, lw + lb& = LBOUND(arr~%) : ub& = UBOUND(arr~%) + res& = 0 : lw& = 0 + FOR i& = lb& TO ub& + IF LEN(_TRIM$(STR$(arr~%(i&)))) > lw& THEN + lw& = LEN(_TRIM$(STR$(arr~%(i&)))) + res& = i& + END IF + NEXT i& + ARR_UINT.longest~% = arr~%(res&) + END FUNCTION + + + '' + ' Perform some math on every element of a _UNSIGNED INTEGER array + ' + ' @param _UNSIGNED INTEGER ARRAY source_arr~%() to do math on + ' @param _UNSIGNED INTEGER ARRAY dest_arr~%() to store results in + ' @param STRING op$ one of: + ' and or xor shl shr + ' "+", "-", "*", "\", "&&", "||", "!!", "<<", ">>" + ' @param _UNSIGNED INTEGER value~% to use for operand + ' + SUB ARR_UINT.math(source_arr~%(), dest_arr~%(), op$, value~%) + DIM AS LONG lb, ub, i + lb& = LBOUND(source_arr~%) : ub& = UBOUND(source_arr~%) + REDIM dest_arr(lb& TO ub&) AS _UNSIGNED INTEGER + FOR i& = lb& TO ub& + SELECT CASE op$ + CASE "+": + dest_arr~%(i&) = source_arr~%(i&) + value~% + CASE "-": + dest_arr~%(i&) = source_arr~%(i&) - value~% + CASE "*": + dest_arr~%(i&) = source_arr~%(i&) * value~% + CASE "\": + IF value~% > 0 THEN + dest_arr~%(i&) = source_arr~%(i&) \ value~% + END IF + CASE "&&": + dest_arr~%(i&) = source_arr~%(i&) AND value~% + CASE "||": + dest_arr~%(i&) = source_arr~%(i&) OR value~% + CASE "!!": + dest_arr~%(i&) = source_arr~%(i&) XOR value~% + CASE "<<": + dest_arr~%(i&) = _SHL(source_arr~%(i&), value~%) + CASE ">>": + dest_arr~%(i&) = _SHR(source_arr~%(i&), value~%) + END SELECT + NEXT i& + END SUB + + + '' + ' Return the minimum element value in _UNSIGNED INTEGER array + ' + ' @param _UNSIGNED INTEGER ARRAY arr~%() to check in + ' @return _UNSIGNED INTEGER minimum value found + ' + FUNCTION ARR_UINT.min~%(arr~%()) + DIM AS LONG lb, ub, i + DIM AS _UNSIGNED INTEGER s + lb& = LBOUND(arr~%) : ub& = UBOUND(arr~%) + s~% = 127 + FOR i& = lb& TO ub& + IF arr~%(i&) < s~% THEN + s~% = arr~%(i&) + END IF + NEXT i& + ARR_UINT.min~% = s~% + END FUNCTION + + + '' + ' Return the maximum element value in _UNSIGNED INTEGER array + ' + ' @param _UNSIGNED INTEGER ARRAY arr~%() to check in + ' @return _UNSIGNED INTEGER maximum value found + ' + FUNCTION ARR_UINT.max~%(arr~%()) + DIM AS LONG lb, ub, i + DIM AS _UNSIGNED INTEGER s + lb& = LBOUND(arr~%) : ub& = UBOUND(arr~%) + s~% = 0 + FOR i& = lb& TO ub& + IF arr~%(i&) > s~% THEN + s~% = arr~%(i&) + END IF + NEXT i& + ARR_UINT.max~% = s~% + END FUNCTION + + + '' + ' Return the visually shortest element of a _UNSIGNED INTEGER array + ' + ' @param _UNSIGNED INTEGER ARRAY arr~%() to check in + ' @return _UNSIGNED INTEGER value of visually shortest element + ' + FUNCTION ARR_UINT.shortest~%(arr~%()) + DIM AS LONG lb, ub, i, res, sw + lb& = LBOUND(arr~%) : ub& = UBOUND(arr~%) + res& = 0 : sw& = 0 + FOR i& = lb& TO ub& + IF LEN(_TRIM$(STR$(arr~%(i&)))) < sw& THEN + sw& = LEN(_TRIM$(STR$(arr~%(i&)))) + res& = i& + END IF + NEXT i& + ARR_UINT.shortest~% = arr~%(res&) + END FUNCTION + + + '' + ' Return the first element of a _UNSIGNED INTEGER array + ' + ' @param _UNSIGNED INTEGER ARRAY arr~%() to check in + ' @return _UNSIGNED INTEGER value of first element + ' + FUNCTION ARR_UINT.first~%(arr~%()) + ARR_UINT.first~% = arr~%(LBOUND(arr~%)) + END FUNCTION + + + '' + ' Return the last element of a _UNSIGNED INTEGER array + ' + ' @param _UNSIGNED INTEGER ARRAY arr~%() to check in + ' @return _UNSIGNED INTEGER value of last element + ' + FUNCTION ARR_UINT.last~%(arr~%()) + ARR_UINT.last~% = arr~%(UBOUND(arr~%)) + END FUNCTION + + + '' + ' Return every nth array element of a _UNSIGNED INTEGER array + ' + ' @param _UNSIGNED INTEGER ARRAY source_arr~%() to get from + ' @param _UNSIGNED INTEGER ARRAY dest_arr~%() to store in + ' @param INTEGER nth% element + ' + SUB ARR_UINT.nth(source_arr~%(), dest_arr~%(), nth%) + DIM AS LONG lb, ub, i, n + lb& = LBOUND(source_arr~%) : ub& = UBOUND(source_arr~%) + n& = (ub& - lb&) \ nth% + REDIM dest_arr(n&) AS _UNSIGNED INTEGER + n& = 0 + FOR i& = lb& to ub& + IF i& MOD nth% = 0 THEN + dest_arr~%(n&) = source_arr~%(i&) + n& = n& + 1 + END IF + NEXT i& + END SUB + + + '' + ' Checks if value exists in _UNSIGNED INTEGER array + ' + ' @param _UNSIGNED INTEGER ARRAY arr~%() to check in + ' @param _UNSIGNED INTEGER value~% value to check for + ' @return INTEGER -1 if found or 0 if not found + ' + FUNCTION ARR_UINT.in%(arr~%(), value~%) + DIM AS LONG lb, ub, i + lb& = LBOUND(arr~%) : ub& = UBOUND(arr~%) + FOR i& = lb& TO ub& + IF arr~%(i&) = value~% THEN + ARR_UINT.in% = -1 + EXIT FUNCTION + END IF + NEXT i& + ARR_UINT.in% = 0 + END FUNCTION + + + '' + ' Checks if value exists in _UNSIGNED INTEGER array and returns index if found + ' + ' @param _UNSIGNED INTEGER ARRAY arr~%() to check in + ' @param _UNSIGNED INTEGER value~% value to check for + ' @return INTEGER index of element if found or -1 if not found + ' + FUNCTION ARR_UINT.find%(arr~%(), value~%) + DIM AS LONG lb, ub, i + lb& = LBOUND(arr~%) : ub& = UBOUND(arr~%) + FOR i& = lb& TO ub& + IF arr~%(i&) = value~% THEN + ARR_UINT.find% = i& + EXIT FUNCTION + END IF + NEXT i& + ARR_UINT.find% = -1 + END FUNCTION + + + '' + ' Return the number of elements in a _UNSIGNED INTEGER array + ' + ' @param _UNSIGNED INTEGER ARRAY arr~%() to count + ' @return INTEGER number of elements + ' + FUNCTION ARR_UINT.count&(arr~%()) + ARR_UINT.count& = UBOUND(arr~%) - LBOUND(arr~%) + END FUNCTION + + + '' + ' Return the size of a _UNSIGNED INTEGER array + ' + ' @param _UNSIGNED INTEGER ARRAY arr~%() to get size of + ' @return LONG size in bytes + ' + FUNCTION ARR_UINT.size&(arr~%()) + ARR_UINT.size& = LEN(arr~%()) + END FUNCTION + + + '' + ' Reverses the elements of a _UNSIGNED INTEGER array + ' + ' @param _UNSIGNED INTEGER ARRAY source_arr~%() to reverse + ' @param _UNSIGNED INTEGER ARRAY dest_arr~%() to store reversed array in + ' + SUB ARR_UINT.reverse(source_arr~%(), dest_arr~%()) + DIM AS LONG lb, ub, i, n + lb& = LBOUND(source_arr~%) : ub& = UBOUND(source_arr~%) + REDIM dest_arr(0 TO (ub& - lb&)) AS _UNSIGNED INTEGER + n& = 0 + FOR i& = ub& TO lb& STEP -1 + dest_arr~%(n&) = source_arr~%(i&) + n& = n& + 1 + NEXT i& + END SUB + + + '' + ' Returns a random uint from a _UNSIGNED INTEGER array + ' + ' @param _UNSIGNED INTEGER ARRAY arr~%() array to get random element from + ' @return _UNSIGNED INTEGER random element + ' + FUNCTION ARR_UINT.random~%(arr~%()) + DIM AS LONG lb, ub + lb& = LBOUND(arr~%) : ub& = UBOUND(arr~%) + RANDOMIZE TIMER + ARR_UINT.random~% = arr~%(INT(RND * (ub& - lb&)) + 1) + END FUNCTION + + + '' + ' Returns the sum of all elements in a _UNSIGNED INTEGER array + ' + ' @param _UNSIGNED INTEGER ARRAY arr~%() array to get some for + ' @return LONG sum of all elements + ' + FUNCTION ARR_UINT.sum&(arr~%()) + DIM AS LONG lb, ub, i + DIM sum AS LONG + lb& = LBOUND(arr~%) : ub& = UBOUND(arr~%) + FOR i& = lb& TO ub& + sum& = sum& + arr~%(i&) + NEXT i& + ARR_UINT.sum& = sum& + END FUNCTION + + + '' + ' Returns the average value of elements in a _UNSIGNED INTEGER array + ' + ' @param _UNSIGNED INTEGER ARRAY arr~%() array to get average for + ' @return LONG average of elements + ' + FUNCTION ARR_UINT.avg&(arr~%()) + DIM AS LONG lb, ub, i + DIM sum AS LONG + lb& = LBOUND(arr~%) : ub& = UBOUND(arr~%) + FOR i& = lb& TO ub& + sum& = sum& + arr~%(i&) + NEXT i& + ARR_UINT.avg& = sum& / (ub& - lb&) + END FUNCTION + + + '' + ' Shuffle the elements of a _UNSIGNED INTEGER array + ' + ' @param _UNSIGNED INTEGER ARRAY source_arr~%() to shuffle + ' @param _UNSIGNED INTEGER ARRAY dest_arr~%() to store shuffled array in + ' + SUB ARR_UINT.shuffle(source_arr~%(), dest_arr~%()) + DIM AS LONG lb, ub, i, count + lb& = LBOUND(source_arr~%) : ub& = UBOUND(source_arr~%) + count& = ub& - lb& + REDIM dest_arr(0 TO count&) AS _UNSIGNED INTEGER + CALL ARR_UINT.copy(source_arr~%(), dest_arr~%()) + RANDOMIZE TIMER + FOR i& = 0 TO count& + SWAP dest_arr~%(i&), dest_arr~%(lb& + RND * (ub& - lb&)) + NEXT i& + END SUB + + + '' + ' Makes a _UNSIGNED INTEGER array contain only unique values + ' + ' @param _UNSIGNED INTEGER ARRAY source_arr~%() array to get uniques for + ' @param _UNSIGNED INTEGER ARRAY dest_arr~%() array to store uniques in + ' + SUB ARR_UINT.unique(source_arr~%(), dest_arr~%()) + DIM AS LONG lb, ub, i + DIM tmp AS _UNSIGNED INTEGER + lb& = LBOUND(source_arr~%) : ub& = UBOUND(source_arr~%) + DIM work_arr(0) AS _UNSIGNED INTEGER + FOR i& = lb& TO ub& + IF NOT ARR_UINT.in%(work_arr~%(), source_arr~%(i&)) THEN + CALL ARR_UINT.push(work_arr~%(), source_arr~%(i&)) + END IF + NEXT i& + CALL ARR_UINT.shift(work_arr~%(), tmp~%) + CALL ARR_UINT.copy(work_arr~%(), dest_arr~%()) + END SUB + + + '' + ' Filters a _UNSIGNED INTEGER array to only elements greater than value + ' + ' @param _UNSIGNED INTEGER ARRAY source_arr~%() array to work on + ' @param _UNSIGNED INTEGER ARRAY dest_arr~%() array to store in + ' @param _UNSIGNED INTEGER value~% to be greater than to be returned + ' + SUB ARR_UINT.gt(source_arr~%(), dest_arr~%(), value~%) + DIM AS LONG lb, ub, i + DIM tmp AS _UNSIGNED INTEGER + lb& = LBOUND(source_arr~%) : ub& = UBOUND(source_arr~%) + DIM work_arr(0) AS _UNSIGNED INTEGER + FOR i& = lb& TO ub& + IF source_arr~%(i&) > value~% THEN + CALL ARR_UINT.push(work_arr~%(), source_arr~%(i&)) + END IF + NEXT i& + CALL ARR_UINT.shift(work_arr~%(), tmp~%) + CALL ARR_UINT.copy(work_arr~%(), dest_arr~%()) + END SUB + + + '' + ' Filters a _UNSIGNED INTEGER array to only elements greater than or equal to value + ' + ' @param _UNSIGNED INTEGER ARRAY source_arr~%() array to work on + ' @param _UNSIGNED INTEGER ARRAY dest_arr~%() array to store in + ' @param _UNSIGNED INTEGER value~% to be greater than or equal to be returned + ' + SUB ARR_UINT.gte(source_arr~%(), dest_arr~%(), value~%) + DIM AS LONG lb, ub, i + DIM tmp AS _UNSIGNED INTEGER + lb& = LBOUND(source_arr~%) : ub& = UBOUND(source_arr~%) + DIM work_arr(0) AS _UNSIGNED INTEGER + FOR i& = lb& TO ub& + IF source_arr~%(i&) >= value~% THEN + CALL ARR_UINT.push(work_arr~%(), source_arr~%(i&)) + END IF + NEXT i& + CALL ARR_UINT.shift(work_arr~%(), tmp~%) + CALL ARR_UINT.copy(work_arr~%(), dest_arr~%()) + END SUB + + + '' + ' Filters a _UNSIGNED INTEGER array to only elements less than value + ' + ' @param _UNSIGNED INTEGER ARRAY source_arr~%() array to work on + ' @param _UNSIGNED INTEGER ARRAY dest_arr~%() array to store in + ' @param _UNSIGNED INTEGER value~% to be less than to be returned + ' + SUB ARR_UINT.lt(source_arr~%(), dest_arr~%(), value~%) + DIM AS LONG lb, ub, i + DIM tmp AS _UNSIGNED INTEGER + lb& = LBOUND(source_arr~%) : ub& = UBOUND(source_arr~%) + DIM work_arr(0) AS _UNSIGNED INTEGER + FOR i& = lb& TO ub& + IF source_arr~%(i&) < value~% THEN + CALL ARR_UINT.push(work_arr~%(), source_arr~%(i&)) + END IF + NEXT i& + CALL ARR_UINT.shift(work_arr~%(), tmp~%) + CALL ARR_UINT.copy(work_arr~%(), dest_arr~%()) + END SUB + + + '' + ' Filters a _UNSIGNED INTEGER array to only elements less than or equal to value + ' + ' @param _UNSIGNED INTEGER ARRAY source_arr~%() array to work on + ' @param _UNSIGNED INTEGER ARRAY dest_arr~%() array to store in + ' @param _UNSIGNED INTEGER value~% to be less than or equal to be returned + ' + SUB ARR_UINT.lte(source_arr~%(), dest_arr~%(), value~%) + DIM AS LONG lb, ub, i + DIM tmp AS _UNSIGNED INTEGER + lb& = LBOUND(source_arr~%) : ub& = UBOUND(source_arr~%) + DIM work_arr(0) AS _UNSIGNED INTEGER + FOR i& = lb& TO ub& + IF source_arr~%(i&) <= value~% THEN + CALL ARR_UINT.push(work_arr~%(), source_arr~%(i&)) + END IF + NEXT i& + CALL ARR_UINT.shift(work_arr~%(), tmp~%) + CALL ARR_UINT.copy(work_arr~%(), dest_arr~%()) + END SUB + + + '' + ' Finds and replaces values across all elements in a _UNSIGNED INTEGER ARRAY + ' + ' @param _UNSIGNED INTEGER ARRAY arr~%() to check in + ' @param _UNSIGNED INTEGER find~% value to find + ' @param _UNSIGNED INTEGER replace~% value to replace with if found + ' + SUB ARR_UINT.replace(arr~%(), find~%, replace~%) + DIM AS LONG lb, ub, i + lb& = LBOUND(arr~%) : ub& = UBOUND(arr~%) + FOR i& = lb& TO ub& + IF arr~%(i&) = find~% THEN + arr~%(i&) = replace~% + END IF + NEXT i& + END SUB + + + '' + ' Inserts a new element into _UNSIGNED INTEGER array after index + ' + ' @param _UNSIGNED INTEGER ARRAY arr~%() array to work on + ' @param _UNSIGNED INTEGER value~% to insert + ' @param INTEGER index% of element to insert at + ' + SUB ARR_UINT.insert(arr~%(), value~%, index%) + DIM AS LONG lb, ub, i + DIM tmp AS _UNSIGNED INTEGER + DIM work_arr(0) AS _UNSIGNED INTEGER + lb& = LBOUND(arr~%) : ub& = UBOUND(arr~%) + IF index% >= lb& AND index% <= ub& THEN + ' build new array up to index + FOR i& = lb& TO index% + CALL ARR_UINT.push(work_arr~%(), arr~%(i&)) + NEXT i& + ' insert new element + CALL ARR_UINT.push(work_arr~%(), value~%) + ' finish building array from index + 1 + FOR i& = index% + 1 TO ub& + CALL ARR_UINT.push(work_arr~%(), arr~%(i&)) + NEXT i& + CALL ARR_UINT.shift(work_arr~%(), tmp~%) + CALL ARR_UINT.copy(work_arr~%(), arr~%()) + END IF + END SUB + + + '' + ' Removes element from a _UNSIGNED INTEGER array by element index + ' + ' @param _UNSIGNED INTEGER ARRAY arr~%() array to work on + ' @param INTEGER index% of element to remove + ' + SUB ARR_UINT.remove(arr~%(), index%) + DIM AS LONG lb, ub, i + DIM tmp AS _UNSIGNED INTEGER + DIM work_arr(0) AS _UNSIGNED INTEGER + lb& = LBOUND(arr~%) : ub& = UBOUND(arr~%) + IF index% >= lb& AND index% <= ub& THEN + ' build new array up to index + FOR i& = lb& TO index% - 1 + CALL ARR_UINT.push(work_arr~%(), arr~%(i&)) + NEXT i& + ' skip elements + FOR i& = index% + 1 TO ub& + CALL ARR_UINT.push(work_arr~%(), arr~%(i&)) + NEXT i& + CALL ARR_UINT.shift(work_arr~%(), tmp~%) + CALL ARR_UINT.copy(work_arr~%(), arr~%()) + END IF + END SUB + + + '' + ' Filters a _UNSIGNED INTEGER array to only elements that have odd values + ' + ' @param _UNSIGNED INTEGER ARRAY source_arr~%() array to work on + ' @param _UNSIGNED INTEGER ARRAY dest_arr~%() array to store in + ' + SUB ARR_UINT.odd(source_arr~%(), dest_arr~%()) + DIM AS LONG lb, ub, i + DIM tmp AS _UNSIGNED INTEGER + lb& = LBOUND(source_arr~%) : ub& = UBOUND(source_arr~%) + DIM work_arr(0) AS _UNSIGNED INTEGER + FOR i& = lb& TO ub& + IF source_arr~%(i&) MOD 2 <> 0 THEN + CALL ARR_UINT.push(work_arr~%(), source_arr~%(i&)) + END IF + NEXT i& + CALL ARR_UINT.shift(work_arr~%(), tmp~%) + CALL ARR_UINT.copy(work_arr~%(), dest_arr~%()) + END SUB + + + '' + ' Filters a _UNSIGNED INTEGER array to only elements that have even values + ' + ' @param _UNSIGNED INTEGER ARRAY source_arr~%() array to work on + ' @param _UNSIGNED INTEGER ARRAY dest_arr~%() array to store in + ' + SUB ARR_UINT.even(source_arr~%(), dest_arr~%()) + DIM AS LONG lb, ub, i + DIM tmp AS _UNSIGNED INTEGER + lb& = LBOUND(source_arr~%) : ub& = UBOUND(source_arr~%) + DIM work_arr(0) AS _UNSIGNED INTEGER + FOR i& = lb& TO ub& + IF source_arr~%(i&) MOD 2 = 0 THEN + CALL ARR_UINT.push(work_arr~%(), source_arr~%(i&)) + END IF + NEXT i& + CALL ARR_UINT.shift(work_arr~%(), tmp~%) + CALL ARR_UINT.copy(work_arr~%(), dest_arr~%()) + END SUB + + + '' + ' Filters a _UNSIGNED INTEGER array to only elements that have values evenly divisible by divisor + ' + ' @param _UNSIGNED INTEGER ARRAY source_arr~%() array to work on + ' @param _UNSIGNED INTEGER ARRAY dest_arr~%() array to store in + ' @param _UNSIGNED INTEGER divisor~% for modulo + ' + SUB ARR_UINT.mod(source_arr~%(), dest_arr~%(), divisor~%) + DIM AS LONG lb, ub, i + DIM tmp AS _UNSIGNED INTEGER + lb& = LBOUND(source_arr~%) : ub& = UBOUND(source_arr~%) + DIM work_arr(0) AS _UNSIGNED INTEGER + FOR i& = lb& TO ub& + IF source_arr~%(i&) MOD divisor~% = 0 THEN + CALL ARR_UINT.push(work_arr~%(), source_arr~%(i&)) + END IF + NEXT i& + CALL ARR_UINT.shift(work_arr~%(), tmp~%) + CALL ARR_UINT.copy(work_arr~%(), dest_arr~%()) + END SUB + + + '' + ' Filters a _UNSIGNED INTEGER array to only elements between min and max + ' + ' @param _UNSIGNED INTEGER ARRAY source_arr~%() array to work on + ' @param _UNSIGNED INTEGER ARRAY dest_arr~%() array to store in + ' @param _UNSIGNED INTEGER min~% to be greater than or equal to be returned + ' @param _UNSIGNED INTEGER max~% to be less than or equal to be returned + ' + SUB ARR_UINT.between(source_arr~%(), dest_arr~%(), min~%, max~%) + DIM AS LONG lb, ub, i + DIM tmp AS _UNSIGNED INTEGER + lb& = LBOUND(source_arr~%) : ub& = UBOUND(source_arr~%) + DIM work_arr(0) AS _UNSIGNED INTEGER + FOR i& = lb& TO ub& + IF source_arr~%(i&) >= min~% _ + AND source_arr~%(i&) <= max~% THEN + CALL ARR_UINT.push(work_arr~%(), source_arr~%(i&)) + END IF + NEXT i& + CALL ARR_UINT.shift(work_arr~%(), tmp~%) + CALL ARR_UINT.copy(work_arr~%(), dest_arr~%()) + END SUB + + + '' + ' Sorts _UNSIGNED INTEGER array in ascending order + ' + ' @param _UNSIGNED INTEGER ARRAY source_arr~%() array to sort + ' @param _UNSIGNED INTEGER ARRAY dest_arr~%() array to store sorted in + ' + SUB ARR_UINT.sort(source_arr~%(), dest_arr~%()) + DIM AS LONG lb, ub, count + lb& = LBOUND(source_arr~%) : ub& = UBOUND(source_arr~%) + count& = ub& - lb& + REDIM dest_arr(0 TO count&) AS _UNSIGNED INTEGER + CALL ARR_UINT.copy(source_arr~%(), dest_arr~%()) + CALL ARR_UINT.quicksort(dest_arr~%(), lb&, ub&, 0) + END SUB + + + '' + ' Sorts _UNSIGNED INTEGER array in descending order + ' + ' @param _UNSIGNED INTEGER ARRAY source_arr~%() array to sort + ' @param _UNSIGNED INTEGER ARRAY dest_arr~%() array to store sorted in + ' + SUB ARR_UINT.rsort(source_arr~%(), dest_arr~%()) + DIM AS LONG lb, ub, count + lb& = LBOUND(source_arr~%) : ub& = UBOUND(source_arr~%) + count& = ub& - lb& + REDIM dest_arr(0 TO count&) AS _UNSIGNED INTEGER + CALL ARR_UINT.copy(source_arr~%(), dest_arr~%()) + CALL ARR_UINT.quicksort(dest_arr~%(), lb&, ub&, 1) + END SUB + + + '' + ' Quicksort array with pivot algorithm by logiclrd + ' + ' @link https://www.tek-tips.com/faqs.cfm?fid=336 + ' @param _UNSIGNED INTEGER ARRAY array~%() to sort + ' @param INTEGER start% of range to sort + ' @param INTEGER finish% range of sort + ' @param INTEGER order% to sort by (0 = asc / 1 = desc) + ' + SUB ARR_UINT.quicksort(arr~%(), start%, finish%, order%) + DIM i AS LONG + DIM pivot AS INTEGER + DIM pivotvalue AS _UNSIGNED INTEGER + + 'first, partition the array + pivot% = start% + pivotvalue~% = arr~%(pivot%) + FOR i& = start% + 1 TO finish% + IF order% = 0 THEN ' ascending order + IF arr~%(i&) < pivotvalue~% THEN + arr~%(pivot%) = arr~%(i&) + arr~%(i&) = arr~%(pivot% + 1) + arr~%(pivot% + 1) = pivotvalue~% + pivot% = pivot% + 1 + END IF + ELSEIF order% = 1 THEN ' descending order + IF arr~%(i&) > pivotvalue~% THEN + arr~%(pivot%) = arr~%(i&) + arr~%(i&) = arr~%(pivot% + 1) + arr~%(pivot% + 1) = pivotvalue~% + pivot% = pivot% + 1 + END IF + END IF + NEXT i& + + 'then, sort the subarrays to each side of the pivot + IF pivot% - start% >= 2 THEN + CALL ARR_UINT.quicksort(arr~%(), start%, pivot% - 1, order%) + END IF + IF finish% - pivot% >= 2 THEN + CALL ARR_UINT.quicksort(arr~%(), pivot% + 1, finish%, order%) + END IF + END SUB + $END IF + $IF GJ_LIB_ARR_UINT64_BAS = UNDEFINED THEN + '$DYNAMIC + $LET GJ_LIB_ARR_UINT64_BAS = 1 + + '' + ' Slice an array from source to destination starting at index and count slices + ' + ' @param _UNSIGNED _INTEGER64() source_arr~&& to slice from + ' @param _UNSIGNED _INTEGER64() dest_arr~&& to put slices into + ' @param INTEGER start_idx% starting index to use as slice range + ' @param INTEGER count% number of slices - if negative, backwards from index + ' + SUB ARR_UINT64.slice(source_arr~&&(), dest_arr~&&(), start_idx%, count%) + DIM AS LONG ub, lb, i, n + lb& = LBOUND(source_arr~&&) : ub& = UBOUND(source_arr~&&) + IF start_idx% < lb& OR start_idx% + count% > ub& THEN EXIT SUB ' out of range + IF ub& - lb& < count% THEN EXIT SUB ' too many and not enough + REDIM dest_arr(0 TO ABS(count%)) AS _UNSIGNED _INTEGER64 + IF SGN(count%) = -1 THEN + IF ((start_idx% - 1) - ABS(count%)) < 0 THEN EXIT SUB ' out of range + n& = 0 + FOR i& = (start_idx% - 1) TO ((start_idx% - 1) - ABS(count%)) STEP -1 + dest_arr~&&(n&) = source_arr~&&(i&) + n& = n& + 1 + NEXT i& + ELSE + IF ((start_idx% + 1) + ABS(count%)) > (ub& - lb&) THEN EXIT SUB ' out of range + n& = 0 + FOR i& = start_idx% + 1 TO ((start_idx% + 1) + count%) STEP 1 + dest_arr~&&(n&) = source_arr~&&(i&) + n& = n& + 1 + NEXT i& + END IF + END SUB + + + '' + ' Push a uint64 onto the end of a _UNSIGNED _INTEGER64 array + ' + ' @param _UNSIGNED _INTEGER64 arr~&&() array to push into + ' @param _UNSIGNED _INTEGER64 value~&& of byte to push + ' + SUB ARR_UINT64.push(arr~&&(), value~&&) + DIM AS LONG ub, lb + lb& = LBOUND(arr~&&) : ub& = UBOUND(arr~&&) + REDIM _PRESERVE arr(lb& TO (ub& + 1)) AS _UNSIGNED _INTEGER64 + arr~&&(ub& + 1) = value~&& + END SUB + + + '' + ' Pop a uint64 from the end of a _UNSIGNED _INTEGER64 array + ' + ' @param _UNSIGNED _INTEGER64 arr~&&() array to pop from + ' @param _UNSIGNED _INTEGER64 var~&& of uint64 to store popped uint64 + ' + SUB ARR_UINT64.pop(arr~&&(), var~&&) + DIM AS LONG ub, lb + lb& = LBOUND(arr~&&) : ub& = UBOUND(arr~&&) + var~&& = arr~&&(ub&) + REDIM _PRESERVE arr(lb& TO (ub& - 1)) AS _UNSIGNED _INTEGER64 + END SUB + + + '' + ' Pop a uint64 from the beginning of a _UNSIGNED _INTEGER64 array + ' + ' @param _UNSIGNED _INTEGER64 arr~&&() array to pop from + ' @param _UNSIGNED _INTEGER64 var~&& of uint64 to store popped uint64 + ' + SUB ARR_UINT64.shift(arr~&&(), var~&&) + DIM AS LONG ub, lb, i + lb& = LBOUND(arr~&&) : ub& = UBOUND(arr~&&) + var~&& = arr~&&(lb&) + FOR i& = lb& TO ub& - 1 + arr~&&(i&) = arr~&&(i& + 1) + NEXT i& + REDIM _PRESERVE arr(lb& + 1 TO ub&) AS _UNSIGNED _INTEGER64 + END SUB + + + '' + ' Copy an array of UINT64s to another _UNSIGNED _INTEGER64 array + ' + ' @param _UNSIGNED _INTEGER64 ARRAY source_arr~&&() source array to copy + ' @param _UNSIGNED _INTEGER64 ARRAY dest_arr~&&() dest array to copy into + ' + SUB ARR_UINT64.copy(source_arr~&&(), dest_arr~&&()) + DIM AS LONG ub, lb, i + lb& = LBOUND(source_arr~&&) : ub& = UBOUND(source_arr~&&) + REDIM dest_arr(lb& TO ub&) AS _UNSIGNED _INTEGER64 + FOR i& = lb& TO ub& + dest_arr~&&(i&) = source_arr~&&(i&) + NEXT i& + END SUB + + + '' + ' Push a uint64 into the beginning of a _UNSIGNED _INTEGER64 array + ' + ' @param _UNSIGNED _INTEGER64 arr~&&() array to push into + ' @param _UNSIGNED _INTEGER64 value~&& of uint64 to push + ' + SUB ARR_UINT64.unshift(arr~&&(), value~&&) + DIM AS LONG ub, lb, i + lb& = LBOUND(arr~&&) : ub& = UBOUND(arr~&&) + DIM work(lb& TO ub&) AS _UNSIGNED _INTEGER64 + CALL ARR_UINT64.copy(arr~&&(), work~&&()) + REDIM _PRESERVE arr(lb& TO (ub& + 1)) AS _UNSIGNED _INTEGER64 + FOR i& = lb& + 1 TO ub& + 1 + arr~&&(i&) = work~&&(i& - 1) + NEXT i& + arr~&&(lb&) = value~&& + END SUB + + + '' + ' Joins an array of UINT64s as a string + ' + ' @param _UNSIGNED _INTEGER64 ARRAY arr~&&() to get as a string + ' @param STRING s$ to store stringified array in + ' + SUB ARR_UINT64.join(arr~&&(), s$) + DIM AS LONG ub, lb, i + s$ = "" + lb& = LBOUND(arr~&&) : ub& = UBOUND(arr~&&) + FOR i& = lb& TO ub& + s$ = s$ + _TRIM$(STR$(arr~&&(i&))) + ", " + NEXT i& + ' remove trailing comma + s$ = MID$(s$, 1, (LEN(s$)-LEN(", "))) + END SUB + + + '' + ' Create a new _UNSIGNED _INTEGER64 array using string of uint64s seperated by commas + ' + ' @param _UNSIGNED _INTEGER64 ARRAY arr~&&() to store the uint64s in + ' @param STRING s$ string of comma separated uint64s + ' + SUB ARR_UINT64.new(arr~&&(), s$) + DIM AS LONG i, count + DIM t AS STRING + count& = 0 + FOR i& = 1 TO LEN(s$) + IF ASC(s$, i&) = ASC(",") THEN count& = count& + 1 + NEXT i& + REDIM arr(0 TO count&) AS _UNSIGNED _INTEGER64 + IF count& = 0 THEN + arr~&&(0) = VAL(s$) + EXIT SUB + END IF + count& = 0 + FOR i& = 1 TO LEN(s$) + t$ = t$ + CHR$(ASC(s$, i&)) + IF ASC(s$, i&) = ASC(",") OR i& = LEN(s$) THEN + arr~&&(count&) = VAL(t$) + count& = count& + 1 + t$ = "" + END IF + NEXT i& + END SUB + + + '' + ' Return the visually longest element of a _UNSIGNED _INTEGER64 array + ' + ' @param _UNSIGNED _INTEGER64 ARRAY arr~&&() to check in + ' @return _UNSIGNED _INTEGER64 value of visually longest element + ' + FUNCTION ARR_UINT64.longest~&&(arr~&&()) + DIM AS LONG lb, ub, i, res, lw + lb& = LBOUND(arr~&&) : ub& = UBOUND(arr~&&) + res& = 0 : lw& = 0 + FOR i& = lb& TO ub& + IF LEN(_TRIM$(STR$(arr~&&(i&)))) > lw& THEN + lw& = LEN(_TRIM$(STR$(arr~&&(i&)))) + res& = i& + END IF + NEXT i& + ARR_UINT64.longest~&& = arr~&&(res&) + END FUNCTION + + + '' + ' Perform some math on every element of a _UNSIGNED _INTEGER64 array + ' + ' @param _UNSIGNED _INTEGER64 ARRAY source_arr~&&() to do math on + ' @param _UNSIGNED _INTEGER64 ARRAY dest_arr~&&() to store results in + ' @param STRING op$ one of: + ' and or xor shl shr + ' "+", "-", "*", "\", "&&", "||", "!!", "<<", ">>" + ' @param _UNSIGNED _INTEGER64 value~&& to use for operand + ' + SUB ARR_UINT64.math(source_arr~&&(), dest_arr~&&(), op$, value~&&) + DIM AS LONG lb, ub, i + lb& = LBOUND(source_arr~&&) : ub& = UBOUND(source_arr~&&) + REDIM dest_arr(lb& TO ub&) AS _UNSIGNED _INTEGER64 + FOR i& = lb& TO ub& + SELECT CASE op$ + CASE "+": + dest_arr~&&(i&) = source_arr~&&(i&) + value~&& + CASE "-": + dest_arr~&&(i&) = source_arr~&&(i&) - value~&& + CASE "*": + dest_arr~&&(i&) = source_arr~&&(i&) * value~&& + CASE "\": + IF value~&& > 0 THEN + dest_arr~&&(i&) = source_arr~&&(i&) \ value~&& + END IF + CASE "&&": + dest_arr~&&(i&) = source_arr~&&(i&) AND value~&& + CASE "||": + dest_arr~&&(i&) = source_arr~&&(i&) OR value~&& + CASE "!!": + dest_arr~&&(i&) = source_arr~&&(i&) XOR value~&& + CASE "<<": + dest_arr~&&(i&) = _SHL(source_arr~&&(i&), value~&&) + CASE ">>": + dest_arr~&&(i&) = _SHR(source_arr~&&(i&), value~&&) + END SELECT + NEXT i& + END SUB + + + '' + ' Return the minimum element value in _UNSIGNED _INTEGER64 array + ' + ' @param _UNSIGNED _INTEGER64 ARRAY arr~&&() to check in + ' @return _UNSIGNED _INTEGER64 minimum value found + ' + FUNCTION ARR_UINT64.min~&&(arr~&&()) + DIM AS LONG lb, ub, i + DIM AS _UNSIGNED _INTEGER64 s + lb& = LBOUND(arr~&&) : ub& = UBOUND(arr~&&) + s~&& = 127 + FOR i& = lb& TO ub& + IF arr~&&(i&) < s~&& THEN + s~&& = arr~&&(i&) + END IF + NEXT i& + ARR_UINT64.min~&& = s~&& + END FUNCTION + + + '' + ' Return the maximum element value in _UNSIGNED _INTEGER64 array + ' + ' @param _UNSIGNED _INTEGER64 ARRAY arr~&&() to check in + ' @return _UNSIGNED _INTEGER64 maximum value found + ' + FUNCTION ARR_UINT64.max~&&(arr~&&()) + DIM AS LONG lb, ub, i + DIM AS _UNSIGNED _INTEGER64 s + lb& = LBOUND(arr~&&) : ub& = UBOUND(arr~&&) + s~&& = 0 + FOR i& = lb& TO ub& + IF arr~&&(i&) > s~&& THEN + s~&& = arr~&&(i&) + END IF + NEXT i& + ARR_UINT64.max~&& = s~&& + END FUNCTION + + + '' + ' Return the visually shortest element of a _UNSIGNED _INTEGER64 array + ' + ' @param _UNSIGNED _INTEGER64 ARRAY arr~&&() to check in + ' @return _UNSIGNED _INTEGER64 value of visually shortest element + ' + FUNCTION ARR_UINT64.shortest~&&(arr~&&()) + DIM AS LONG lb, ub, i, res, sw + lb& = LBOUND(arr~&&) : ub& = UBOUND(arr~&&) + res& = 0 : sw& = 0 + FOR i& = lb& TO ub& + IF LEN(_TRIM$(STR$(arr~&&(i&)))) < sw& THEN + sw& = LEN(_TRIM$(STR$(arr~&&(i&)))) + res& = i& + END IF + NEXT i& + ARR_UINT64.shortest~&& = arr~&&(res&) + END FUNCTION + + + '' + ' Return the first element of a _UNSIGNED _INTEGER64 array + ' + ' @param _UNSIGNED _INTEGER64 ARRAY arr~&&() to check in + ' @return _UNSIGNED _INTEGER64 value of first element + ' + FUNCTION ARR_UINT64.first~&&(arr~&&()) + ARR_UINT64.first~&& = arr~&&(LBOUND(arr~&&)) + END FUNCTION + + + '' + ' Return the last element of a _UNSIGNED _INTEGER64 array + ' + ' @param _UNSIGNED _INTEGER64 ARRAY arr~&&() to check in + ' @return _UNSIGNED _INTEGER64 value of last element + ' + FUNCTION ARR_UINT64.last~&&(arr~&&()) + ARR_UINT64.last~&& = arr~&&(UBOUND(arr~&&)) + END FUNCTION + + + '' + ' Return every nth array element of a _UNSIGNED _INTEGER64 array + ' + ' @param _UNSIGNED _INTEGER64 ARRAY source_arr~&&() to get from + ' @param _UNSIGNED _INTEGER64 ARRAY dest_arr~&&() to store in + ' @param INTEGER nth% element + ' + SUB ARR_UINT64.nth(source_arr~&&(), dest_arr~&&(), nth%) + DIM AS LONG lb, ub, i, n + lb& = LBOUND(source_arr~&&) : ub& = UBOUND(source_arr~&&) + n& = (ub& - lb&) \ nth% + REDIM dest_arr(n&) AS _UNSIGNED _INTEGER64 + n& = 0 + FOR i& = lb& to ub& + IF i& MOD nth% = 0 THEN + dest_arr~&&(n&) = source_arr~&&(i&) + n& = n& + 1 + END IF + NEXT i& + END SUB + + + '' + ' Checks if value exists in _UNSIGNED _INTEGER64 array + ' + ' @param _UNSIGNED _INTEGER64 ARRAY arr~&&() to check in + ' @param _UNSIGNED _INTEGER64 value~&& value to check for + ' @return INTEGER -1 if found or 0 if not found + ' + FUNCTION ARR_UINT64.in%(arr~&&(), value~&&) + DIM AS LONG lb, ub, i + lb& = LBOUND(arr~&&) : ub& = UBOUND(arr~&&) + FOR i& = lb& TO ub& + IF arr~&&(i&) = value~&& THEN + ARR_UINT64.in% = -1 + EXIT FUNCTION + END IF + NEXT i& + ARR_UINT64.in% = 0 + END FUNCTION + + + '' + ' Checks if value exists in _UNSIGNED _INTEGER64 array and returns index if found + ' + ' @param _UNSIGNED _INTEGER64 ARRAY arr~&&() to check in + ' @param _UNSIGNED _INTEGER64 value~&& value to check for + ' @return INTEGER index of element if found or -1 if not found + ' + FUNCTION ARR_UINT64.find%(arr~&&(), value~&&) + DIM AS LONG lb, ub, i + lb& = LBOUND(arr~&&) : ub& = UBOUND(arr~&&) + FOR i& = lb& TO ub& + IF arr~&&(i&) = value~&& THEN + ARR_UINT64.find% = i& + EXIT FUNCTION + END IF + NEXT i& + ARR_UINT64.find% = -1 + END FUNCTION + + + '' + ' Return the number of elements in a _UNSIGNED _INTEGER64 array + ' + ' @param _UNSIGNED _INTEGER64 ARRAY arr~&&() to count + ' @return INTEGER number of elements + ' + FUNCTION ARR_UINT64.count&(arr~&&()) + ARR_UINT64.count& = UBOUND(arr~&&) - LBOUND(arr~&&) + END FUNCTION + + + '' + ' Return the size of a _UNSIGNED _INTEGER64 array + ' + ' @param _UNSIGNED _INTEGER64 ARRAY arr~&&() to get size of + ' @return LONG size in bytes + ' + FUNCTION ARR_UINT64.size&(arr~&&()) + ARR_UINT64.size& = LEN(arr~&&()) + END FUNCTION + + + '' + ' Reverses the elements of a _UNSIGNED _INTEGER64 array + ' + ' @param _UNSIGNED _INTEGER64 ARRAY source_arr~&&() to reverse + ' @param _UNSIGNED _INTEGER64 ARRAY dest_arr~&&() to store reversed array in + ' + SUB ARR_UINT64.reverse(source_arr~&&(), dest_arr~&&()) + DIM AS LONG lb, ub, i, n + lb& = LBOUND(source_arr~&&) : ub& = UBOUND(source_arr~&&) + REDIM dest_arr(0 TO (ub& - lb&)) AS _UNSIGNED _INTEGER64 + n& = 0 + FOR i& = ub& TO lb& STEP -1 + dest_arr~&&(n&) = source_arr~&&(i&) + n& = n& + 1 + NEXT i& + END SUB + + + '' + ' Returns a random uint64 from a _UNSIGNED _INTEGER64 array + ' + ' @param _UNSIGNED _INTEGER64 ARRAY arr~&&() array to get random element from + ' @return _UNSIGNED _INTEGER64 random element + ' + FUNCTION ARR_UINT64.random~&&(arr~&&()) + DIM AS LONG lb, ub + lb& = LBOUND(arr~&&) : ub& = UBOUND(arr~&&) + RANDOMIZE TIMER + ARR_UINT64.random~&& = arr~&&(INT(RND * (ub& - lb&)) + 1) + END FUNCTION + + + '' + ' Returns the sum of all elements in a _UNSIGNED _INTEGER64 array + ' + ' @param _UNSIGNED _INTEGER64 ARRAY arr~&&() array to get some for + ' @return LONG sum of all elements + ' + FUNCTION ARR_UINT64.sum&(arr~&&()) + DIM AS LONG lb, ub, i + DIM sum AS LONG + lb& = LBOUND(arr~&&) : ub& = UBOUND(arr~&&) + FOR i& = lb& TO ub& + sum& = sum& + arr~&&(i&) + NEXT i& + ARR_UINT64.sum& = sum& + END FUNCTION + + + '' + ' Returns the average value of elements in a _UNSIGNED _INTEGER64 array + ' + ' @param _UNSIGNED _INTEGER64 ARRAY arr~&&() array to get average for + ' @return LONG average of elements + ' + FUNCTION ARR_UINT64.avg&(arr~&&()) + DIM AS LONG lb, ub, i + DIM sum AS LONG + lb& = LBOUND(arr~&&) : ub& = UBOUND(arr~&&) + FOR i& = lb& TO ub& + sum& = sum& + arr~&&(i&) + NEXT i& + ARR_UINT64.avg& = sum& / (ub& - lb&) + END FUNCTION + + + '' + ' Shuffle the elements of a _UNSIGNED _INTEGER64 array + ' + ' @param _UNSIGNED _INTEGER64 ARRAY source_arr~&&() to shuffle + ' @param _UNSIGNED _INTEGER64 ARRAY dest_arr~&&() to store shuffled array in + ' + SUB ARR_UINT64.shuffle(source_arr~&&(), dest_arr~&&()) + DIM AS LONG lb, ub, i, count + lb& = LBOUND(source_arr~&&) : ub& = UBOUND(source_arr~&&) + count& = ub& - lb& + REDIM dest_arr(0 TO count&) AS _UNSIGNED _INTEGER64 + CALL ARR_UINT64.copy(source_arr~&&(), dest_arr~&&()) + RANDOMIZE TIMER + FOR i& = 0 TO count& + SWAP dest_arr~&&(i&), dest_arr~&&(lb& + RND * (ub& - lb&)) + NEXT i& + END SUB + + + '' + ' Makes a _UNSIGNED _INTEGER64 array contain only unique values + ' + ' @param _UNSIGNED _INTEGER64 ARRAY source_arr~&&() array to get uniques for + ' @param _UNSIGNED _INTEGER64 ARRAY dest_arr~&&() array to store uniques in + ' + SUB ARR_UINT64.unique(source_arr~&&(), dest_arr~&&()) + DIM AS LONG lb, ub, i + DIM tmp AS _UNSIGNED _INTEGER64 + lb& = LBOUND(source_arr~&&) : ub& = UBOUND(source_arr~&&) + DIM work_arr(0) AS _UNSIGNED _INTEGER64 + FOR i& = lb& TO ub& + IF NOT ARR_UINT64.in%(work_arr~&&(), source_arr~&&(i&)) THEN + CALL ARR_UINT64.push(work_arr~&&(), source_arr~&&(i&)) + END IF + NEXT i& + CALL ARR_UINT64.shift(work_arr~&&(), tmp~&&) + CALL ARR_UINT64.copy(work_arr~&&(), dest_arr~&&()) + END SUB + + + '' + ' Filters a _UNSIGNED _INTEGER64 array to only elements greater than value + ' + ' @param _UNSIGNED _INTEGER64 ARRAY source_arr~&&() array to work on + ' @param _UNSIGNED _INTEGER64 ARRAY dest_arr~&&() array to store in + ' @param _UNSIGNED _INTEGER64 value~&& to be greater than to be returned + ' + SUB ARR_UINT64.gt(source_arr~&&(), dest_arr~&&(), value~&&) + DIM AS LONG lb, ub, i + DIM tmp AS _UNSIGNED _INTEGER64 + lb& = LBOUND(source_arr~&&) : ub& = UBOUND(source_arr~&&) + DIM work_arr(0) AS _UNSIGNED _INTEGER64 + FOR i& = lb& TO ub& + IF source_arr~&&(i&) > value~&& THEN + CALL ARR_UINT64.push(work_arr~&&(), source_arr~&&(i&)) + END IF + NEXT i& + CALL ARR_UINT64.shift(work_arr~&&(), tmp~&&) + CALL ARR_UINT64.copy(work_arr~&&(), dest_arr~&&()) + END SUB + + + '' + ' Filters a _UNSIGNED _INTEGER64 array to only elements greater than or equal to value + ' + ' @param _UNSIGNED _INTEGER64 ARRAY source_arr~&&() array to work on + ' @param _UNSIGNED _INTEGER64 ARRAY dest_arr~&&() array to store in + ' @param _UNSIGNED _INTEGER64 value~&& to be greater than or equal to be returned + ' + SUB ARR_UINT64.gte(source_arr~&&(), dest_arr~&&(), value~&&) + DIM AS LONG lb, ub, i + DIM tmp AS _UNSIGNED _INTEGER64 + lb& = LBOUND(source_arr~&&) : ub& = UBOUND(source_arr~&&) + DIM work_arr(0) AS _UNSIGNED _INTEGER64 + FOR i& = lb& TO ub& + IF source_arr~&&(i&) >= value~&& THEN + CALL ARR_UINT64.push(work_arr~&&(), source_arr~&&(i&)) + END IF + NEXT i& + CALL ARR_UINT64.shift(work_arr~&&(), tmp~&&) + CALL ARR_UINT64.copy(work_arr~&&(), dest_arr~&&()) + END SUB + + + '' + ' Filters a _UNSIGNED _INTEGER64 array to only elements less than value + ' + ' @param _UNSIGNED _INTEGER64 ARRAY source_arr~&&() array to work on + ' @param _UNSIGNED _INTEGER64 ARRAY dest_arr~&&() array to store in + ' @param _UNSIGNED _INTEGER64 value~&& to be less than to be returned + ' + SUB ARR_UINT64.lt(source_arr~&&(), dest_arr~&&(), value~&&) + DIM AS LONG lb, ub, i + DIM tmp AS _UNSIGNED _INTEGER64 + lb& = LBOUND(source_arr~&&) : ub& = UBOUND(source_arr~&&) + DIM work_arr(0) AS _UNSIGNED _INTEGER64 + FOR i& = lb& TO ub& + IF source_arr~&&(i&) < value~&& THEN + CALL ARR_UINT64.push(work_arr~&&(), source_arr~&&(i&)) + END IF + NEXT i& + CALL ARR_UINT64.shift(work_arr~&&(), tmp~&&) + CALL ARR_UINT64.copy(work_arr~&&(), dest_arr~&&()) + END SUB + + + '' + ' Filters a _UNSIGNED _INTEGER64 array to only elements less than or equal to value + ' + ' @param _UNSIGNED _INTEGER64 ARRAY source_arr~&&() array to work on + ' @param _UNSIGNED _INTEGER64 ARRAY dest_arr~&&() array to store in + ' @param _UNSIGNED _INTEGER64 value~&& to be less than or equal to be returned + ' + SUB ARR_UINT64.lte(source_arr~&&(), dest_arr~&&(), value~&&) + DIM AS LONG lb, ub, i + DIM tmp AS _UNSIGNED _INTEGER64 + lb& = LBOUND(source_arr~&&) : ub& = UBOUND(source_arr~&&) + DIM work_arr(0) AS _UNSIGNED _INTEGER64 + FOR i& = lb& TO ub& + IF source_arr~&&(i&) <= value~&& THEN + CALL ARR_UINT64.push(work_arr~&&(), source_arr~&&(i&)) + END IF + NEXT i& + CALL ARR_UINT64.shift(work_arr~&&(), tmp~&&) + CALL ARR_UINT64.copy(work_arr~&&(), dest_arr~&&()) + END SUB + + + '' + ' Finds and replaces values across all elements in a _UNSIGNED _INTEGER64 ARRAY + ' + ' @param _UNSIGNED _INTEGER64 ARRAY arr~&&() to check in + ' @param _UNSIGNED _INTEGER64 find~&& value to find + ' @param _UNSIGNED _INTEGER64 replace~&& value to replace with if found + ' + SUB ARR_UINT64.replace(arr~&&(), find~&&, replace~&&) + DIM AS LONG lb, ub, i + lb& = LBOUND(arr~&&) : ub& = UBOUND(arr~&&) + FOR i& = lb& TO ub& + IF arr~&&(i&) = find~&& THEN + arr~&&(i&) = replace~&& + END IF + NEXT i& + END SUB + + + '' + ' Inserts a new element into _UNSIGNED _INTEGER64 array after index + ' + ' @param _UNSIGNED _INTEGER64 ARRAY arr~&&() array to work on + ' @param _UNSIGNED _INTEGER64 value~&& to insert + ' @param INTEGER index% of element to insert at + ' + SUB ARR_UINT64.insert(arr~&&(), value~&&, index%) + DIM AS LONG lb, ub, i + DIM tmp AS _UNSIGNED _INTEGER64 + DIM work_arr(0) AS _UNSIGNED _INTEGER64 + lb& = LBOUND(arr~&&) : ub& = UBOUND(arr~&&) + IF index% >= lb& AND index% <= ub& THEN + ' build new array up to index + FOR i& = lb& TO index% + CALL ARR_UINT64.push(work_arr~&&(), arr~&&(i&)) + NEXT i& + ' insert new element + CALL ARR_UINT64.push(work_arr~&&(), value~&&) + ' finish building array from index + 1 + FOR i& = index% + 1 TO ub& + CALL ARR_UINT64.push(work_arr~&&(), arr~&&(i&)) + NEXT i& + CALL ARR_UINT64.shift(work_arr~&&(), tmp~&&) + CALL ARR_UINT64.copy(work_arr~&&(), arr~&&()) + END IF + END SUB + + + '' + ' Removes element from a _UNSIGNED _INTEGER64 array by element index + ' + ' @param _UNSIGNED _INTEGER64 ARRAY arr~&&() array to work on + ' @param INTEGER index% of element to remove + ' + SUB ARR_UINT64.remove(arr~&&(), index%) + DIM AS LONG lb, ub, i + DIM tmp AS _UNSIGNED _INTEGER64 + DIM work_arr(0) AS _UNSIGNED _INTEGER64 + lb& = LBOUND(arr~&&) : ub& = UBOUND(arr~&&) + IF index% >= lb& AND index% <= ub& THEN + ' build new array up to index + FOR i& = lb& TO index% - 1 + CALL ARR_UINT64.push(work_arr~&&(), arr~&&(i&)) + NEXT i& + ' skip elements + FOR i& = index% + 1 TO ub& + CALL ARR_UINT64.push(work_arr~&&(), arr~&&(i&)) + NEXT i& + CALL ARR_UINT64.shift(work_arr~&&(), tmp~&&) + CALL ARR_UINT64.copy(work_arr~&&(), arr~&&()) + END IF + END SUB + + + '' + ' Filters a _UNSIGNED _INTEGER64 array to only elements that have odd values + ' + ' @param _UNSIGNED _INTEGER64 ARRAY source_arr~&&() array to work on + ' @param _UNSIGNED _INTEGER64 ARRAY dest_arr~&&() array to store in + ' + SUB ARR_UINT64.odd(source_arr~&&(), dest_arr~&&()) + DIM AS LONG lb, ub, i + DIM tmp AS _UNSIGNED _INTEGER64 + lb& = LBOUND(source_arr~&&) : ub& = UBOUND(source_arr~&&) + DIM work_arr(0) AS _UNSIGNED _INTEGER64 + FOR i& = lb& TO ub& + IF source_arr~&&(i&) MOD 2 <> 0 THEN + CALL ARR_UINT64.push(work_arr~&&(), source_arr~&&(i&)) + END IF + NEXT i& + CALL ARR_UINT64.shift(work_arr~&&(), tmp~&&) + CALL ARR_UINT64.copy(work_arr~&&(), dest_arr~&&()) + END SUB + + + '' + ' Filters a _UNSIGNED _INTEGER64 array to only elements that have even values + ' + ' @param _UNSIGNED _INTEGER64 ARRAY source_arr~&&() array to work on + ' @param _UNSIGNED _INTEGER64 ARRAY dest_arr~&&() array to store in + ' + SUB ARR_UINT64.even(source_arr~&&(), dest_arr~&&()) + DIM AS LONG lb, ub, i + DIM tmp AS _UNSIGNED _INTEGER64 + lb& = LBOUND(source_arr~&&) : ub& = UBOUND(source_arr~&&) + DIM work_arr(0) AS _UNSIGNED _INTEGER64 + FOR i& = lb& TO ub& + IF source_arr~&&(i&) MOD 2 = 0 THEN + CALL ARR_UINT64.push(work_arr~&&(), source_arr~&&(i&)) + END IF + NEXT i& + CALL ARR_UINT64.shift(work_arr~&&(), tmp~&&) + CALL ARR_UINT64.copy(work_arr~&&(), dest_arr~&&()) + END SUB + + + '' + ' Filters a _UNSIGNED _INTEGER64 array to only elements that have values evenly divisible by divisor + ' + ' @param _UNSIGNED _INTEGER64 ARRAY source_arr~&&() array to work on + ' @param _UNSIGNED _INTEGER64 ARRAY dest_arr~&&() array to store in + ' @param _UNSIGNED _INTEGER64 divisor~&& for modulo + ' + SUB ARR_UINT64.mod(source_arr~&&(), dest_arr~&&(), divisor~&&) + DIM AS LONG lb, ub, i + DIM tmp AS _UNSIGNED _INTEGER64 + lb& = LBOUND(source_arr~&&) : ub& = UBOUND(source_arr~&&) + DIM work_arr(0) AS _UNSIGNED _INTEGER64 + FOR i& = lb& TO ub& + IF source_arr~&&(i&) MOD divisor~&& = 0 THEN + CALL ARR_UINT64.push(work_arr~&&(), source_arr~&&(i&)) + END IF + NEXT i& + CALL ARR_UINT64.shift(work_arr~&&(), tmp~&&) + CALL ARR_UINT64.copy(work_arr~&&(), dest_arr~&&()) + END SUB + + + '' + ' Filters a _UNSIGNED _INTEGER64 array to only elements between min and max + ' + ' @param _UNSIGNED _INTEGER64 ARRAY source_arr~&&() array to work on + ' @param _UNSIGNED _INTEGER64 ARRAY dest_arr~&&() array to store in + ' @param _UNSIGNED _INTEGER64 min~&& to be greater than or equal to be returned + ' @param _UNSIGNED _INTEGER64 max~&& to be less than or equal to be returned + ' + SUB ARR_UINT64.between(source_arr~&&(), dest_arr~&&(), min~&&, max~&&) + DIM AS LONG lb, ub, i + DIM tmp AS _UNSIGNED _INTEGER64 + lb& = LBOUND(source_arr~&&) : ub& = UBOUND(source_arr~&&) + DIM work_arr(0) AS _UNSIGNED _INTEGER64 + FOR i& = lb& TO ub& + IF source_arr~&&(i&) >= min~&& _ + AND source_arr~&&(i&) <= max~&& THEN + CALL ARR_UINT64.push(work_arr~&&(), source_arr~&&(i&)) + END IF + NEXT i& + CALL ARR_UINT64.shift(work_arr~&&(), tmp~&&) + CALL ARR_UINT64.copy(work_arr~&&(), dest_arr~&&()) + END SUB + + + '' + ' Sorts _UNSIGNED _INTEGER64 array in ascending order + ' + ' @param _UNSIGNED _INTEGER64 ARRAY source_arr~&&() array to sort + ' @param _UNSIGNED _INTEGER64 ARRAY dest_arr~&&() array to store sorted in + ' + SUB ARR_UINT64.sort(source_arr~&&(), dest_arr~&&()) + DIM AS LONG lb, ub, count + lb& = LBOUND(source_arr~&&) : ub& = UBOUND(source_arr~&&) + count& = ub& - lb& + REDIM dest_arr(0 TO count&) AS _UNSIGNED _INTEGER64 + CALL ARR_UINT64.copy(source_arr~&&(), dest_arr~&&()) + CALL ARR_UINT64.quicksort(dest_arr~&&(), lb&, ub&, 0) + END SUB + + + '' + ' Sorts _UNSIGNED _INTEGER64 array in descending order + ' + ' @param _UNSIGNED _INTEGER64 ARRAY source_arr~&&() array to sort + ' @param _UNSIGNED _INTEGER64 ARRAY dest_arr~&&() array to store sorted in + ' + SUB ARR_UINT64.rsort(source_arr~&&(), dest_arr~&&()) + DIM AS LONG lb, ub, count + lb& = LBOUND(source_arr~&&) : ub& = UBOUND(source_arr~&&) + count& = ub& - lb& + REDIM dest_arr(0 TO count&) AS _UNSIGNED _INTEGER64 + CALL ARR_UINT64.copy(source_arr~&&(), dest_arr~&&()) + CALL ARR_UINT64.quicksort(dest_arr~&&(), lb&, ub&, 1) + END SUB + + + '' + ' Quicksort array with pivot algorithm by logiclrd + ' + ' @link https://www.tek-tips.com/faqs.cfm?fid=336 + ' @param _UNSIGNED _INTEGER64 ARRAY array~&&() to sort + ' @param INTEGER start% of range to sort + ' @param INTEGER finish% range of sort + ' @param INTEGER order% to sort by (0 = asc / 1 = desc) + ' + SUB ARR_UINT64.quicksort(arr~&&(), start%, finish%, order%) + DIM i AS LONG + DIM pivot AS INTEGER + DIM pivotvalue AS _UNSIGNED _INTEGER64 + + 'first, partition the array + pivot% = start% + pivotvalue~&& = arr~&&(pivot%) + FOR i& = start% + 1 TO finish% + IF order% = 0 THEN ' ascending order + IF arr~&&(i&) < pivotvalue~&& THEN + arr~&&(pivot%) = arr~&&(i&) + arr~&&(i&) = arr~&&(pivot% + 1) + arr~&&(pivot% + 1) = pivotvalue~&& + pivot% = pivot% + 1 + END IF + ELSEIF order% = 1 THEN ' descending order + IF arr~&&(i&) > pivotvalue~&& THEN + arr~&&(pivot%) = arr~&&(i&) + arr~&&(i&) = arr~&&(pivot% + 1) + arr~&&(pivot% + 1) = pivotvalue~&& + pivot% = pivot% + 1 + END IF + END IF + NEXT i& + + 'then, sort the subarrays to each side of the pivot + IF pivot% - start% >= 2 THEN + CALL ARR_UINT64.quicksort(arr~&&(), start%, pivot% - 1, order%) + END IF + IF finish% - pivot% >= 2 THEN + CALL ARR_UINT64.quicksort(arr~&&(), pivot% + 1, finish%, order%) + END IF + END SUB + $END IF + $IF GJ_LIB_ARR_ULONG_BAS = UNDEFINED THEN + '$DYNAMIC + $LET GJ_LIB_ARR_ULONG_BAS = 1 + + '' + ' Slice an array from source to destination starting at index and count slices + ' + ' @param _UNSIGNED LONG() source_arr~& to slice from + ' @param _UNSIGNED LONG() dest_arr~& to put slices into + ' @param INTEGER start_idx% starting index to use as slice range + ' @param INTEGER count% number of slices - if negative, backwards from index + ' + SUB ARR_ULONG.slice(source_arr~&(), dest_arr~&(), start_idx%, count%) + DIM AS LONG ub, lb, i, n + lb& = LBOUND(source_arr~&) : ub& = UBOUND(source_arr~&) + IF start_idx% < lb& OR start_idx% + count% > ub& THEN EXIT SUB ' out of range + IF ub& - lb& < count% THEN EXIT SUB ' too many and not enough + REDIM dest_arr(0 TO ABS(count%)) AS _UNSIGNED LONG + IF SGN(count%) = -1 THEN + IF ((start_idx% - 1) - ABS(count%)) < 0 THEN EXIT SUB ' out of range + n& = 0 + FOR i& = (start_idx% - 1) TO ((start_idx% - 1) - ABS(count%)) STEP -1 + dest_arr~&(n&) = source_arr~&(i&) + n& = n& + 1 + NEXT i& + ELSE + IF ((start_idx% + 1) + ABS(count%)) > (ub& - lb&) THEN EXIT SUB ' out of range + n& = 0 + FOR i& = start_idx% + 1 TO ((start_idx% + 1) + count%) STEP 1 + dest_arr~&(n&) = source_arr~&(i&) + n& = n& + 1 + NEXT i& + END IF + END SUB + + + '' + ' Push a ulong onto the end of a _UNSIGNED LONG array + ' + ' @param _UNSIGNED LONG arr~&() array to push into + ' @param _UNSIGNED LONG value~& of byte to push + ' + SUB ARR_ULONG.push(arr~&(), value~&) + DIM AS LONG ub, lb + lb& = LBOUND(arr~&) : ub& = UBOUND(arr~&) + REDIM _PRESERVE arr(lb& TO (ub& + 1)) AS _UNSIGNED LONG + arr~&(ub& + 1) = value~& + END SUB + + + '' + ' Pop a ulong from the end of a _UNSIGNED LONG array + ' + ' @param _UNSIGNED LONG arr~&() array to pop from + ' @param _UNSIGNED LONG var~& of ulong to store popped ulong + ' + SUB ARR_ULONG.pop(arr~&(), var~&) + DIM AS LONG ub, lb + lb& = LBOUND(arr~&) : ub& = UBOUND(arr~&) + var~& = arr~&(ub&) + REDIM _PRESERVE arr(lb& TO (ub& - 1)) AS _UNSIGNED LONG + END SUB + + + '' + ' Pop a ulong from the beginning of a _UNSIGNED LONG array + ' + ' @param _UNSIGNED LONG arr~&() array to pop from + ' @param _UNSIGNED LONG var~& of ulong to store popped ulong + ' + SUB ARR_ULONG.shift(arr~&(), var~&) + DIM AS LONG ub, lb, i + lb& = LBOUND(arr~&) : ub& = UBOUND(arr~&) + var~& = arr~&(lb&) + FOR i& = lb& TO ub& - 1 + arr~&(i&) = arr~&(i& + 1) + NEXT i& + REDIM _PRESERVE arr(lb& + 1 TO ub&) AS _UNSIGNED LONG + END SUB + + + '' + ' Copy an array of ULONGs to another _UNSIGNED LONG array + ' + ' @param _UNSIGNED LONG ARRAY source_arr~&() source array to copy + ' @param _UNSIGNED LONG ARRAY dest_arr~&() dest array to copy into + ' + SUB ARR_ULONG.copy(source_arr~&(), dest_arr~&()) + DIM AS LONG ub, lb, i + lb& = LBOUND(source_arr~&) : ub& = UBOUND(source_arr~&) + REDIM dest_arr(lb& TO ub&) AS _UNSIGNED LONG + FOR i& = lb& TO ub& + dest_arr~&(i&) = source_arr~&(i&) + NEXT i& + END SUB + + + '' + ' Push a ulong into the beginning of a _UNSIGNED LONG array + ' + ' @param _UNSIGNED LONG arr~&() array to push into + ' @param _UNSIGNED LONG value~& of ulong to push + ' + SUB ARR_ULONG.unshift(arr~&(), value~&) + DIM AS LONG ub, lb, i + lb& = LBOUND(arr~&) : ub& = UBOUND(arr~&) + DIM work(lb& TO ub&) AS _UNSIGNED LONG + CALL ARR_ULONG.copy(arr~&(), work~&()) + REDIM _PRESERVE arr(lb& TO (ub& + 1)) AS _UNSIGNED LONG + FOR i& = lb& + 1 TO ub& + 1 + arr~&(i&) = work~&(i& - 1) + NEXT i& + arr~&(lb&) = value~& + END SUB + + + '' + ' Joins an array of ULONGs as a string + ' + ' @param _UNSIGNED LONG ARRAY arr~&() to get as a string + ' @param STRING s$ to store stringified array in + ' + SUB ARR_ULONG.join(arr~&(), s$) + DIM AS LONG ub, lb, i + s$ = "" + lb& = LBOUND(arr~&) : ub& = UBOUND(arr~&) + FOR i& = lb& TO ub& + s$ = s$ + _TRIM$(STR$(arr~&(i&))) + ", " + NEXT i& + ' remove trailing comma + s$ = MID$(s$, 1, (LEN(s$)-LEN(", "))) + END SUB + + + '' + ' Create a new _UNSIGNED LONG array using string of ulongs seperated by commas + ' + ' @param _UNSIGNED LONG ARRAY arr~&() to store the ulongs in + ' @param STRING s$ string of comma separated ulongs + ' + SUB ARR_ULONG.new(arr~&(), s$) + DIM AS LONG i, count + DIM t AS STRING + count& = 0 + FOR i& = 1 TO LEN(s$) + IF ASC(s$, i&) = ASC(",") THEN count& = count& + 1 + NEXT i& + REDIM arr(0 TO count&) AS _UNSIGNED LONG + IF count& = 0 THEN + arr~&(0) = VAL(s$) + EXIT SUB + END IF + count& = 0 + FOR i& = 1 TO LEN(s$) + t$ = t$ + CHR$(ASC(s$, i&)) + IF ASC(s$, i&) = ASC(",") OR i& = LEN(s$) THEN + arr~&(count&) = VAL(t$) + count& = count& + 1 + t$ = "" + END IF + NEXT i& + END SUB + + + '' + ' Return the visually longest element of a _UNSIGNED LONG array + ' + ' @param _UNSIGNED LONG ARRAY arr~&() to check in + ' @return _UNSIGNED LONG value of visually longest element + ' + FUNCTION ARR_ULONG.longest~&(arr~&()) + DIM AS LONG lb, ub, i, res, lw + lb& = LBOUND(arr~&) : ub& = UBOUND(arr~&) + res& = 0 : lw& = 0 + FOR i& = lb& TO ub& + IF LEN(_TRIM$(STR$(arr~&(i&)))) > lw& THEN + lw& = LEN(_TRIM$(STR$(arr~&(i&)))) + res& = i& + END IF + NEXT i& + ARR_ULONG.longest~& = arr~&(res&) + END FUNCTION + + + '' + ' Perform some math on every element of a _UNSIGNED LONG array + ' + ' @param _UNSIGNED LONG ARRAY source_arr~&() to do math on + ' @param _UNSIGNED LONG ARRAY dest_arr~&() to store results in + ' @param STRING op$ one of: + ' and or xor shl shr + ' "+", "-", "*", "\", "&&", "||", "!!", "<<", ">>" + ' @param _UNSIGNED LONG value~& to use for operand + ' + SUB ARR_ULONG.math(source_arr~&(), dest_arr~&(), op$, value~&) + DIM AS LONG lb, ub, i + lb& = LBOUND(source_arr~&) : ub& = UBOUND(source_arr~&) + REDIM dest_arr(lb& TO ub&) AS _UNSIGNED LONG + FOR i& = lb& TO ub& + SELECT CASE op$ + CASE "+": + dest_arr~&(i&) = source_arr~&(i&) + value~& + CASE "-": + dest_arr~&(i&) = source_arr~&(i&) - value~& + CASE "*": + dest_arr~&(i&) = source_arr~&(i&) * value~& + CASE "\": + IF value~& > 0 THEN + dest_arr~&(i&) = source_arr~&(i&) \ value~& + END IF + CASE "&&": + dest_arr~&(i&) = source_arr~&(i&) AND value~& + CASE "||": + dest_arr~&(i&) = source_arr~&(i&) OR value~& + CASE "!!": + dest_arr~&(i&) = source_arr~&(i&) XOR value~& + CASE "<<": + dest_arr~&(i&) = _SHL(source_arr~&(i&), value~&) + CASE ">>": + dest_arr~&(i&) = _SHR(source_arr~&(i&), value~&) + END SELECT + NEXT i& + END SUB + + + '' + ' Return the minimum element value in _UNSIGNED LONG array + ' + ' @param _UNSIGNED LONG ARRAY arr~&() to check in + ' @return _UNSIGNED LONG minimum value found + ' + FUNCTION ARR_ULONG.min~&(arr~&()) + DIM AS LONG lb, ub, i + DIM AS _UNSIGNED LONG s + lb& = LBOUND(arr~&) : ub& = UBOUND(arr~&) + s~& = 127 + FOR i& = lb& TO ub& + IF arr~&(i&) < s~& THEN + s~& = arr~&(i&) + END IF + NEXT i& + ARR_ULONG.min~& = s~& + END FUNCTION + + + '' + ' Return the maximum element value in _UNSIGNED LONG array + ' + ' @param _UNSIGNED LONG ARRAY arr~&() to check in + ' @return _UNSIGNED LONG maximum value found + ' + FUNCTION ARR_ULONG.max~&(arr~&()) + DIM AS LONG lb, ub, i + DIM AS _UNSIGNED LONG s + lb& = LBOUND(arr~&) : ub& = UBOUND(arr~&) + s~& = 0 + FOR i& = lb& TO ub& + IF arr~&(i&) > s~& THEN + s~& = arr~&(i&) + END IF + NEXT i& + ARR_ULONG.max~& = s~& + END FUNCTION + + + '' + ' Return the visually shortest element of a _UNSIGNED LONG array + ' + ' @param _UNSIGNED LONG ARRAY arr~&() to check in + ' @return _UNSIGNED LONG value of visually shortest element + ' + FUNCTION ARR_ULONG.shortest~&(arr~&()) + DIM AS LONG lb, ub, i, res, sw + lb& = LBOUND(arr~&) : ub& = UBOUND(arr~&) + res& = 0 : sw& = 0 + FOR i& = lb& TO ub& + IF LEN(_TRIM$(STR$(arr~&(i&)))) < sw& THEN + sw& = LEN(_TRIM$(STR$(arr~&(i&)))) + res& = i& + END IF + NEXT i& + ARR_ULONG.shortest~& = arr~&(res&) + END FUNCTION + + + '' + ' Return the first element of a _UNSIGNED LONG array + ' + ' @param _UNSIGNED LONG ARRAY arr~&() to check in + ' @return _UNSIGNED LONG value of first element + ' + FUNCTION ARR_ULONG.first~&(arr~&()) + ARR_ULONG.first~& = arr~&(LBOUND(arr~&)) + END FUNCTION + + + '' + ' Return the last element of a _UNSIGNED LONG array + ' + ' @param _UNSIGNED LONG ARRAY arr~&() to check in + ' @return _UNSIGNED LONG value of last element + ' + FUNCTION ARR_ULONG.last~&(arr~&()) + ARR_ULONG.last~& = arr~&(UBOUND(arr~&)) + END FUNCTION + + + '' + ' Return every nth array element of a _UNSIGNED LONG array + ' + ' @param _UNSIGNED LONG ARRAY source_arr~&() to get from + ' @param _UNSIGNED LONG ARRAY dest_arr~&() to store in + ' @param INTEGER nth% element + ' + SUB ARR_ULONG.nth(source_arr~&(), dest_arr~&(), nth%) + DIM AS LONG lb, ub, i, n + lb& = LBOUND(source_arr~&) : ub& = UBOUND(source_arr~&) + n& = (ub& - lb&) \ nth% + REDIM dest_arr(n&) AS _UNSIGNED LONG + n& = 0 + FOR i& = lb& to ub& + IF i& MOD nth% = 0 THEN + dest_arr~&(n&) = source_arr~&(i&) + n& = n& + 1 + END IF + NEXT i& + END SUB + + + '' + ' Checks if value exists in _UNSIGNED LONG array + ' + ' @param _UNSIGNED LONG ARRAY arr~&() to check in + ' @param _UNSIGNED LONG value~& value to check for + ' @return INTEGER -1 if found or 0 if not found + ' + FUNCTION ARR_ULONG.in%(arr~&(), value~&) + DIM AS LONG lb, ub, i + lb& = LBOUND(arr~&) : ub& = UBOUND(arr~&) + FOR i& = lb& TO ub& + IF arr~&(i&) = value~& THEN + ARR_ULONG.in% = -1 + EXIT FUNCTION + END IF + NEXT i& + ARR_ULONG.in% = 0 + END FUNCTION + + + '' + ' Checks if value exists in _UNSIGNED LONG array and returns index if found + ' + ' @param _UNSIGNED LONG ARRAY arr~&() to check in + ' @param _UNSIGNED LONG value~& value to check for + ' @return INTEGER index of element if found or -1 if not found + ' + FUNCTION ARR_ULONG.find%(arr~&(), value~&) + DIM AS LONG lb, ub, i + lb& = LBOUND(arr~&) : ub& = UBOUND(arr~&) + FOR i& = lb& TO ub& + IF arr~&(i&) = value~& THEN + ARR_ULONG.find% = i& + EXIT FUNCTION + END IF + NEXT i& + ARR_ULONG.find% = -1 + END FUNCTION + + + '' + ' Return the number of elements in a _UNSIGNED LONG array + ' + ' @param _UNSIGNED LONG ARRAY arr~&() to count + ' @return INTEGER number of elements + ' + FUNCTION ARR_ULONG.count&(arr~&()) + ARR_ULONG.count& = UBOUND(arr~&) - LBOUND(arr~&) + END FUNCTION + + + '' + ' Return the size of a _UNSIGNED LONG array + ' + ' @param _UNSIGNED LONG ARRAY arr~&() to get size of + ' @return LONG size in bytes + ' + FUNCTION ARR_ULONG.size&(arr~&()) + ARR_ULONG.size& = LEN(arr~&()) + END FUNCTION + + + '' + ' Reverses the elements of a _UNSIGNED LONG array + ' + ' @param _UNSIGNED LONG ARRAY source_arr~&() to reverse + ' @param _UNSIGNED LONG ARRAY dest_arr~&() to store reversed array in + ' + SUB ARR_ULONG.reverse(source_arr~&(), dest_arr~&()) + DIM AS LONG lb, ub, i, n + lb& = LBOUND(source_arr~&) : ub& = UBOUND(source_arr~&) + REDIM dest_arr(0 TO (ub& - lb&)) AS _UNSIGNED LONG + n& = 0 + FOR i& = ub& TO lb& STEP -1 + dest_arr~&(n&) = source_arr~&(i&) + n& = n& + 1 + NEXT i& + END SUB + + + '' + ' Returns a random ulong from a _UNSIGNED LONG array + ' + ' @param _UNSIGNED LONG ARRAY arr~&() array to get random element from + ' @return _UNSIGNED LONG random element + ' + FUNCTION ARR_ULONG.random~&(arr~&()) + DIM AS LONG lb, ub + lb& = LBOUND(arr~&) : ub& = UBOUND(arr~&) + RANDOMIZE TIMER + ARR_ULONG.random~& = arr~&(INT(RND * (ub& - lb&)) + 1) + END FUNCTION + + + '' + ' Returns the sum of all elements in a _UNSIGNED LONG array + ' + ' @param _UNSIGNED LONG ARRAY arr~&() array to get some for + ' @return LONG sum of all elements + ' + FUNCTION ARR_ULONG.sum&(arr~&()) + DIM AS LONG lb, ub, i + DIM sum AS LONG + lb& = LBOUND(arr~&) : ub& = UBOUND(arr~&) + FOR i& = lb& TO ub& + sum& = sum& + arr~&(i&) + NEXT i& + ARR_ULONG.sum& = sum& + END FUNCTION + + + '' + ' Returns the average value of elements in a _UNSIGNED LONG array + ' + ' @param _UNSIGNED LONG ARRAY arr~&() array to get average for + ' @return LONG average of elements + ' + FUNCTION ARR_ULONG.avg&(arr~&()) + DIM AS LONG lb, ub, i + DIM sum AS LONG + lb& = LBOUND(arr~&) : ub& = UBOUND(arr~&) + FOR i& = lb& TO ub& + sum& = sum& + arr~&(i&) + NEXT i& + ARR_ULONG.avg& = sum& / (ub& - lb&) + END FUNCTION + + + '' + ' Shuffle the elements of a _UNSIGNED LONG array + ' + ' @param _UNSIGNED LONG ARRAY source_arr~&() to shuffle + ' @param _UNSIGNED LONG ARRAY dest_arr~&() to store shuffled array in + ' + SUB ARR_ULONG.shuffle(source_arr~&(), dest_arr~&()) + DIM AS LONG lb, ub, i, count + lb& = LBOUND(source_arr~&) : ub& = UBOUND(source_arr~&) + count& = ub& - lb& + REDIM dest_arr(0 TO count&) AS _UNSIGNED LONG + CALL ARR_ULONG.copy(source_arr~&(), dest_arr~&()) + RANDOMIZE TIMER + FOR i& = 0 TO count& + SWAP dest_arr~&(i&), dest_arr~&(lb& + RND * (ub& - lb&)) + NEXT i& + END SUB + + + '' + ' Makes a _UNSIGNED LONG array contain only unique values + ' + ' @param _UNSIGNED LONG ARRAY source_arr~&() array to get uniques for + ' @param _UNSIGNED LONG ARRAY dest_arr~&() array to store uniques in + ' + SUB ARR_ULONG.unique(source_arr~&(), dest_arr~&()) + DIM AS LONG lb, ub, i + DIM tmp AS _UNSIGNED LONG + lb& = LBOUND(source_arr~&) : ub& = UBOUND(source_arr~&) + DIM work_arr(0) AS _UNSIGNED LONG + FOR i& = lb& TO ub& + IF NOT ARR_ULONG.in%(work_arr~&(), source_arr~&(i&)) THEN + CALL ARR_ULONG.push(work_arr~&(), source_arr~&(i&)) + END IF + NEXT i& + CALL ARR_ULONG.shift(work_arr~&(), tmp~&) + CALL ARR_ULONG.copy(work_arr~&(), dest_arr~&()) + END SUB + + + '' + ' Filters a _UNSIGNED LONG array to only elements greater than value + ' + ' @param _UNSIGNED LONG ARRAY source_arr~&() array to work on + ' @param _UNSIGNED LONG ARRAY dest_arr~&() array to store in + ' @param _UNSIGNED LONG value~& to be greater than to be returned + ' + SUB ARR_ULONG.gt(source_arr~&(), dest_arr~&(), value~&) + DIM AS LONG lb, ub, i + DIM tmp AS _UNSIGNED LONG + lb& = LBOUND(source_arr~&) : ub& = UBOUND(source_arr~&) + DIM work_arr(0) AS _UNSIGNED LONG + FOR i& = lb& TO ub& + IF source_arr~&(i&) > value~& THEN + CALL ARR_ULONG.push(work_arr~&(), source_arr~&(i&)) + END IF + NEXT i& + CALL ARR_ULONG.shift(work_arr~&(), tmp~&) + CALL ARR_ULONG.copy(work_arr~&(), dest_arr~&()) + END SUB + + + '' + ' Filters a _UNSIGNED LONG array to only elements greater than or equal to value + ' + ' @param _UNSIGNED LONG ARRAY source_arr~&() array to work on + ' @param _UNSIGNED LONG ARRAY dest_arr~&() array to store in + ' @param _UNSIGNED LONG value~& to be greater than or equal to be returned + ' + SUB ARR_ULONG.gte(source_arr~&(), dest_arr~&(), value~&) + DIM AS LONG lb, ub, i + DIM tmp AS _UNSIGNED LONG + lb& = LBOUND(source_arr~&) : ub& = UBOUND(source_arr~&) + DIM work_arr(0) AS _UNSIGNED LONG + FOR i& = lb& TO ub& + IF source_arr~&(i&) >= value~& THEN + CALL ARR_ULONG.push(work_arr~&(), source_arr~&(i&)) + END IF + NEXT i& + CALL ARR_ULONG.shift(work_arr~&(), tmp~&) + CALL ARR_ULONG.copy(work_arr~&(), dest_arr~&()) + END SUB + + + '' + ' Filters a _UNSIGNED LONG array to only elements less than value + ' + ' @param _UNSIGNED LONG ARRAY source_arr~&() array to work on + ' @param _UNSIGNED LONG ARRAY dest_arr~&() array to store in + ' @param _UNSIGNED LONG value~& to be less than to be returned + ' + SUB ARR_ULONG.lt(source_arr~&(), dest_arr~&(), value~&) + DIM AS LONG lb, ub, i + DIM tmp AS _UNSIGNED LONG + lb& = LBOUND(source_arr~&) : ub& = UBOUND(source_arr~&) + DIM work_arr(0) AS _UNSIGNED LONG + FOR i& = lb& TO ub& + IF source_arr~&(i&) < value~& THEN + CALL ARR_ULONG.push(work_arr~&(), source_arr~&(i&)) + END IF + NEXT i& + CALL ARR_ULONG.shift(work_arr~&(), tmp~&) + CALL ARR_ULONG.copy(work_arr~&(), dest_arr~&()) + END SUB + + + '' + ' Filters a _UNSIGNED LONG array to only elements less than or equal to value + ' + ' @param _UNSIGNED LONG ARRAY source_arr~&() array to work on + ' @param _UNSIGNED LONG ARRAY dest_arr~&() array to store in + ' @param _UNSIGNED LONG value~& to be less than or equal to be returned + ' + SUB ARR_ULONG.lte(source_arr~&(), dest_arr~&(), value~&) + DIM AS LONG lb, ub, i + DIM tmp AS _UNSIGNED LONG + lb& = LBOUND(source_arr~&) : ub& = UBOUND(source_arr~&) + DIM work_arr(0) AS _UNSIGNED LONG + FOR i& = lb& TO ub& + IF source_arr~&(i&) <= value~& THEN + CALL ARR_ULONG.push(work_arr~&(), source_arr~&(i&)) + END IF + NEXT i& + CALL ARR_ULONG.shift(work_arr~&(), tmp~&) + CALL ARR_ULONG.copy(work_arr~&(), dest_arr~&()) + END SUB + + + '' + ' Finds and replaces values across all elements in a _UNSIGNED LONG ARRAY + ' + ' @param _UNSIGNED LONG ARRAY arr~&() to check in + ' @param _UNSIGNED LONG find~& value to find + ' @param _UNSIGNED LONG replace~& value to replace with if found + ' + SUB ARR_ULONG.replace(arr~&(), find~&, replace~&) + DIM AS LONG lb, ub, i + lb& = LBOUND(arr~&) : ub& = UBOUND(arr~&) + FOR i& = lb& TO ub& + IF arr~&(i&) = find~& THEN + arr~&(i&) = replace~& + END IF + NEXT i& + END SUB + + + '' + ' Inserts a new element into _UNSIGNED LONG array after index + ' + ' @param _UNSIGNED LONG ARRAY arr~&() array to work on + ' @param _UNSIGNED LONG value~& to insert + ' @param INTEGER index% of element to insert at + ' + SUB ARR_ULONG.insert(arr~&(), value~&, index%) + DIM AS LONG lb, ub, i + DIM tmp AS _UNSIGNED LONG + DIM work_arr(0) AS _UNSIGNED LONG + lb& = LBOUND(arr~&) : ub& = UBOUND(arr~&) + IF index% >= lb& AND index% <= ub& THEN + ' build new array up to index + FOR i& = lb& TO index% + CALL ARR_ULONG.push(work_arr~&(), arr~&(i&)) + NEXT i& + ' insert new element + CALL ARR_ULONG.push(work_arr~&(), value~&) + ' finish building array from index + 1 + FOR i& = index% + 1 TO ub& + CALL ARR_ULONG.push(work_arr~&(), arr~&(i&)) + NEXT i& + CALL ARR_ULONG.shift(work_arr~&(), tmp~&) + CALL ARR_ULONG.copy(work_arr~&(), arr~&()) + END IF + END SUB + + + '' + ' Removes element from a _UNSIGNED LONG array by element index + ' + ' @param _UNSIGNED LONG ARRAY arr~&() array to work on + ' @param INTEGER index% of element to remove + ' + SUB ARR_ULONG.remove(arr~&(), index%) + DIM AS LONG lb, ub, i + DIM tmp AS _UNSIGNED LONG + DIM work_arr(0) AS _UNSIGNED LONG + lb& = LBOUND(arr~&) : ub& = UBOUND(arr~&) + IF index% >= lb& AND index% <= ub& THEN + ' build new array up to index + FOR i& = lb& TO index% - 1 + CALL ARR_ULONG.push(work_arr~&(), arr~&(i&)) + NEXT i& + ' skip elements + FOR i& = index% + 1 TO ub& + CALL ARR_ULONG.push(work_arr~&(), arr~&(i&)) + NEXT i& + CALL ARR_ULONG.shift(work_arr~&(), tmp~&) + CALL ARR_ULONG.copy(work_arr~&(), arr~&()) + END IF + END SUB + + + '' + ' Filters a _UNSIGNED LONG array to only elements that have odd values + ' + ' @param _UNSIGNED LONG ARRAY source_arr~&() array to work on + ' @param _UNSIGNED LONG ARRAY dest_arr~&() array to store in + ' + SUB ARR_ULONG.odd(source_arr~&(), dest_arr~&()) + DIM AS LONG lb, ub, i + DIM tmp AS _UNSIGNED LONG + lb& = LBOUND(source_arr~&) : ub& = UBOUND(source_arr~&) + DIM work_arr(0) AS _UNSIGNED LONG + FOR i& = lb& TO ub& + IF source_arr~&(i&) MOD 2 <> 0 THEN + CALL ARR_ULONG.push(work_arr~&(), source_arr~&(i&)) + END IF + NEXT i& + CALL ARR_ULONG.shift(work_arr~&(), tmp~&) + CALL ARR_ULONG.copy(work_arr~&(), dest_arr~&()) + END SUB + + + '' + ' Filters a _UNSIGNED LONG array to only elements that have even values + ' + ' @param _UNSIGNED LONG ARRAY source_arr~&() array to work on + ' @param _UNSIGNED LONG ARRAY dest_arr~&() array to store in + ' + SUB ARR_ULONG.even(source_arr~&(), dest_arr~&()) + DIM AS LONG lb, ub, i + DIM tmp AS _UNSIGNED LONG + lb& = LBOUND(source_arr~&) : ub& = UBOUND(source_arr~&) + DIM work_arr(0) AS _UNSIGNED LONG + FOR i& = lb& TO ub& + IF source_arr~&(i&) MOD 2 = 0 THEN + CALL ARR_ULONG.push(work_arr~&(), source_arr~&(i&)) + END IF + NEXT i& + CALL ARR_ULONG.shift(work_arr~&(), tmp~&) + CALL ARR_ULONG.copy(work_arr~&(), dest_arr~&()) + END SUB + + + '' + ' Filters a _UNSIGNED LONG array to only elements that have values evenly divisible by divisor + ' + ' @param _UNSIGNED LONG ARRAY source_arr~&() array to work on + ' @param _UNSIGNED LONG ARRAY dest_arr~&() array to store in + ' @param _UNSIGNED LONG divisor~& for modulo + ' + SUB ARR_ULONG.mod(source_arr~&(), dest_arr~&(), divisor~&) + DIM AS LONG lb, ub, i + DIM tmp AS _UNSIGNED LONG + lb& = LBOUND(source_arr~&) : ub& = UBOUND(source_arr~&) + DIM work_arr(0) AS _UNSIGNED LONG + FOR i& = lb& TO ub& + IF source_arr~&(i&) MOD divisor~& = 0 THEN + CALL ARR_ULONG.push(work_arr~&(), source_arr~&(i&)) + END IF + NEXT i& + CALL ARR_ULONG.shift(work_arr~&(), tmp~&) + CALL ARR_ULONG.copy(work_arr~&(), dest_arr~&()) + END SUB + + + '' + ' Filters a _UNSIGNED LONG array to only elements between min and max + ' + ' @param _UNSIGNED LONG ARRAY source_arr~&() array to work on + ' @param _UNSIGNED LONG ARRAY dest_arr~&() array to store in + ' @param _UNSIGNED LONG min~& to be greater than or equal to be returned + ' @param _UNSIGNED LONG max~& to be less than or equal to be returned + ' + SUB ARR_ULONG.between(source_arr~&(), dest_arr~&(), min~&, max~&) + DIM AS LONG lb, ub, i + DIM tmp AS _UNSIGNED LONG + lb& = LBOUND(source_arr~&) : ub& = UBOUND(source_arr~&) + DIM work_arr(0) AS _UNSIGNED LONG + FOR i& = lb& TO ub& + IF source_arr~&(i&) >= min~& _ + AND source_arr~&(i&) <= max~& THEN + CALL ARR_ULONG.push(work_arr~&(), source_arr~&(i&)) + END IF + NEXT i& + CALL ARR_ULONG.shift(work_arr~&(), tmp~&) + CALL ARR_ULONG.copy(work_arr~&(), dest_arr~&()) + END SUB + + + '' + ' Sorts _UNSIGNED LONG array in ascending order + ' + ' @param _UNSIGNED LONG ARRAY source_arr~&() array to sort + ' @param _UNSIGNED LONG ARRAY dest_arr~&() array to store sorted in + ' + SUB ARR_ULONG.sort(source_arr~&(), dest_arr~&()) + DIM AS LONG lb, ub, count + lb& = LBOUND(source_arr~&) : ub& = UBOUND(source_arr~&) + count& = ub& - lb& + REDIM dest_arr(0 TO count&) AS _UNSIGNED LONG + CALL ARR_ULONG.copy(source_arr~&(), dest_arr~&()) + CALL ARR_ULONG.quicksort(dest_arr~&(), lb&, ub&, 0) + END SUB + + + '' + ' Sorts _UNSIGNED LONG array in descending order + ' + ' @param _UNSIGNED LONG ARRAY source_arr~&() array to sort + ' @param _UNSIGNED LONG ARRAY dest_arr~&() array to store sorted in + ' + SUB ARR_ULONG.rsort(source_arr~&(), dest_arr~&()) + DIM AS LONG lb, ub, count + lb& = LBOUND(source_arr~&) : ub& = UBOUND(source_arr~&) + count& = ub& - lb& + REDIM dest_arr(0 TO count&) AS _UNSIGNED LONG + CALL ARR_ULONG.copy(source_arr~&(), dest_arr~&()) + CALL ARR_ULONG.quicksort(dest_arr~&(), lb&, ub&, 1) + END SUB + + + '' + ' Quicksort array with pivot algorithm by logiclrd + ' + ' @link https://www.tek-tips.com/faqs.cfm?fid=336 + ' @param _UNSIGNED LONG ARRAY array~&() to sort + ' @param INTEGER start% of range to sort + ' @param INTEGER finish% range of sort + ' @param INTEGER order% to sort by (0 = asc / 1 = desc) + ' + SUB ARR_ULONG.quicksort(arr~&(), start%, finish%, order%) + DIM i AS LONG + DIM pivot AS INTEGER + DIM pivotvalue AS _UNSIGNED LONG + + 'first, partition the array + pivot% = start% + pivotvalue~& = arr~&(pivot%) + FOR i& = start% + 1 TO finish% + IF order% = 0 THEN ' ascending order + IF arr~&(i&) < pivotvalue~& THEN + arr~&(pivot%) = arr~&(i&) + arr~&(i&) = arr~&(pivot% + 1) + arr~&(pivot% + 1) = pivotvalue~& + pivot% = pivot% + 1 + END IF + ELSEIF order% = 1 THEN ' descending order + IF arr~&(i&) > pivotvalue~& THEN + arr~&(pivot%) = arr~&(i&) + arr~&(i&) = arr~&(pivot% + 1) + arr~&(pivot% + 1) = pivotvalue~& + pivot% = pivot% + 1 + END IF + END IF + NEXT i& + + 'then, sort the subarrays to each side of the pivot + IF pivot% - start% >= 2 THEN + CALL ARR_ULONG.quicksort(arr~&(), start%, pivot% - 1, order%) + END IF + IF finish% - pivot% >= 2 THEN + CALL ARR_ULONG.quicksort(arr~&(), pivot% + 1, finish%, order%) + END IF + END SUB + $END IF + $IF GJ_LIB_DICT_INC_BM = UNDEFINED THEN + '' + ' QB64_GJ_LIB + ' GRYMMJACK'S DICTIONARY Object (part of _GJ_LIB) + ' + ' Simulates a dictionary object as found in other languages. + ' + ' USAGE FOR Dict Object alone: + ' Insert '$INCLUDE:'path_to_GJ_LIB/DICT/DICT.BI' at the top of file + ' Insert '$INCLUDE:'path_to_GJ_LIB/DICT/DICT.BM' at the bottom of file + ' + ' @author Rick Christy + ' @uses DICT.BI + ' + $LET GJ_LIB_DICT_INC_BM = 1 + $IF GJ_LIB_DICT_INC_BI = UNDEFINED THEN + '' + ' QB64_GJ_LIB + ' GRYMMJACK'S DICT Object + ' + ' Simulates a dictionary object as found in other languages. + ' + ' USAGE: + ' Insert '$INCLUDE:'path_to_GJ_LIB/DICT/DICT.BI' at the top of file + ' Insert '$INCLUDE:'path_to_GJ_LIB/DICT/DICT.BM' at the bottom of file + ' + ' @author Rick Christy + ' @uses DICT.BM + ' + $LET GJ_LIB_DICT_INC_BI = 1 + + + + ' DICTIONARY type consists of keys and values and is intended for array use + TYPE DICTIONARY + key AS STRING + val AS STRING + END TYPE + $END IF + + + '' + ' Populates a dictionary with arrays of keys and values + ' + ' @param DICTIONARY d() object to populate + ' @param STRING ARRAY keys$() keys to use for dict keys + ' @param STRING ARRAY vals$() vals to use for dict vals + ' @return Nothing, but the d() passed is populated by keys and values + ' + SUB DICT.populate(d() AS DICTIONARY, keys$(), vals$()) + DIM AS INTEGER uk, uv, i + uk% = UBOUND(keys$) : uv% = UBOUND(vals$) + IF uk% <> uv% THEN EXIT SUB + FOR i% = 0 TO uk% + d(i%).key$ = keys$(i) + d(i%).val$ = vals$(i) + NEXT i + END SUB + + + '' + ' Fills a dictionary with serialized keys and values + ' + ' @param DICTIONARY d() object to fill + ' @return Nothing, but the d() passed in is filled + ' + SUB DICT.fill(d() AS DICTIONARY) + DIM AS INTEGER ub, lb, i + ub% = UBOUND(d) : lb% = LBOUND(d) + FOR i% = lb% TO ub% + d(i%).key$ = "key" + _TRIM$(STR$(i%)) + d(i%).val$ = _TRIM$(STR$(i%)) + NEXT i + END SUB + + + '' + ' Gets a dictionary array index by key + ' + ' @param DICTIONARY d() to look in + ' @param STRING ARRAY key$ to find the index for + ' @return INTEGER array index if found or 0 if not found + ' + FUNCTION DICT.get_index_by_key%(d() AS DICTIONARY, key$) + DIM AS INTEGER ub, lb, i + ub% = UBOUND(d) : lb% = LBOUND(d) + FOR i% = lb% TO ub% + IF d(i%).key$ = key$ THEN + DICT.get_index_by_key% = i% + EXIT FUNCTION + END IF + NEXT i% + DICT.get_index_by_key% = 0 + END FUNCTION + + + '' + ' Gets a dictionary items key by its array index + ' + ' @param DICTIONARY d() to look in + ' @param INTEGER index% to lookup the key for + ' @return STRING key of item at index + ' + FUNCTION DICT.get_key_by_index$(d() AS DICTIONARY, index%) + DIM AS INTEGER ub, lb + ub% = UBOUND(d) : lb% = LBOUND(d) + IF index% >= lb% AND index% <= ub% THEN + DICT.get_key_by_index$ = d(index%).key$ + END IF + END FUNCTION + + + '' + ' Gets a dictionary items value by its array index + ' + ' @param DICTIONARY d() to look in + ' @param INTEGER index% to lookup the value for + ' @return STRING value of item at index + ' + FUNCTION DICT.get_val_by_index$(d() AS DICTIONARY, index%) + DIM AS INTEGER ub, lb + ub% = UBOUND(d) : lb% = LBOUND(d) + IF index% >= lb% AND index% <= ub% THEN + DICT.get_val_by_index$ = d(index%).val$ + END IF + END FUNCTION + + + '' + ' Gets a dictionary items value by its key + ' + ' @param DICTIONARY d() to look in + ' @param STRING key$ to get the value for + ' @return STRING value of dictionary item by key + ' + FUNCTION DICT.get_val_by_key$(d() AS DICTIONARY, key$) + DIM AS INTEGER ub, lb, i + ub% = UBOUND(d) : lb% = LBOUND(d) + FOR i% = lb% TO ub% + IF d(i%).key$ = key$ THEN + DICT.get_val_by_key$ = d(i%).val$ + EXIT FUNCTION + END IF + NEXT i% + END FUNCTION + + + '' + ' Get all dictionary object keys as an array of strings + ' + ' @param DICTIONARY d() to look in + ' @param STRING ARRAY keys$() to store dict object keys into + ' @return Nothing, but the keys$() array is populated + ' + SUB DICT.get_keys(d() AS DICTIONARY, keys$()) + DIM AS INTEGER ub, lb, i, c + ub = UBOUND(d) : lb = LBOUND(d) : c% = ub% - lb% + REDIM keys$(c%) + FOR i% = lb% TO ub% + keys$(i%) = d(i%).key$ + NEXT i% + END SUB + + + '' + ' Get all dictionary object values as an array of strings + ' + ' @param DICTIONARY d() to look in + ' @param STRING ARRAY vals$() to store dict object vals into + ' @return Nothing, but the vals$() array is populated + ' + SUB DICT.get_vals(d() AS DICTIONARY, vals$()) + DIM AS INTEGER ub, lb, i, c + ub = UBOUND(d) : lb = LBOUND(d) : c% = ub% - lb% + REDIM vals$(c%) + FOR i% = lb% TO ub% + vals$(i%) = d(i%).val$ + NEXT i% + END SUB + + + '' + ' Swaps a dictionary objects keys for its values + ' + ' @param DICTIONARY d() to operate on + ' @return Nothing, but the dict() passed in is operated on directly + ' + SUB DICT.swap_keys_for_vals(d() AS DICTIONARY) + DIM AS INTEGER ub, lb, i, c + ub = UBOUND(d) : lb = LBOUND(d) : c% = ub% - lb% + DIM res(c%) AS DICTIONARY + FOR i% = lb% TO ub% + res(i%).key$ = d(i%).val$ + res(i%).val$ = d(i%).key$ + SWAP d(i%).key$, res(i%).key$ + SWAP d(i%).val$, res(i%).val$ + NEXT i% + END SUB + $END IF + $IF GJ_LIB_DUMP_INC_BM = UNDEFINED THEN + '' + ' QB64_GJ_LIB + ' GRYMMJACK'S DUMP LIB + ' + ' Dumps variables in a human friendly way to assist in debugging. + ' Inspired by PHP print_r() [1] which I missed when writing in QB64. + ' + ' So, why "dump"? + ' + ' [0] dump: + ' to copy (data in a computer's internal storage) to an external storage + ' or output device + ' ^^^^^^^^^^^^^ + ' this + ' + ' USAGE: + ' Insert '$INCLUDE:'path_to_GJ_LIB/DUMP/DUMP.BI' at the top of file + ' Insert '$INCLUDE:'path_to_GJ_LIB/DUMP/DUMP.BM' at the bottom of file + ' + ' TL;DR: + ' + ' Every function returns a string called a dump block. The idea is that + ' You can then PRINT the string in your code where you need to see what vars + ' have inside them quickly. + ' + ' A dump block consists of: + ' - A blank line + ' - The type of variable being dumped + ' - The number of elements in an array (if applicable) + ' - A label for reference + ' + ' Here is an example of a dump of a string array: + ' + ' DIM words$(2) + ' words$(0) = "foo" : words$(1) = "bar" : words$(2) = "baz" + ' PRINT DUMP.string_array(words$(), "words") + ' + ' OUTPUT: + ' + ' STRING ARRAY: words$(2) { + ' (0): "foo" [3] + ' (1): "bar" [3] + ' (2): "baz" [3] + ' } + ' + ' Because QB64 lacks any reflection support, has no way to pass an optional + ' argument, has no ability to identify a variable type, or even eval() it's + ' own dialect, there are separate functions for each common type. If you don't + ' see the one you need, it's easy enough to copy an existing one and make what + ' you want while staying in the spirit of DUMP LIB. + ' + ' FUNCTION NOTES + ' DUMP.bit_array$ Returns string with a dump of an array of bits + ' DUMP.unsigned_bit_array$ Returns string with a dump of an array of unsigned bits + ' DUMP.byte_array$ Returns string with a dump of an array of bytes + ' DUMP.unsigned_byte_array$ Returns string with a dump of an array of unsigned bytes + ' DUMP.unsigned_integer$ Returns string with a dump of an array of unsigned integers + ' DUMP.unsigned_byte_array_as_hex$ Returns string with a dump of an array of unsigned bytes as hex + ' DUMP.unsigned_byte_array_as_ascii$ Returns string with a dump of an array of unsigned bytes as hex and ASCII + ' DUMP.string$ Includes handy output of the strings length. + ' DUMP.string_array$ Works on 1 dimensional arrays only (right now). + ' DUMP.integer_array$ Works on 1 dimensional arrays only (right now). + ' DUMP.single_array$ Works on 1 dimensional arrays only (right now). + ' DUMP.long_array$ Works on 1 dimensional arrays only (right now). + ' DUMP.double_array$ Works on 1 dimensional arrays only (right now). + ' DUMP.dict$ Dump a dictionary object and it's contents. + ' + ' @author Rick Christy + ' @depends DUMP.BI + ' @see [0] https://www.merriam-webster.com/dictionary/dump + ' @see [1] https://www.php.net/manual/en/function.print-r.php + ' + $LET GJ_LIB_DUMP_INC_BM = 1 + + + + '' + ' Returns string with a dump of a string + ' + ' @param STRING s$ to dump + ' @param STRING label$ to give the dump block + ' @return STRING dump block + ' + FUNCTION DUMP.string$(s$, label$) + DIM AS STRING l, r + l$ = _TRIM$(STR$(LEN(s$))) + r$ = GJ_LIB_NL$ + "STRING: " + label$ + "$ {" + GJ_LIB_NL$ + r$ = r$ + " " + CHR$(34) + s$ + CHR$(34) + " [" + l$ + "]" + GJ_LIB_NL$ + r$ = r$ + "} " + DUMP.string$ = r$ + END FUNCTION + + + '' + ' Returns string with a dump of an array of bits + ' + ' @param _BIT ARRAY arr`() of bits to dump + ' @param STRING label$ to give the dump block + ' @return STRING dump block + ' + FUNCTION DUMP.bit_array$(arr`(), label$) + DIM AS INTEGER lb, ub, i + DIM AS STRING t, r + lb% = LBOUND(arr`) : ub% = UBOUND(arr`) : t$ = _TRIM$(STR$(ub% - lb%)) + r$ = GJ_LIB_NL$ + "STRING ARRAY: " + label$ + "`(" + t$ + ") {" + GJ_LIB_NL$ + FOR i% = lb% TO ub% + r$ = r$ + " (" + _TRIM$(STR$(i%)) + "): " + STR$(arr`(i%)) + r$ = r$ + " [" + t$ + "]" + GJ_LIB_NL$ + NEXT i% + r$ = r$ + "}" + DUMP.bit_array$ = r$ + END FUNCTION + + + '' + ' Returns string with a dump of an array of unsigned bits + ' + ' @param _UNSIGNED _BIT arr~`() of unsigned bits to dump + ' @param STRING label$ to give the dump block + ' @return STRING dump block + ' + FUNCTION DUMP.unsigned_bit_array$(arr~`(), label$) + DIM AS INTEGER lb, ub, i + DIM AS STRING t, r + lb% = LBOUND(arr~`) : ub% = UBOUND(arr~`) : t$ = _TRIM$(STR$(ub% - lb%)) + r$ = GJ_LIB_NL$ + "STRING ARRAY: " + label$ + "~`(" + t$ + ") {" + GJ_LIB_NL$ + FOR i% = lb% TO ub% + r$ = r$ + " (" + _TRIM$(STR$(i%)) + "): " + STR$(arr~`(i%)) + r$ = r$ + " [" + t$ + "]" + GJ_LIB_NL$ + NEXT i% + r$ = r$ + "}" + DUMP.unsigned_bit_array$ = r$ + END FUNCTION + + + '' + ' Returns string with a dump of an array of strings + ' + ' @param STRING ARRAY arr$() of strings to dump + ' @param STRING label$ to give the dump block + ' @return STRING dump block + ' + FUNCTION DUMP.string_array$(arr$(), label$) + DIM AS INTEGER lb, ub, i + DIM AS STRING t, r + lb% = LBOUND(arr$) : ub% = UBOUND(arr$) : t$ = _TRIM$(STR$(ub% - lb%)) + r$ = GJ_LIB_NL$ + "STRING ARRAY: " + label$ + "$(" + t$ + ") {" + GJ_LIB_NL$ + FOR i% = lb% TO ub% + r$ = r$ + " (" + _TRIM$(STR$(i%)) + "): " + CHR$(34) + arr$(i%) + CHR$(34) + r$ = r$ + " [" + t$ + "]" + GJ_LIB_NL$ + NEXT i% + r$ = r$ + "}" + DUMP.string_array$ = r$ + END FUNCTION + + + '' + ' Returns string with a dump of an array of integers + ' + ' @param INTEGER ARRAY arr%() of integers to dump + ' @param STRING label$ to give the dump block + ' @return STRING dump block + ' + FUNCTION DUMP.integer_array$(arr%(), label$) + DIM AS INTEGER lb, ub, i + DIM AS STRING t, r, num + lb% = LBOUND(arr%) : ub% = UBOUND(arr%) : t$ = _TRIM$(STR$(ub% - lb%)) + r$ = GJ_LIB_NL$ + "INTEGER ARRAY: " + label$ + "%(" + t$ + ") {" + GJ_LIB_NL$ + FOR i% = lb% TO ub% + num$ = _TRIM$(STR$(arr%(i%))) + r$ = r$ + " (" + _TRIM$(STR$(i%)) + "): " + num$ + GJ_LIB_NL$ + NEXT i% + r$ = r$ + "}" + DUMP.integer_array$ = r$ + END FUNCTION + + + '' + ' Returns string with a dump of an array of unsigned integers + ' + ' @param _UNSIGNED INTEGER arr%() of unsigned integers to dump + ' @param STRING label$ to give the dump block + ' @return STRING dump block + ' + FUNCTION DUMP.unsigned_integer_array$(arr~%(), label$) + DIM AS INTEGER lb, ub, i + DIM AS STRING r, t, num + lb% = LBOUND(arr~%) : ub% = UBOUND(arr~%) : t$ = _TRIM$(STR$(ub% - lb%)) + r$ = GJ_LIB_NL$ + "INTEGER ARRAY: " + label$ + "~%(" + t$ + ") {" + GJ_LIB_NL$ + FOR i% = lb% TO ub% + num$ = _TRIM$(STR$(arr~%(i%))) + r$ = r$ + " (" + _TRIM$(STR$(i%)) + "): " + num$ + GJ_LIB_NL$ + NEXT i% + r$ = r$ + "}" + DUMP.unsigned_integer_array$ = r$ + END FUNCTION + + + '' + ' Returns a string containing a dump of an array of singles + ' + ' @param SINGLE ARRAY arr!() of singles to dump + ' @param STRING label$ to give the dump block + ' @return STRING dump block + ' + FUNCTION DUMP.single_array$(arr!(), label$) + DIM AS INTEGER lb, ub, i + DIM AS STRING r, t, num + lb% = LBOUND(arr!) : ub% = UBOUND(arr!) : t$ = _TRIM$(STR$(ub% - lb%)) + r$ = GJ_LIB_NL$ + "SINGLE ARRAY: " + label$ + "!(" + t$ + ") {" + GJ_LIB_NL$ + FOR i% = lb% TO ub% + num$ = _TRIM$(STR$(arr!(i%))) + r$ = r$ + " (" + _TRIM$(STR$(i%)) + "): " + num$ + GJ_LIB_NL$ + NEXT i% + r$ = r$ + "}" + DUMP.single_array$ = r$ + END FUNCTION + + + '' + ' Dumps an array of longs + ' + ' @param LONG ARRAY arr&() of longs to dump + ' @param STRING label$ to give the dump block + ' @return STRING dump block + ' + FUNCTION DUMP.long_array$(arr&(), label$) + DIM AS INTEGER lb, ub, i + DIM AS STRING r, t, num + lb% = LBOUND(arr&) : ub% = UBOUND(arr&) : t$ = _TRIM$(STR$(ub% - lb%)) + r$ = GJ_LIB_NL$ + "LONG ARRAY: " + label$ + "&(" + t$ + ") {" + GJ_LIB_NL$ + FOR i% = lb% TO ub% + num$ = _TRIM$(STR$(arr&(i%))) + r$ = r$ + " (" + _TRIM$(STR$(i%)) + "): " + num$ + GJ_LIB_NL$ + NEXT i% + r$ = r$ + "}" + DUMP.long_array$ = r$ + END FUNCTION + + + '' + ' Dumps an array of doubles + ' + ' @param DOUBLE ARRAY arr#() of doubles to dump + ' @param STRING label$ to give the dump block + ' @return STRING dump block + ' + FUNCTION DUMP.double_array$(arr#(), label$) + DIM AS INTEGER lb, ub, i + DIM AS STRING r, t, num + lb% = LBOUND(arr#) : ub% = UBOUND(arr#) : t$ = _TRIM$(STR$(ub% - lb%)) + r$ = GJ_LIB_NL$ + "DOUBLE ARRAY: " + label$ + "#(" + t$ + ") {" + GJ_LIB_NL$ + FOR i% = lb% TO ub% + num$ = _TRIM$(STR$(arr#(i%))) + r$ = r$ + " (" + _TRIM$(STR$(i%)) + "): " + num$ + GJ_LIB_NL$ + NEXT i% + r$ = r$ + "}" + DUMP.double_array$ = r$ + END FUNCTION + + + '' + ' Returns string with a dump of an array of bytes + ' + ' @param _BYTE ARRAY arr%%() of bytes to dump + ' @param STRING label$ to give the dump block + ' @return STRING dump block + ' + FUNCTION DUMP.byte_array$(arr%%(), label$) + DIM AS INTEGER lb, ub, i + DIM AS STRING r, t, num, si + lb% = LBOUND(arr%%) : ub% = UBOUND(arr%%) : t$ = _TRIM$(STR$(ub% - lb%)) + r$ = GJ_LIB_NL$ + "BYTE ARRAY: " + label$ + "%%(" + t$ + ") {" + FOR i% = lb% TO ub% + si$ = _TRIM$(STR$(i%)) + IF LEN(si$) = 1 THEN si$ = "0" + si$ + num$ = _TRIM$(STR$(arr%%(i%))) + IF SGN(arr%%(i%)) = 1 THEN + IF LEN(num$) = 2 THEN + num$ = " 0" + num$ + ELSEIF LEN(num$) = 1 THEN + num$ = " 00" + num$ + END IF + ELSEIF SGN(arr%%(i%)) = 0 THEN + num$ = " 000" + ELSE + IF LEN(num$) = 3 THEN + num$ = "-0" + MID$(num$, 2, 2) + ELSEIF LEN(num$) = 2 THEN + num$ = "-00" + MID$(num$, 2, 1) + END IF + END IF + IF i% MOD 8 = 0 THEN + r$ = r$ + GJ_LIB_NL$ + r$ = r$ + STRING$(4, " ") + si$ + ": " + END IF + r$ = r$ + num$ + " " + NEXT i% + r$ = r$ + GJ_LIB_NL$ + "}" + DUMP.byte_array$ = r$ + END FUNCTION + + + '' + ' Returns string with a dump of an array of unsigned bytes + ' + ' @param _UNSIGNED _BYTE ARRAY arr~%%() of unsigned bytes to dump + ' @param STRING label$ to give the dump block + ' @return STRING dump block + ' + FUNCTION DUMP.unsigned_byte_array$(arr~%%(), label$) + DIM AS INTEGER lb, ub, i + DIM AS STRING r, t, num, si + lb% = LBOUND(arr~%%) : ub% = UBOUND(arr~%%) : t$ = _TRIM$(STR$(ub% - lb%)) + r$ = GJ_LIB_NL$ + "UNSIGNED BYTE ARRAY: " + label$ + "~%%(" + t$ + ") {" + FOR i% = lb% TO ub% + si$ = _TRIM$(STR$(i%)) + IF LEN(si$) = 1 THEN si$ = "0" + si$ + num$ = _TRIM$(STR$(arr~%%(i%))) + IF LEN(num$) = 2 THEN + num$ = "0" + num$ + ELSEIF LEN(num$) = 1 THEN + num$ = "00" + num$ + END IF + IF i% MOD 16 = 0 THEN + r$ = r$ + GJ_LIB_NL$ + r$ = r$ + STRING$(4, " ") + si$ + ": " + END IF + r$ = r$ + num$ + " " + NEXT i% + r$ = r$ + GJ_LIB_NL$ + "}" + DUMP.unsigned_byte_array$ = r$ + END FUNCTION + + + '' + ' Returns string with a dump of an array of unsigned bytes as hex + ' + ' @param _UNSIGNED _BYTE ARRAY arr~%%() of unsigned bytes to dump + ' @param STRING label$ to give the dump block + ' @return STRING dump block + ' + FUNCTION DUMP.unsigned_byte_array_as_hex$(arr~%%(), label$) + DIM AS INTEGER lb, ub, i + DIM AS STRING r, t, num, si, h + lb% = LBOUND(arr~%%) : ub% = UBOUND(arr~%%) : t$ = _TRIM$(STR$(ub% - lb%)) + r$ = GJ_LIB_NL$ + "UNSIGNED BYTE ARRAY: " + label$ + "~%%(" + t$ + ") {" + FOR i% = lb% TO ub% + si$ = _TRIM$(STR$(i%)) + IF LEN(si$) = 1 THEN si$ = "0" + si$ + h$ = HEX$(arr~%%(i%)) + IF LEN(h$) = 1 THEN h$ = "0" + h$ + num$ = _TRIM$(STR$(arr~%%(i%))) + IF i% MOD 16 = 0 THEN + r$ = r$ + GJ_LIB_NL$ + r$ = r$ + STRING$(4, " ") + si$ + ": " + END IF + r$ = r$ + h$ + " " + NEXT i% + r$ = r$ + GJ_LIB_NL$ + "}" + DUMP.unsigned_byte_array_as_hex$ = r$ + END FUNCTION + + + '' + ' Returns string with a dump of an array of unsigned bytes as ascii + ' + ' @param _UNSIGNED _BYTE ARRAY arr~%%() of unsigned bytes to dump + ' @param STRING label$ to give the dump block + ' @return STRING dump block + ' + FUNCTION DUMP.unsigned_byte_array_as_ascii$(arr~%%(), label$) + DIM AS INTEGER lb, ub, i + DIM AS STRING r, t, num, si, h, c + lb% = LBOUND(arr~%%) : ub% = UBOUND(arr~%%) : t$ = _TRIM$(STR$(ub% - lb%)) + r$ = GJ_LIB_NL$ + "UNSIGNED BYTE ARRAY: " + label$ + "~%%(" + t$ + ") {" + FOR i% = lb% TO ub% + si$ = _TRIM$(STR$(i%)) + DO WHILE LEN(si$) < LEN(STR$(ub%))-1 + si$ = "0" + si$ + LOOP + h$ = HEX$(arr~%%(i%)) + IF LEN(h$) = 1 THEN h$ = "0" + h$ + num$ = _TRIM$(STR$(arr~%%(i%))) + IF i% MOD 16 = 0 THEN + r$ = r$ + GJ_LIB_NL$ + r$ = r$ + STRING$(4, " ") + si$ + ": " + END IF + r$ = r$ + h$ + " " + NEXT i% + r$ = r$ + GJ_LIB_NL$ + "ASCII:" + FOR i% = lb% TO ub% + si$ = _TRIM$(STR$(i%)) + DO WHILE LEN(si$) < LEN(STR$(ub%))-1 + si$ = "0" + si$ + LOOP + IF arr~%%(i%) < 33 OR arr~%%(i%) > 254 THEN + c$ = ".." + ELSE + c$ = CHR$(arr~%%(i%)) + " " + END IF + IF i% MOD 16 = 0 THEN + r$ = r$ + GJ_LIB_NL$ + r$ = r$ + STRING$(4, " ") + si$ + ": " + END IF + r$ = r$ + c$ + " " + NEXT i% + r$ = r$ + GJ_LIB_NL$ + "}" + DUMP.unsigned_byte_array_as_ascii$ = r$ + END FUNCTION + + + '' + ' Dumps a dictionary object and its contents + ' + ' @param DICTIONARY d() object to dump + ' @param STRING label$ to give the dump block + ' @return STRING dump block + ' + FUNCTION DUMP.dict$(d() AS DICTIONARY, label$) + DIM AS INTEGER lb, ub, i + DIM AS STRING r, t, q, skey, sval + lb% = LBOUND(d) : ub% = UBOUND(d) : t$ = _TRIM$(STR$(ub% - lb%)) + r$ = GJ_LIB_NL$ + "DICT: " + label$ + "(" + t$ + ") {" + GJ_LIB_NL$ + q$ = CHR$(34) + FOR i% = lb% TO ub% + skey$ = d(i%).key$ + sval$ = d(i%).val$ + r$ = r$ + " (" + _TRIM$(STR$(i%)) + ") " + q$ + skey$ + q$ + r$ = r$ + ": " + q$ + sval$ + q$ + GJ_LIB_NL$ + NEXT i% + r$ = r$ + "}" + DUMP.dict$ = r$ + END FUNCTION + $END IF + $IF GJ_LIB_INPUT_LIGHTBAR_INC_BM = UNDEFINED THEN + '' + ' QB64_GJ_LIB + ' GRYMMJACK'S INPUT LIB - LIGHTBAR + ' + ' USAGE: + ' Insert '$INCLUDE:'path_to_GJ_LIB/INPUT/LIGHTBAR.BI' at the top of file + ' Insert '$INCLUDE:'path_to_GJ_LIB/INPUT/LIGHTBAR.BM' at the bottom of file + ' + ' @author Rick Christy + ' @uses LIGHTBAR.BI + ' + $LET GJ_LIB_INPUT_LIGHTBAR_INC_BM = 1 + + '' + ' Render a LIGHTBAR menu + ' @param LIGHTBAR menu UDT + ' @param LIGHTBAR_OPTIONS o array to hold LIGHTBAR_OPTIONS + ' @return integer choice made (-1 if abort with ESC) + ' + FUNCTION LIGHTBAR%(menu AS LIGHTBAR, o() AS LIGHTBAR_OPTION) + DIM AS INTEGER row, col,sel, lb, ub + DIM AS INTEGER orig_fg, orig_bg + + ' Get lower and upper bounds of options array + lb% = LBOUND(o) : ub% = UBOUND(o) + + ' Capture initial state for cursor and colors + row% = CSRLIN : col% = POS(0) ' Store initial cursor position + orig_fg% = SCREEN(row%, col%, 1) AND 15 ' Store initial foreground color + orig_bg% = SCREEN(row%, col%, 1) \ 16 ' Store initial background color + + LIGHTBAR.get_options menu, o(), row%, col%, menu.opt_selected% + LIGHTBAR.draw menu, o(), menu.opt_selected% + sel% = LIGHTBAR.get_choice%(menu, o()) + + ' Restore original colors + COLOR orig_fg%, orig_bg% + + ' Position cursor under menu + IF menu.opt_vertical = 1 THEN + LOCATE row% + (ub% - lb%) + 1, col% ' Vertical + ELSE + LOCATE o(ub%).row% + 1, col% ' Horizontal + END IF + + LIGHTBAR% = sel% + END FUNCTION + + + '' + ' Get LIGHTBAR options as an array + ' @param LIGHTBAR menu + ' @param LIGHTBAR_OPTIONS array of LIGHTBAR_OPTIONS + ' @param INTEGER row% the original row cursor was on + ' @param INTEGER col% the original column cursor was on + ' @param INTEGER sel% the selected menu item + ' + SUB LIGHTBAR.get_options(menu AS LIGHTBAR, o() AS LIGHTBAR_OPTION, row%, col%, sel%) + DIM AS INTEGER i, lb, ub, key_pos_s, key_pos_e + DIM AS INTEGER cur_row, cur_col, w + + ' Get lower and upper bounds of options array + lb% = LBOUND(o) : ub% = UBOUND(o) + + w% = menu.max_width% ' Get the max width for horiz menu + cur_row% = row% : cur_col% = col% ' Init current row and current col + + FOR i% = lb% to ub% + ' Extract hot key start and end positions + key_pos_s% = INSTR(0, o(i%).txt, menu.delimeter$) + key_pos_e% = INSTR(key_pos_s%, o(i%).txt, menu.delimeter$) + + ' Extract left and right part of option without key or delimeter + o(i%).lft$ = MID$(o(i%).txt, 0, key_pos_s%) + o(i%).rgt$ = MID$(o(i%).txt, key_pos_s% + 3) + + ' Capture hot key into arrays + o(i%).key$ = MID$(o(i%).txt, key_pos_s% + 1, 1) + + ' Capture visible option length + o(i%).len% = LEN(o(i%).lft$ + o(i%).key$ + o(i%).rgt$) + + ' Check if option is selected + o(i%).sel% = 0 + IF i% = menu.opt_selected% THEN sel% = i% : o(i%).sel% = 1 + + ' Calculate row and col positions for option + IF menu.opt_vertical% = 1 THEN ' Vertical + o(i%).row% = row% + i% ' In vert LIGHTBAR menu, 1 opt per row + o(i%).col% = col% ' In vert LIGHTBAR menu, column is same + ELSE ' Horizontal + IF cur_col% + o(i%).len% >= w% THEN ' Option WILL wrap + o(i%).col% = col% ' Reset col to init col + cur_col% = col% + o(i%).len% ' Reset cur_col counter + cur_row% = cur_row% + 1 ' Increment cur_row + o(i%).row% = cur_row% ' Set row to cur_row + ELSE ' Option will NOT wrap + o(i%).col% = cur_col% ' Set col to current col + o(i%).row% = cur_row% ' Set row to current row + cur_col% = cur_col% + o(i%).len% ' Increment current col + END IF + END IF + NEXT i% + END SUB + + + '' + ' Draws LIGHTBAR menu + ' @param LIGHTBAR menu + ' @param LIGHTBAR_OPTIONS array of LIGHTBAR_OPTIONS + ' @param INTEGER sel% which option is selected + ' + SUB LIGHTBAR.draw(menu AS LIGHTBAR, o() AS LIGHTBAR_OPTION, sel%) + DIM AS INTEGER i, lb, ub + DIM AS INTEGER fg, bg, kf, kb + + ' Get lower and upper bounds of options array + lb% = LBOUND(o) : ub% = UBOUND(o) + + FOR i% = lb% TO ub% ' Walk the array of menu options + LOCATE o(i%).row%, o(i%).col% ' Position the option + IF i% = sel% THEN ' Selected colors + fg% = menu.bar_fg_color% : bg% = menu.bar_bg_color% + kf% = menu.bar_kf_color% : kb% = menu.bar_kb_color% + ELSE ' Unselected colors + fg% = menu.opt_fg_color% : bg% = menu.opt_bg_color% + kf% = menu.key_fg_color% : kb% = menu.key_bg_color% + END IF + ' Draw the option + COLOR fg%, bg% : PRINT o(i%).lft$; ' Draw opt left + COLOR kf%, kb% : PRINT o(i%).key$; ' Draw opt hot key + COLOR fg%, bg% : PRINT o(i%).rgt$; ' Draw opt right + NEXT i% + END SUB + + + '' + ' Get choice from user in LIGHTBAR menu + ' @param LIGHTBAR menu + ' @param LIGHTBAR_OPTIONS array of LIGHTBAR_OPTIONS + ' @return INTEGER choice user made (-1 = aborted) + ' + FUNCTION LIGHTBAR.get_choice%(menu AS LIGHTBAR, o() AS LIGHTBAR_OPTION) + DIM k AS STRING + DIM AS INTEGER i, key_code, do_move, lb, ub, sel + + ' Get lower and upper bounds of options array + lb% = LBOUND(o) : ub% = UBOUND(o) + + ' Define key constants + CONST KEY_ESC = 27 + CONST KEY_HOME = 71 : CONST KEY_END = 79 + CONST KEY_LEFT = 75 : CONST KEY_RIGHT = 77 + CONST KEY_UP = 72 : CONST KEY_DOWN = 80 + CONST KEY_ENTER = 13 + + DO: + ' Init do_move which determines if we moved after all the SELECTs + do_move% = 0 ' 1 = move, 2 = pick, 3 = abort + _LIMIT 30 + k$ = INKEY$ + IF k$ <> "" THEN + IF LEFT$(k$, 1) = CHR$(0) THEN ' Handle SPECIAL keys + key_code% = ASC(RIGHT$(k$, 1)) ' Get char code sans CHR$(0) + SELECT CASE key_code% + CASE KEY_HOME: + do_move% = 1 + sel% = lb% + CASE KEY_END: + do_move% = 1 + sel% = ub% + CASE KEY_DOWN, KEY_RIGHT: + do_move% = 1 + sel% = sel% + 1 + IF sel% > ub% THEN sel% = lb% + CASE KEY_UP, KEY_LEFT: + do_move% = 1 + sel% = sel% - 1 + IF sel% < lb% THEN sel% = ub% + END SELECT + END IF + + FOR i% = lb% TO ub% ' Handle option hot keys + IF LCASE$(k$) = LCASE$(o(i%).key$) THEN + do_move% = 2 + sel% = i% + END IF + NEXT i% + + IF k$ = CHR$(KEY_ESC) THEN ' ESCAPE to abort + do_move% = 3 + END IF + END IF + IF do_move% > 0 THEN + ' Handle moves + SELECT CASE do_move% + CASE 1: ' move + LIGHTBAR.sound menu.use_sounds%, menu.snd_move_frq!, menu.snd_move_dur!, menu.snd_move_vol! + LIGHTBAR.draw menu, o(), sel% + CASE 2: ' pick + LIGHTBAR.draw menu, o(), sel% + CASE 3: ' abort + LIGHTBAR.sound menu.use_sounds%, menu.snd_abrt_frq!, menu.snd_abrt_dur!, menu.snd_abrt_vol! + LIGHTBAR.get_choice% = -1 + EXIT FUNCTION + END SELECT + END IF + LOOP UNTIL k$ = CHR$(KEY_ENTER) OR k$ = CHR$(KEY_ESC) OR do_move% = 2 + LIGHTBAR.sound menu.use_sounds%, menu.snd_pick_frq!, menu.snd_pick_dur!, menu.snd_pick_vol! + LIGHTBAR.get_choice% = sel% + END FUNCTION + + + '' + ' Handles LIGHTBAR sounds + ' @param INTEGER use_sounds% 0 = no, 1 = yes + ' @param SINGLE frq frequency for sound + ' @param SINGLE dur duration of sound + ' @param SINGLE vol volume of sound + ' + SUB LIGHTBAR.sound(use_sounds%, frq!, dur!, vol!) + IF use_sounds% = 1 THEN + SOUND frq!, dur!, vol! + END IF + END SUB + $END IF + $IF GJ_LIB_INPUT_LIGHTBAR32_INC_BM = UNDEFINED THEN + '' + ' QB64_GJ_LIB + ' GRYMMJACK'S INPUT LIB - LIGHTBAR32 + ' + ' USAGE: + ' Insert '$INCLUDE:'path_to_GJ_LIB/INPUT/LIGHTBAR32.BI' at the top of file + ' Insert '$INCLUDE:'path_to_GJ_LIB/INPUT/LIGHTBAR32.BM' at the bottom of file + ' + ' @author Rick Christy + ' @uses LIGHTBAR32.BI + ' + $LET GJ_LIB_INPUT_LIGHTBAR32_INC_BM = 1 + + '' + ' Render a LIGHTBA32R menu + ' @param LIGHTBAR32 menu UDT + ' @param LIGHTBAR_OPTIONS o array to hold LIGHTBAR_OPTIONS + ' @return integer choice made (-1 if abort with ESC) + ' + FUNCTION LIGHTBAR32%(menu AS LIGHTBAR32, o() AS LIGHTBAR32_OPTION) + DIM AS INTEGER row, col, sel, lb, ub + DIM AS _UNSIGNED LONG orig_fg, orig_bg + + ' Get lower and upper bounds of options array + lb% = LBOUND(o) : ub% = UBOUND(o) + + ' Capture initial state for cursor and colors + row% = CSRLIN : col% = POS(0) ' Store initial cursor position + orig_fg~& = LB_EGA(7) ' Store initial foreground color + orig_bg~& = LB_EGA(0) ' Store initial background color + + LIGHTBAR32.get_options menu, o(), row%, col%, menu.opt_selected% + LIGHTBAR32.draw menu, o(), menu.opt_selected% + sel% = LIGHTBAR32.get_choice%(menu, o()) + + ' Restore original colors + COLOR orig_fg~&, orig_bg~& + + ' Position cursor under menu + IF menu.opt_vertical = 1 THEN + LOCATE row% + (ub% - lb%) + 1, col% ' Vertical + ELSE + LOCATE o(ub%).row% + 1, col% ' Horizontal + END IF + + LIGHTBAR32% = sel% + END FUNCTION + + + '' + ' Get LIGHTBAR32 options as an array + ' @param LIGHTBAR32 menu + ' @param LIGHTBAR_OPTIONS array of LIGHTBAR_OPTIONS + ' @param INTEGER row% the original row cursor was on + ' @param INTEGER col% the original column cursor was on + ' @param INTEGER sel% the selected menu item + ' + SUB LIGHTBAR32.get_options(menu AS LIGHTBAR32, o() AS LIGHTBAR32_OPTION, row%, col%, sel%) + DIM AS INTEGER i, lb, ub, key_pos_s, key_pos_e + DIM AS INTEGER cur_row, cur_col, w + + ' Get lower and upper bounds of options array + lb% = LBOUND(o) : ub% = UBOUND(o) + + w% = menu.max_width% ' Get the max width for horiz menu + cur_row% = row% : cur_col% = col% ' Init current row and current col + + FOR i% = lb% to ub% + ' Extract hot key start and end positions + key_pos_s% = INSTR(0, o(i%).txt, menu.delimeter$) + key_pos_e% = INSTR(key_pos_s%, o(i%).txt, menu.delimeter$) + + ' Extract left and right part of option without key or delimeter + o(i%).lft$ = MID$(o(i%).txt, 0, key_pos_s%) + o(i%).rgt$ = MID$(o(i%).txt, key_pos_s% + 3) + + ' Capture hot key into arrays + o(i%).key$ = MID$(o(i%).txt, key_pos_s% + 1, 1) + + ' Capture visible option length + o(i%).len% = LEN(o(i%).lft$ + o(i%).key$ + o(i%).rgt$) + + ' Check if option is selected + o(i%).sel% = 0 + IF i% = menu.opt_selected% THEN sel% = i% : o(i%).sel% = 1 + + ' Calculate row and col positions for option + IF menu.opt_vertical% = 1 THEN ' Vertical + o(i%).row% = row% + i% ' In vert LIGHTBAR menu, 1 opt per row + o(i%).col% = col% ' In vert LIGHTBAR menu, column is same + ELSE ' Horizontal + IF ((cur_col% + o(i%).len%) * _FONTWIDTH) >= w% THEN ' Will wrap + o(i%).col% = col% ' Reset col to init col + cur_col% = col% + o(i%).len% ' Reset cur_col counter + cur_row% = cur_row% + 1 ' Increment cur_row + o(i%).row% = cur_row% ' Set row to cur_row + ELSE ' Option will NOT wrap + o(i%).col% = cur_col% ' Set col to current col + o(i%).row% = cur_row% ' Set row to current row + cur_col% = cur_col% + o(i%).len% ' Increment current col + END IF + END IF + NEXT i% + END SUB + + + '' + ' Draws LIGHTBAR32 menu + ' @param LIGHTBAR32 menu + ' @param LIGHTBAR_OPTIONS array of LIGHTBAR_OPTIONS + ' @param INTEGER sel% which option is selected + ' + SUB LIGHTBAR32.draw(menu AS LIGHTBAR32, o() AS LIGHTBAR32_OPTION, sel%) + DIM AS INTEGER i, lb, ub + DIM AS _UNSIGNED LONG fg, bg, kf, kb + + ' Get lower and upper bounds of options array + lb% = LBOUND(o) : ub% = UBOUND(o) + + FOR i% = lb% TO ub% ' Walk the array of menu options + LOCATE o(i%).row%, o(i%).col% ' Position the option + IF i% = sel% THEN ' Selected colors + fg~& = menu.bar_fg_color~& : bg~& = menu.bar_bg_color~& + kf~& = menu.bar_kf_color~& : kb~& = menu.bar_kb_color~& + ELSE ' Unselected colors + fg~& = menu.opt_fg_color~& : bg~& = menu.opt_bg_color~& + kf~& = menu.key_fg_color~& : kb~& = menu.key_bg_color~& + END IF + ' Draw the option + COLOR fg~&, bg~& : PRINT o(i%).lft$; ' Draw opt left + COLOR kf~&, kb~& : PRINT o(i%).key$; ' Draw opt hot key + COLOR fg~&, bg~& : PRINT o(i%).rgt$; ' Draw opt right + NEXT i% + END SUB + + + '' + ' Get choice from user in LIGHTBAR menu + ' @param LIGHTBAR32 menu + ' @param LIGHTBAR_OPTIONS array of LIGHTBAR_OPTIONS + ' @return INTEGER choice user made (-1 = aborted) + ' + FUNCTION LIGHTBAR32.get_choice%(menu AS LIGHTBAR32, o() AS LIGHTBAR32_OPTION) + DIM k AS STRING + DIM AS INTEGER i, key_code, do_move, lb, ub, sel + + ' Get lower and upper bounds of options array + lb% = LBOUND(o) : ub% = UBOUND(o) + + ' Define key constants + CONST KEY_ESC = 27 + CONST KEY_HOME = 71 : CONST KEY_END = 79 + CONST KEY_LEFT = 75 : CONST KEY_RIGHT = 77 + CONST KEY_UP = 72 : CONST KEY_DOWN = 80 + CONST KEY_ENTER = 13 + + DO: + ' Init do_move which determines if we moved after all the SELECTs + do_move% = 0 ' 1 = move, 2 = pick, 3 = abort + _LIMIT 30 + k$ = INKEY$ + IF k$ <> "" THEN + IF LEFT$(k$, 1) = CHR$(0) THEN ' Handle SPECIAL keys + key_code% = ASC(RIGHT$(k$, 1)) ' Get char code sans CHR$(0) + SELECT CASE key_code% + CASE KEY_HOME: + do_move% = 1 + sel% = lb% + CASE KEY_END: + do_move% = 1 + sel% = ub% + CASE KEY_DOWN, KEY_RIGHT: + do_move% = 1 + sel% = sel% + 1 + IF sel% > ub% THEN sel% = lb% + CASE KEY_UP, KEY_LEFT: + do_move% = 1 + sel% = sel% - 1 + IF sel% < lb% THEN sel% = ub% + END SELECT + END IF + + FOR i% = lb% TO ub% ' Handle option hot keys + IF LCASE$(k$) = LCASE$(o(i%).key$) THEN + do_move% = 2 + sel% = i% + END IF + NEXT i% + + IF k$ = CHR$(KEY_ESC) THEN ' ESCAPE to abort + do_move% = 3 + END IF + END IF + IF do_move% > 0 THEN + ' Handle moves + SELECT CASE do_move% + CASE 1: ' move + LIGHTBAR32.sound menu.use_sounds%, menu.snd_move_frq!, menu.snd_move_dur!, menu.snd_move_vol! + LIGHTBAR32.draw menu, o(), sel% + CASE 2: ' pick + LIGHTBAR32.draw menu, o(), sel% + CASE 3: ' abort + LIGHTBAR32.sound menu.use_sounds%, menu.snd_abrt_frq!, menu.snd_abrt_dur!, menu.snd_abrt_vol! + LIGHTBAR32.get_choice% = -1 + EXIT FUNCTION + END SELECT + END IF + LOOP UNTIL k$ = CHR$(KEY_ENTER) OR k$ = CHR$(KEY_ESC) OR do_move% = 2 + LIGHTBAR32.sound menu.use_sounds%, menu.snd_pick_frq!, menu.snd_pick_dur!, menu.snd_pick_vol! + LIGHTBAR32.get_choice% = sel% + END FUNCTION + + + '' + ' Handles LIGHTBAR sounds + ' @param INTEGER use_sounds% 0 = no, 1 = yes + ' @param SINGLE frq frequency for sound + ' @param SINGLE dur duration of sound + ' @param SINGLE vol volume of sound + ' + SUB LIGHTBAR32.sound(use_sounds%, frq!, dur!, vol!) + IF use_sounds% = 1 THEN + SOUND frq!, dur!, vol! + END IF + END SUB + $END IF + $IF GJ_LIB_ANSI_INC_BM = UNDEFINED THEN + '' + ' QB64_GJ_LIB + ' GRYMMJACK'S ANSI LIB + ' + ' Support for ANSI.SYS and extended codes for terminal. + ' + ' To emulate ANSI functionality using QB internals set this var to TRUE: + ' GJ_LIB_ANSI_EMU = TRUE + ' This variable can be toggled between TRUE and FALSE whenever needed, as many + ' times as desired as it is not a CONST. + ' + ' NOTE: + ' There is a QB64 bug on MacOS/Linux where $CONSOLE:ONLY does not read input + ' in the same way as on Windows. This bug is described here: + ' https://github.com/QB64Official/qb64/issues/33 + ' + ' @author Rick Christy + ' @uses ANSI.BI + ' @see https://gist.github.com/grymmjack/9dae29a60ea65f086d0b35df96fe2291 + ' + $LET GJ_LIB_ANSI_INC_BM = 1 + + + + '' + ' Clamps a value from going below 0 + ' + ' @param INTEGER var% to clamp to zero + ' @return INTEGER var clamped to 0 or more + ' + FUNCTION ANSI.clamp_zero%(var%) + IF var% < 0 THEN + ANSI.clamp_zero% = 0 + ELSE + ANSI.clamp_zero% = var% + END IF + END FUNCTION + + + '' + ' Safely locates within ranges 1+ on row and col + ' + ' @param INTEGER row% for locate + ' @param INTEGER col% for locate + ' + SUB ANSI.safe_locate(row%, col%) + IF row% <= 0 THEN row% = 1 + IF col% <= 0 THEN col% = 1 + LOCATE row%, col% + END SUB + + + '' + ' Safely locates within ranges 1+ on col + ' + ' @param INTEGER col% for locate + ' + SUB ANSI.safe_locate_x(col%) + IF col% <= 0 THEN col% = 1 + LOCATE , col% + END SUB + + + '' + ' Safely locates within ranges 1+ on row + ' + ' @param INTEGER row% for locate + ' + SUB ANSI.safe_locate_y(row%) + IF row% <= 0 THEN row% = 1 + LOCATE row% + END SUB + + + '' + ' Hides cursor + ' + ' @return STRING with ANSI escape codes to hide cursor + ' + FUNCTION ANSI.hide_cursor$() + DIM AS STRING sout + sout$ = CHR$(ANSI.ESC) + "[?25l" + IF GJ_LIB_ANSI_EMU THEN LOCATE ,,0 + IF GJ_LIB_ANSI_OUTPUT THEN ANSI.hide_cursor$ = sout$ + END FUNCTION + + + '' + ' Shows cursor + ' + ' @return STRING with ANSI escape codes to show cursor + ' + FUNCTION ANSI.show_cursor$() + DIM AS STRING sout + sout$ = CHR$(ANSI.ESC) + "[?25h" + IF GJ_LIB_ANSI_EMU THEN LOCATE ,,1 + IF GJ_LIB_ANSI_OUTPUT THEN ANSI.show_cursor$ = sout$ + END FUNCTION + + + '' + ' Moves cursor to home position (0,0) + ' + ' @return STRING with ANSI escape codes to move cursor + ' + FUNCTION ANSI.home$() + DIM AS STRING sout + sout$ = CHR$(ANSI.ESC) + "[H" + ANSI.x% = 0 : ANSI.y% = 0 + IF GJ_LIB_ANSI_EMU THEN CALL ANSI.safe_locate(ANSI.y%, ANSI.x%) + IF GJ_LIB_ANSI_OUTPUT THEN ANSI.home$ = sout$ + END FUNCTION + + + '' + ' Moves cursor to desired row and column + ' + ' @param INTEGER row% to move cursor to + ' @param INTEGER col% to move cursor to + ' @return STRING with ANSI escape codes to move cursor + ' + FUNCTION ANSI.locate$(row%, col%) + DIM AS STRING sout + sout$ = CHR$(ANSI.ESC) + "[" + sout$ = sout$ + _TRIM$(STR$(row%)) + ";" + sout$ = sout$ + _TRIM$(STR$(col%)) + "H" + ANSI.x% = col% : ANSI.y% = row% + IF GJ_LIB_ANSI_EMU THEN CALL ANSI.safe_locate(ANSI.y%, ANSI.x%) + IF GJ_LIB_ANSI_OUTPUT THEN ANSI.locate$ = sout$ + END FUNCTION + + + '' + ' Moves cursor up n lines + ' + ' @param INTEGER n% Number of lines to move cursor up + ' @return STRING with ANSI escape codes to move cursor + ' + FUNCTION ANSI.move_up$(n%) + DIM AS STRING sout + sout$ = CHR$(ANSI.ESC) + "[" + sout$ = sout$ + _TRIM$(STR$(n%)) + "A" + ANSI.y% = ANSI.clamp_zero(ANSI.y% - n%) + IF GJ_LIB_ANSI_EMU THEN CALL ANSI.safe_locate_y(ANSI.y%) + IF GJ_LIB_ANSI_OUTPUT THEN ANSI.move_up$ = sout$ + END FUNCTION + + + '' + ' Moves cursor down n lines + ' + ' @param INTEGER n% Number of lines to move cursor down + ' @return STRING with ANSI escape codes to move cursor + ' + FUNCTION ANSI.move_down$(n%) + DIM AS STRING sout + sout$ = CHR$(ANSI.ESC) + "[" + sout$ = sout$ + _TRIM$(STR$(n%)) + "B" + ANSI.y% = ANSI.y% + n% + IF GJ_LIB_ANSI_EMU THEN CALL ANSI.safe_locate_y(ANSI.y%) + IF GJ_LIB_ANSI_OUTPUT THEN ANSI.move_down$ = sout$ + END FUNCTION + + + '' + ' Moves cursor right n lines + ' + ' @param INTEGER n% Number of lines to move cursor right + ' @return STRING with ANSI escape codes to move cursor + ' + FUNCTION ANSI.move_right$(n%) + DIM AS STRING sout + sout$ = CHR$(ANSI.ESC) + "[" + sout$ = sout$ + _TRIM$(STR$(n%)) + "C" + ANSI.x% = ANSI.x% + n% + IF GJ_LIB_ANSI_EMU THEN CALL ANSI.safe_locate_x(ANSI.x%) + IF GJ_LIB_ANSI_OUTPUT THEN ANSI.move_right$ = sout$ + END FUNCTION + + + '' + ' Moves cursor left n lines + ' + ' @param INTEGER n% Number of lines to move cursor left + ' @return STRING with ANSI escape codes to move cursor + ' + FUNCTION ANSI.move_left$(n%) + DIM AS STRING sout + sout$ = CHR$(ANSI.ESC) + "[" + sout$ = sout$ + _TRIM$(STR$(n%)) + "D" + ANSI.x% = ANSI.clamp_zero(ANSI.x% - n%) + IF GJ_LIB_ANSI_EMU THEN CALL ANSI.safe_locate_x(ANSI.x%) + IF GJ_LIB_ANSI_OUTPUT THEN ANSI.move_left$ = sout$ + END FUNCTION + + + '' + ' Moves cursor to beginning of next line, n lines down + ' + ' @param INTEGER n% Number of lines to move cursor down + ' @return STRING with ANSI escape codes to move cursor + ' + FUNCTION ANSI.move_lines_down$(n%) + DIM AS STRING sout + sout$ = CHR$(ANSI.ESC) + "[" + sout$ = sout$ + _TRIM$(STR$(n%)) + "E" + ANSI.y% = ANSI.y% + n% + IF GJ_LIB_ANSI_EMU THEN CALL ANSI.safe_locate_y(ANSI.y%) + IF GJ_LIB_ANSI_OUTPUT THEN ANSI.move_lines_down$ = sout$ + END FUNCTION + + + '' + ' Moves cursor to beginning of previous line, n lines up + ' + ' @param INTEGER n% Number of lines to move cursor up + ' @return STRING with ANSI escape codes to move cursor + ' + FUNCTION ANSI.move_lines_up$(n%) + DIM AS STRING sout + sout$ = CHR$(ANSI.ESC) + "[" + sout$ = sout$ + _TRIM$(STR$(n%)) + "F" + ANSI.y% = ANSI.clamp_zero(ANSI.y% - n%) + IF GJ_LIB_ANSI_EMU THEN CALL ANSI.safe_locate_y(ANSI.y%) + IF GJ_LIB_ANSI_OUTPUT THEN ANSI.move_lines_up$ = sout$ + END FUNCTION + + + '' + ' Moves cursor to column position n + ' + ' @param INTEGER n% Column to move cursor to + ' @return STRING with ANSI escape codes to move cursor + ' + FUNCTION ANSI.move_column$(n%) + DIM AS STRING sout + sout$ = CHR$(ANSI.ESC) + "[" + sout$ = sout$ + _TRIM$(STR$(n%)) + "G" + ANSI.x% = n% + IF GJ_LIB_ANSI_EMU THEN CALL ANSI.safe_locate_x(ANSI.x%) + IF GJ_LIB_ANSI_OUTPUT THEN ANSI.move_column$ = sout$ + END FUNCTION + + + '' + ' Moves cursor one line up, scrolling if needed + ' + ' @return STRING with ANSI escape codes to move cursor + ' + FUNCTION ANSI.move_line_up$() + DIM AS STRING sout + sout$ = CHR$(ANSI.ESC) + "M" + ANSI.y% = ANSI.clamp_zero(ANSI.y% - 1) + IF GJ_LIB_ANSI_EMU THEN CALL ANSI.safe_locate_y(ANSI.y%) + IF GJ_LIB_ANSI_OUTPUT THEN ANSI.move_line_up$ = sout$ + END FUNCTION + + + '' + ' Save cursor position + ' + ' @return STRING with ANSI escape codes + ' + FUNCTION ANSI.save_pos$() + DIM AS STRING sout + sout$ = CHR$(ANSI.ESC) + "[s" + ANSI.save_x% = ANSI.x% : ANSI.save_y% = ANSI.y% + IF GJ_LIB_ANSI_OUTPUT THEN ANSI.save_pos$ = sout$ + END FUNCTION + + + '' + ' Restore cursor position + ' + ' @return STRING with ANSI escape codes + ' + FUNCTION ANSI.restore_pos$() + DIM AS STRING sout, nil + sout$ = CHR$(ANSI.ESC) + "[u" + nil$ = ANSI.locate(ANSI.save_y%, ANSI.save_x%) + IF GJ_LIB_ANSI_EMU THEN CALL ANSI.safe_locate(ANSI.save_y%, ANSI.save_x%) + IF GJ_LIB_ANSI_OUTPUT THEN ANSI.restore_pos$ = sout$ + END FUNCTION + + + '' + ' Erase from cursor to end of screen + ' + ' @return STRING with ANSI escape codes + ' + FUNCTION ANSI.erase_to_eos$() + DIM AS STRING sout + DIM AS INTEGER w, h, x, y, row + sout$ = CHR$(ANSI.ESC) + "[0J" + IF GJ_LIB_ANSI_EMU THEN + w = _WIDTH + h = _HEIGHT + x = ANSI.x% + y = ANSI.y% + PRINT SPC(w-x) + FOR row = y TO h + LOCATE row, 1 + PRINT SPC(w) + NEXT row + CALL ANSI.safe_locate(ANSI.y%, ANSI.x%) + END IF + IF GJ_LIB_ANSI_OUTPUT THEN ANSI.erase_to_eos$ = sout$ + END FUNCTION + + + '' + ' Erase from cursor to beginning of screen + ' + ' @return STRING with ANSI escape codes + ' + FUNCTION ANSI.erase_to_bos$() + DIM AS STRING sout + DIM AS INTEGER w, h, row + sout$ = CHR$(ANSI.ESC) + "[1J" + IF GJ_LIB_ANSI_EMU THEN + w = _WIDTH + h = _HEIGHT + LOCATE ,1 + PRINT SPC(ANSI.x%-1) + FOR row = h TO 1 STEP - 1 + LOCATE row, 1 + PRINT SPC(w) + NEXT row + CALL ANSI.safe_locate(ANSI.y%, ANSI.x%) + END IF + IF GJ_LIB_ANSI_OUTPUT THEN ANSI.erase_to_bos$ = sout$ + END FUNCTION + + + '' + ' Erase entire screen + ' + ' @return STRING with ANSI escape codes + ' + FUNCTION ANSI.erase_screen$() + DIM AS STRING sout + sout$ = CHR$(ANSI.ESC) + "[2J" + sout$ = sout$ + ANSI.locate(1,1) + ANSI.x% = 0 : ANSI.y% = 0 + IF GJ_LIB_ANSI_EMU THEN + CALL ANSI.safe_locate(ANSI.y%, ANSI.x%) + CLS + END IF + IF GJ_LIB_ANSI_OUTPUT THEN ANSI.erase_screen$ = sout$ + END FUNCTION + + + '' + ' Erase from cursor to end of line + ' + ' @return STRING with ANSI escape codes + ' + FUNCTION ANSI.erase_to_eol$() + DIM AS STRING sout + DIM AS INTEGER w + sout$ = CHR$(ANSI.ESC) + "[0K" + IF GJ_LIB_ANSI_EMU THEN + w = _WIDTH + PRINT SPC(w-ANSI.x%) + CALL ANSI.safe_locate(ANSI.y%, ANSI.x%) + END IF + IF GJ_LIB_ANSI_OUTPUT THEN ANSI.erase_to_eol$ = sout$ + END FUNCTION + + + '' + ' Erase from start of line to cursor + ' + ' @return STRING with ANSI escape codes + ' + FUNCTION ANSI.erase_from_sol$() + DIM AS STRING sout + DIM AS INTEGER w + sout$ = CHR$(ANSI.ESC) + "[1K" + IF GJ_LIB_ANSI_EMU THEN + w = _WIDTH + CALL ANSI.safe_locate(ANSI.y%, ANSI.x%) + PRINT SPC(w-ANSI.x%) + END IF + IF GJ_LIB_ANSI_OUTPUT THEN ANSI.erase_from_sol$ = sout$ + END FUNCTION + + + '' + ' Erase line + ' + ' @return STRING with ANSI escape codes + ' + FUNCTION ANSI.erase_line$() + DIM AS STRING sout + DIM AS INTEGER w, x, y + sout$ = CHR$(ANSI.ESC) + "[2K" + IF GJ_LIB_ANSI_EMU THEN + w = _WIDTH + x = ANSI.x% + y = ANSI.y% + LOCATE ,1 + PRINT SPC(w) + CALL ANSI.safe_locate(ANSI.y%, ANSI.x%) + END IF + IF GJ_LIB_ANSI_OUTPUT THEN ANSI.erase_line$ = sout$ + END FUNCTION + + + '' + ' Reset modes + ' + ' @return STRING with ANSI escape codes for resetting 1modes + ' + FUNCTION ANSI.mode_reset_all$() + DIM AS STRING sout + sout$ = CHR$(ANSI.ESC) + "[0m" + IF GJ_LIB_ANSI_EMU THEN + ANSI.fg_color& = 7 : ANSI.bg_color& = 0 + COLOR ANSI.fg_color&, ANSI.bg_color& + _BLINK ON + END IF + IF GJ_LIB_ANSI_OUTPUT THEN ANSI.mode_reset_all$ = sout$ + END FUNCTION + + + '' + ' Set bold mode + ' + ' @return STRING with ANSI escape codes for setting mode + ' + FUNCTION ANSI.mode_bold$() + DIM AS STRING sout + sout$ = CHR$(ANSI.ESC) + "[1m" + IF GJ_LIB_ANSI_EMU THEN + IF ANSI.fg_color& <= 7 THEN ANSI.fg_color& = ANSI.fg_color& + 8 + COLOR ANSI.fg_color& + END IF + IF GJ_LIB_ANSI_OUTPUT THEN ANSI.mode_bold$ = sout$ + END FUNCTION + + + '' + ' Reset bold mode + ' + ' @return STRING with ANSI escape codes for resetting mode + ' + FUNCTION ANSI.mode_bold_reset$() + DIM AS STRING sout + sout$ = CHR$(ANSI.ESC) + "[22m" + IF GJ_LIB_ANSI_EMU THEN + IF ANSI.fg_color& >= 8 THEN ANSI.fg_color& = ANSI.fg_color& - 8 + COLOR ANSI.fg_color& + END IF + IF GJ_LIB_ANSI_OUTPUT THEN ANSI.mode_bold_reset$ = sout$ + END FUNCTION + + + '' + ' Set dim mode + ' + ' @return STRING with ANSI escape codes for setting mode + ' + FUNCTION ANSI.mode_dim$() + DIM AS STRING sout, nil + sout$ = CHR$(ANSI.ESC) + "[2m" + IF GJ_LIB_ANSI_EMU THEN nil$ = ANSI.mode_bold_reset + IF GJ_LIB_ANSI_OUTPUT THEN ANSI.mode_dim$ = sout$ + END FUNCTION + + + '' + ' Reset dim mode + ' + ' @return STRING with ANSI escape codes for resetting mode + ' + FUNCTION ANSI.mode_dim_reset$() + DIM AS STRING sout, nil + sout$ = CHR$(ANSI.ESC) + "[22m" + IF GJ_LIB_ANSI_EMU THEN nil$ = ANSI.mode_bold_reset + IF GJ_LIB_ANSI_OUTPUT THEN ANSI.mode_dim_reset$ = sout$ + END FUNCTION + + + '' + ' Set italic mode + ' + ' @return STRING with ANSI escape codes for setting mode + ' + FUNCTION ANSI.mode_italic$() + DIM AS STRING sout, nil + sout$ = CHR$(ANSI.ESC) + "[3m" + IF GJ_LIB_ANSI_EMU THEN nil$ = ANSI.mode_bold + IF GJ_LIB_ANSI_OUTPUT THEN ANSI.mode_italic$ = sout$ + END FUNCTION + + + '' + ' Reset italic mode + ' + ' @return STRING with ANSI escape codes for resetting mode + ' + FUNCTION ANSI.mode_italic_reset$() + DIM AS STRING sout, nil + sout$ = CHR$(ANSI.ESC) + "[23m" + IF GJ_LIB_ANSI_EMU THEN nil$ = ANSI.mode_bold_reset + IF GJ_LIB_ANSI_OUTPUT THEN ANSI.mode_italic_reset$ = sout$ + END FUNCTION + + + '' + ' Set underline mode + ' + ' @return STRING with ANSI escape codes for setting mode + ' + FUNCTION ANSI.mode_underline$() + DIM AS STRING sout, nil + sout$ = CHR$(ANSI.ESC) + "[4m" + IF GJ_LIB_ANSI_EMU THEN nil$ = ANSI.mode_bold + IF GJ_LIB_ANSI_OUTPUT THEN ANSI.mode_underline$ = sout$ + END FUNCTION + + + '' + ' Reset underline mode + ' + ' @return STRING with ANSI escape codes for resetting mode + ' + FUNCTION ANSI.mode_underline_reset$() + DIM AS STRING sout, nil + sout$ = CHR$(ANSI.ESC) + "[24m" + IF GJ_LIB_ANSI_EMU THEN nil$ = ANSI.mode_bold_reset + IF GJ_LIB_ANSI_OUTPUT THEN ANSI.mode_underline_reset$ = sout$ + END FUNCTION + + + '' + ' Set blinking mode + ' + ' @return STRING with ANSI escape codes for setting mode + ' + FUNCTION ANSI.mode_blinking$() + DIM AS STRING sout + sout$ = CHR$(ANSI.ESC) + "[5m" + IF GJ_LIB_ANSI_EMU THEN _BLINK ON + IF GJ_LIB_ANSI_OUTPUT THEN ANSI.mode_blinking$ = sout$ + END FUNCTION + + + '' + ' Reset blinking mode + ' + ' @return STRING with ANSI escape codes for resetting mode + ' + FUNCTION ANSI.mode_blinking_reset$() + DIM AS STRING sout + sout$ = CHR$(ANSI.ESC) + "[25m" + IF GJ_LIB_ANSI_EMU THEN _BLINK OFF + IF GJ_LIB_ANSI_OUTPUT THEN ANSI.mode_blinking_reset$ = sout$ + END FUNCTION + + + '' + ' Set inverse mode + ' + ' @return STRING with ANSI escape codes for setting mode + ' + FUNCTION ANSI.mode_inverse$() + DIM AS STRING sout + sout$ = CHR$(ANSI.ESC) + "[?5h" + sout$ = sout$ + CHR$(ANSI.ESC) + "[7m" + IF GJ_LIB_ANSI_EMU THEN + ANSI.old_fg_color& = ANSI.fg_color& + ANSI.old_bg_color& = ANSI.bg_color& + ANSI.fg_color& = ANSI.bg_color& + ANSI.bg_color& = ANSI.old_fg_color& + COLOR ANSI.fg_color&, ANSI.bg_color& + END IF + IF GJ_LIB_ANSI_OUTPUT THEN ANSI.mode_inverse$ = sout$ + END FUNCTION + + + '' + ' Reset inverse mode + ' + ' @return STRING with ANSI escape codes for resetting mode + ' + FUNCTION ANSI.mode_inverse_reset$() + DIM AS STRING sout + sout$ = CHR$(ANSI.ESC) + "[?5l" + sout$ = sout$ + CHR$(ANSI.ESC) + "[27m" + IF GJ_LIB_ANSI_EMU THEN COLOR ANSI.old_fg_color&, ANSI.old_bg_color& + IF GJ_LIB_ANSI_OUTPUT THEN ANSI.mode_inverse_reset$ = sout$ + END FUNCTION + + + '' + ' Set invisible mode + ' + ' @return STRING with ANSI escape codes for setting mode + ' + FUNCTION ANSI.mode_invisible$() + DIM AS STRING sout + sout$ = CHR$(ANSI.ESC) + "[8m" + IF GJ_LIB_ANSI_EMU THEN + ANSI.old_fg_color& = ANSI.fg_color& + ANSI.old_bg_color& = ANSI.bg_color& + ANSI.fg_color& = ANSI.bg_color& + COLOR ANSI.fg_color&, ANSI.bg_color& + END IF + IF GJ_LIB_ANSI_OUTPUT THEN ANSI.mode_invisible$ = sout$ + END FUNCTION + + + '' + ' Reset invisible mode + ' + ' @return STRING with ANSI escape codes for resetting mode + ' + FUNCTION ANSI.mode_invisible_reset$() + DIM AS STRING sout + sout$ = CHR$(ANSI.ESC) + "[28m" + IF GJ_LIB_ANSI_EMU THEN COLOR ANSI.old_fg_color&, ANSI.old_bg_color& + IF GJ_LIB_ANSI_OUTPUT THEN ANSI.mode_invisible_reset$ = sout$ + END FUNCTION + + + '' + ' Set strikethrough mode + ' + ' @return STRING with ANSI escape codes for setting mode + ' + FUNCTION ANSI.mode_strikethrough$() + DIM AS STRING sout + sout$ = CHR$(ANSI.ESC) + "[9m" + IF GJ_LIB_ANSI_OUTPUT THEN ANSI.mode_strikethrough$ = sout$ + END FUNCTION + + + '' + ' Reset strikethrough mode + ' + ' @return STRING with ANSI escape codes for resetting mode + ' + FUNCTION ANSI.mode_strikethrough_reset$() + DIM AS STRING sout + sout$ = CHR$(ANSI.ESC) + "[29m" + IF GJ_LIB_ANSI_OUTPUT THEN ANSI.mode_strikethrough_reset$ = sout$ + END FUNCTION + + + '' + ' Reset foreground color + ' + ' @return STRING with ANSI escape codes for resetting foreground color + ' + FUNCTION ANSI.fg_reset$() + DIM AS STRING sout + sout$ = CHR$(ANSI.ESC) + "[0m" + IF GJ_LIB_ANSI_EMU THEN COLOR 7 + IF GJ_LIB_ANSI_OUTPUT THEN ANSI.fg_reset$ = sout$ + END FUNCTION + + + '' + ' Reset background color + ' + ' @return STRING with ANSI escape codes for resetting background color + ' + FUNCTION ANSI.bg_reset$() + DIM AS STRING sout + sout$ = CHR$(ANSI.ESC) + "[0m" + IF GJ_LIB_ANSI_EMU THEN COLOR ,0 + IF GJ_LIB_ANSI_OUTPUT THEN ANSI.bg_reset$ = sout$ + END FUNCTION + + + '' + ' Set foreground color to black + ' + ' @return STRING with ANSI escape codes for setting color + ' + FUNCTION ANSI.fg_black$() + DIM AS STRING sout + sout$ = CHR$(ANSI.ESC) + "[30m" + IF GJ_LIB_ANSI_EMU THEN COLOR 0 + IF GJ_LIB_ANSI_OUTPUT THEN ANSI.fg_black$ = sout$ + END FUNCTION + + + '' + ' Set foreground color to bright black + ' + ' @return STRING with ANSI escape codes for setting color + ' + FUNCTION ANSI.fg_bright_black$() + DIM AS STRING sout + sout$ = CHR$(ANSI.ESC) + "[30;1m" + IF GJ_LIB_ANSI_EMU THEN COLOR 8 + IF GJ_LIB_ANSI_OUTPUT THEN ANSI.fg_bright_black$ = sout$ + END FUNCTION + + + '' + ' Set background color to black + ' + ' @return STRING with ANSI escape codes for setting color + ' + FUNCTION ANSI.bg_black$() + DIM AS STRING sout + sout$ = CHR$(ANSI.ESC) + "[0;40m" + IF GJ_LIB_ANSI_EMU THEN COLOR ,0 + IF GJ_LIB_ANSI_OUTPUT THEN ANSI.bg_black$ = sout$ + END FUNCTION + + + '' + ' Set background color to bright black + ' + ' @return STRING with ANSI escape codes for setting color + ' + FUNCTION ANSI.bg_bright_black$() + DIM AS STRING sout + sout$ = CHR$(ANSI.ESC) + "[100;1m" + IF GJ_LIB_ANSI_EMU THEN COLOR ,8 + IF GJ_LIB_ANSI_OUTPUT THEN ANSI.bg_bright_black$ = sout$ + END FUNCTION + + + '' + ' Set foreground color to blue + ' + ' @return STRING with ANSI escape codes for setting color + ' + FUNCTION ANSI.fg_blue$() + DIM AS STRING sout + sout$ = CHR$(ANSI.ESC) + "[0;34m" + IF GJ_LIB_ANSI_EMU THEN COLOR 1 + IF GJ_LIB_ANSI_OUTPUT THEN ANSI.fg_blue$ = sout$ + END FUNCTION + + + '' + ' Set foreground color to bright blue + ' + ' @return STRING with ANSI escape codes for setting color + ' + FUNCTION ANSI.fg_bright_blue$() + DIM AS STRING sout + sout$ = CHR$(ANSI.ESC) + "[34;1m" + IF GJ_LIB_ANSI_EMU THEN COLOR 9 + IF GJ_LIB_ANSI_OUTPUT THEN ANSI.fg_bright_blue$ = sout$ + END FUNCTION + + + '' + ' Set background color to blue + ' + ' @return STRING with ANSI escape codes for setting color + ' + FUNCTION ANSI.bg_blue$() + DIM AS STRING sout + sout$ = CHR$(ANSI.ESC) + "[44m" + IF GJ_LIB_ANSI_EMU THEN COLOR ,1 + IF GJ_LIB_ANSI_OUTPUT THEN ANSI.bg_blue$ = sout$ + END FUNCTION + + + '' + ' Set background color to bright blue + ' + ' @return STRING with ANSI escape codes for setting color + ' + FUNCTION ANSI.bg_bright_blue$() + DIM AS STRING sout + sout$ = CHR$(ANSI.ESC) + "[104;1m" + IF GJ_LIB_ANSI_EMU THEN COLOR ,9 + IF GJ_LIB_ANSI_OUTPUT THEN ANSI.bg_bright_blue$ = sout$ + END FUNCTION + + + '' + ' Set foreground color to green + ' + ' @return STRING with ANSI escape codes for setting color + ' + FUNCTION ANSI.fg_green$() + DIM AS STRING sout + sout$ = CHR$(ANSI.ESC) + "[0;32m" + IF GJ_LIB_ANSI_EMU THEN COLOR 2 + IF GJ_LIB_ANSI_OUTPUT THEN ANSI.fg_green$ = sout$ + END FUNCTION + + + '' + ' Set foreground color to bright green + ' + ' @return STRING with ANSI escape codes for setting color + ' + FUNCTION ANSI.fg_bright_green$() + DIM AS STRING sout + sout$ = CHR$(ANSI.ESC) + "[32;1m" + IF GJ_LIB_ANSI_EMU THEN COLOR 10 + IF GJ_LIB_ANSI_OUTPUT THEN ANSI.fg_bright_green$ = sout$ + END FUNCTION + + '' + ' Set background color to green + ' + ' @return STRING with ANSI escape codes for setting color + ' + FUNCTION ANSI.bg_green$() + DIM AS STRING sout + sout$ = CHR$(ANSI.ESC) + "[42m" + IF GJ_LIB_ANSI_EMU THEN COLOR ,2 + IF GJ_LIB_ANSI_OUTPUT THEN ANSI.bg_green$ = sout$ + END FUNCTION + + + '' + ' Set background color to bright green + ' + ' @return STRING with ANSI escape codes for setting color + ' + FUNCTION ANSI.bg_bright_green$() + DIM AS STRING sout + sout$ = CHR$(ANSI.ESC) + "[102;1m" + IF GJ_LIB_ANSI_EMU THEN COLOR ,10 + IF GJ_LIB_ANSI_OUTPUT THEN ANSI.bg_bright_green$ = sout$ + END FUNCTION + + + '' + ' Set foreground color to cyan + ' + ' @return STRING with ANSI escape codes for setting color + ' + FUNCTION ANSI.fg_cyan$() + DIM AS STRING sout + sout$ = CHR$(ANSI.ESC) + "[0;36m" + IF GJ_LIB_ANSI_EMU THEN COLOR 3 + IF GJ_LIB_ANSI_OUTPUT THEN ANSI.fg_cyan$ = sout$ + END FUNCTION + + + '' + ' Set foreground color to bright_cyan + ' + ' @return STRING with ANSI escape codes for setting color + ' + FUNCTION ANSI.fg_bright_cyan$() + DIM AS STRING sout + sout$ = CHR$(ANSI.ESC) + "[36;1m" + IF GJ_LIB_ANSI_EMU THEN COLOR 11 + IF GJ_LIB_ANSI_OUTPUT THEN ANSI.fg_bright_cyan$ = sout$ + END FUNCTION + + + '' + ' Set background color to cyan + ' + ' @return STRING with ANSI escape codes for setting color + ' + FUNCTION ANSI.bg_cyan$() + DIM AS STRING sout + sout$ = CHR$(ANSI.ESC) + "[46m" + IF GJ_LIB_ANSI_EMU THEN COLOR ,3 + IF GJ_LIB_ANSI_OUTPUT THEN ANSI.bg_cyan$ = sout$ + END FUNCTION + + + '' + ' Set background color to bright cyan + ' + ' @return STRING with ANSI escape codes for setting color + ' + FUNCTION ANSI.bg_bright_cyan$() + DIM AS STRING sout + sout$ = CHR$(ANSI.ESC) + "[106;1m" + IF GJ_LIB_ANSI_EMU THEN COLOR ,11 + IF GJ_LIB_ANSI_OUTPUT THEN ANSI.bg_bright_cyan$ = sout$ + END FUNCTION + + + '' + ' Set foreground color to red + ' + ' @return STRING with ANSI escape codes for setting color + ' + FUNCTION ANSI.fg_red$() + DIM AS STRING sout + sout$ = CHR$(ANSI.ESC) + "[0;31m" + IF GJ_LIB_ANSI_EMU THEN COLOR 4 + IF GJ_LIB_ANSI_OUTPUT THEN ANSI.fg_red$ = sout$ + END FUNCTION + + + '' + ' Set foreground color to bright red + ' + ' @return STRING with ANSI escape codes for setting color + ' + FUNCTION ANSI.fg_bright_red$() + DIM AS STRING sout + sout$ = CHR$(ANSI.ESC) + "[31;1m" + IF GJ_LIB_ANSI_EMU THEN COLOR 12 + IF GJ_LIB_ANSI_OUTPUT THEN ANSI.fg_bright_red$ = sout$ + END FUNCTION + + + '' + ' Set background color to red + ' + ' @return STRING with ANSI escape codes for setting color + ' + FUNCTION ANSI.bg_red$() + DIM AS STRING sout + sout$ = CHR$(ANSI.ESC) + "[41m" + IF GJ_LIB_ANSI_EMU THEN COLOR ,4 + IF GJ_LIB_ANSI_OUTPUT THEN ANSI.bg_red$ = sout$ + END FUNCTION + + + '' + ' Set background color to bright red + ' + ' @return STRING with ANSI escape codes for setting color + ' + FUNCTION ANSI.bg_bright_red$() + DIM AS STRING sout + sout$ = CHR$(ANSI.ESC) + "[101;1m" + IF GJ_LIB_ANSI_EMU THEN COLOR ,12 + IF GJ_LIB_ANSI_OUTPUT THEN ANSI.bg_bright_red$ = sout$ + END FUNCTION + + + '' + ' Set foreground color to magenta + ' + ' @return STRING with ANSI escape codes for setting color + ' + FUNCTION ANSI.fg_magenta$() + DIM AS STRING sout + sout$ = CHR$(ANSI.ESC) + "[0;35m" + IF GJ_LIB_ANSI_EMU THEN COLOR 5 + IF GJ_LIB_ANSI_OUTPUT THEN ANSI.fg_magenta$ = sout$ + END FUNCTION + + + '' + ' Set foreground color to bright magenta + ' + ' @return STRING with ANSI escape codes for setting color + ' + FUNCTION ANSI.fg_bright_magenta$() + DIM AS STRING sout + sout$ = CHR$(ANSI.ESC) + "[35;1m" + IF GJ_LIB_ANSI_EMU THEN COLOR 13 + IF GJ_LIB_ANSI_OUTPUT THEN ANSI.fg_bright_magenta$ = sout$ + END FUNCTION + + + '' + ' Set background color to magenta + ' + ' @return STRING with ANSI escape codes for setting color + ' + FUNCTION ANSI.bg_magenta$() + DIM AS STRING sout + sout$ = CHR$(ANSI.ESC) + "[45m" + IF GJ_LIB_ANSI_EMU THEN COLOR ,5 + IF GJ_LIB_ANSI_OUTPUT THEN ANSI.bg_magenta$ = sout$ + END FUNCTION + + + '' + ' Set background color to bright magenta + ' + ' @return STRING with ANSI escape codes for setting color + ' + FUNCTION ANSI.bg_bright_magenta$() + DIM AS STRING sout + sout$ = CHR$(ANSI.ESC) + "[105;1m" + IF GJ_LIB_ANSI_EMU THEN COLOR ,13 + IF GJ_LIB_ANSI_OUTPUT THEN ANSI.bg_bright_magenta$ = sout$ + END FUNCTION + + + '' + ' Set foreground color to yellow + ' + ' @return STRING with ANSI escape codes for setting color + ' + FUNCTION ANSI.fg_yellow$() + DIM AS STRING sout + sout$ = CHR$(ANSI.ESC) + "[0;33m" + IF GJ_LIB_ANSI_EMU THEN COLOR 6 + IF GJ_LIB_ANSI_OUTPUT THEN ANSI.fg_yellow$ = sout$ + END FUNCTION + + + '' + ' Set foreground color to bright yellow + ' + ' @return STRING with ANSI escape codes for setting color + ' + FUNCTION ANSI.fg_bright_yellow$() + DIM AS STRING sout + sout$ = CHR$(ANSI.ESC) + "[33;1m" + IF GJ_LIB_ANSI_EMU THEN COLOR 14 + IF GJ_LIB_ANSI_OUTPUT THEN ANSI.fg_bright_yellow$ = sout$ + END FUNCTION + + + '' + ' Set background color to yellow + ' + ' @return STRING with ANSI escape codes for setting color + ' + FUNCTION ANSI.bg_yellow$() + DIM AS STRING sout + sout$ = CHR$(ANSI.ESC) + "[43m" + IF GJ_LIB_ANSI_EMU THEN COLOR ,6 + IF GJ_LIB_ANSI_OUTPUT THEN ANSI.bg_yellow$ = sout$ + END FUNCTION + + + '' + ' Set background color to bright yellow + ' + ' @return STRING with ANSI escape codes for setting color + ' + FUNCTION ANSI.bg_bright_yellow$() + DIM AS STRING sout + sout$ = CHR$(ANSI.ESC) + "[103;1m" + IF GJ_LIB_ANSI_EMU THEN COLOR ,14 + IF GJ_LIB_ANSI_OUTPUT THEN ANSI.bg_bright_yellow$ = sout$ + END FUNCTION + + + '' + ' Set foreground color to white + ' + ' @return STRING with ANSI escape codes for setting color + ' + FUNCTION ANSI.fg_white$() + DIM AS STRING sout + sout$ = CHR$(ANSI.ESC) + "[0;37m" + IF GJ_LIB_ANSI_EMU THEN COLOR 7 + IF GJ_LIB_ANSI_OUTPUT THEN ANSI.fg_white$ = sout$ + END FUNCTION + + + '' + ' Set foreground color to bright white + ' + ' @return STRING with ANSI escape codes for setting color + ' + FUNCTION ANSI.fg_bright_white$() + DIM AS STRING sout + sout$ = CHR$(ANSI.ESC) + "[37;1m" + IF GJ_LIB_ANSI_EMU THEN COLOR 15 + IF GJ_LIB_ANSI_OUTPUT THEN ANSI.fg_bright_white$ = sout$ + END FUNCTION + + + '' + ' Set background color to white + ' + ' @return STRING with ANSI escape codes for setting color + ' + FUNCTION ANSI.bg_white$() + DIM AS STRING sout + sout$ = CHR$(ANSI.ESC) + "[47m" + IF GJ_LIB_ANSI_EMU THEN COLOR ,7 + IF GJ_LIB_ANSI_OUTPUT THEN ANSI.bg_white$ = sout$ + END FUNCTION + + + '' + ' Set background color to bright white + ' + ' @return STRING with ANSI escape codes for setting color + ' + FUNCTION ANSI.bg_bright_white$() + DIM AS STRING sout + sout$ = CHR$(ANSI.ESC) + "[107;1m" + IF GJ_LIB_ANSI_EMU THEN COLOR ,15 + IF GJ_LIB_ANSI_OUTPUT THEN ANSI.bg_bright_white$ = sout$ + END FUNCTION + + + '' + ' Sets text color foreground using 256 color mode + ' + ' @param INTEGER c% Color number (see link for color table) + ' @return STRING with ANSI escape codes for setting color + ' + FUNCTION ANSI.fg_256$(c%) + DIM AS STRING sout + sout$ = CHR$(ANSI.ESC) + "[38;5" + sout$ = sout$ + ";" + _TRIM$(STR$(c%)) + sout$ = sout$ + "m" + IF GJ_LIB_ANSI_EMU THEN COLOR c%, ANSI.bg_color& + IF GJ_LIB_ANSI_OUTPUT THEN ANSI.fg_256$ = sout$ + END FUNCTION + + + '' + ' Sets text color background using 256 color mode + ' + ' @param INTEGER c% Color number (see link for color table) + ' @return STRING with ANSI escape codes for setting color + ' + FUNCTION ANSI.bg_256$(c%) + DIM AS STRING sout + sout$ = CHR$(ANSI.ESC) + "[48;5" + sout$ = sout$ + ";" + _TRIM$(STR$(c%)) + sout$ = sout$ + "m" + IF GJ_LIB_ANSI_EMU THEN COLOR ANSI.fg_color& ,c% + IF GJ_LIB_ANSI_OUTPUT THEN ANSI.bg_256$ = sout$ + END FUNCTION + + + '' + ' Sets text color foreground using RGB 8-bit mode + ' + ' @param INTEGER r% Red value 0-255 + ' @param INTEGER g% Green value 0-255 + ' @param INTEGER b% Blue value 0-255 + ' @return STRING with ANSI escape codes for setting color + ' + FUNCTION ANSI.fg_rgb$(r%, g%, b%) + DIM AS STRING sout + sout$ = CHR$(ANSI.ESC) + "[38;2" + sout$ = sout$ + ";" + _TRIM$(STR$(r%)) + sout$ = sout$ + ";" + _TRIM$(STR$(g%)) + sout$ = sout$ + ";" + _TRIM$(STR$(b%)) + sout$ = sout$ + "m" + IF GJ_LIB_ANSI_EMU THEN COLOR _RGB(r%, g%, b%), ANSI.bg_color& + IF GJ_LIB_ANSI_OUTPUT THEN ANSI.fg_rgb$ = sout$ + END FUNCTION + + + '' + ' Sets text color background using RGB 8-bit mode + ' + ' @param INTEGER r% Red value 0-255 + ' @param INTEGER g% Green value 0-255 + ' @param INTEGER b% Blue value 0-255 + ' @return STRING with ANSI escape codes for setting color + ' + FUNCTION ANSI.bg_rgb$(r%, g%, b%) + DIM AS STRING sout + sout$ = CHR$(ANSI.ESC) + "[48;2" + sout$ = sout$ + ";" + _TRIM$(STR$(r%)) + sout$ = sout$ + ";" + _TRIM$(STR$(g%)) + sout$ = sout$ + ";" + _TRIM$(STR$(b%)) + sout$ = sout$ + "m" + IF GJ_LIB_ANSI_EMU THEN COLOR ANSI.fg_color& ,_RGB(r%, g%, b%) + IF GJ_LIB_ANSI_OUTPUT THEN ANSI.bg_rgb$ = sout$ + END FUNCTION + $END IF + $IF GJ_LIB_STRINGS_INC_BM = UNDEFINED THEN + '' + ' GRYMMJACK'S STRINGS LIB + ' + ' Some commonly used functions that I missed in QB64 coming from PHP + ' + ' @author Rick Christy + ' @uses STRINGS.BI + ' + $LET GJ_LIB_STRINGS_INC_BM = 1 + $LET DEBUGGING = 1 + + + + '' + ' Returns a string if n is true or false + ' + ' @param INTEGER n% to check + ' @param STRING if_false$ string + ' @param STRING if_true$ string + ' @return STRING representing true or false + ' + FUNCTION STR.bool$(n%, if_true$, if_false$) + IF n% = 0 THEN + STR.bool$ = if_false$ + ELSEIF n% = -1 THEN + STR.bool$ = if_true$ + END IF + END FUNCTION + + + '' + ' Check if string is a sentence: ends in .!? + ' + ' @param STRING s$ to check + ' @return INTEGER -1 if true, 0 if false + ' + FUNCTION STR.is_sentence%(s$) + DIM last_char AS STRING + last_char$ = RIGHT$(s$, 1) + IF last_char$ = "." OR last_char$ = "!" OR last_char$ = "?" THEN + STR.is_sentence% = -1 + ELSE + STR.is_sentence% = 0 + END IF + END FUNCTION + + + '' + ' Check if string is truthy: not null or -1 + ' + ' @param STRING s$ to check + ' @return INTEGER -1 if true, 0 if false + ' + FUNCTION STR.is_truthy%(s$) + IF s$ <> "" OR s$ = "-1" THEN + STR.is_truthy% = -1 + EXIT FUNCTION + END IF + STR.is_truthy% = 0 + END FUNCTION + + + '' + ' Check if string is falsy: null or 0 + ' + ' @param STRING s$ to check + ' @return INTEGER -1 if true, 0 if false + ' + FUNCTION STR.is_falsey%(s$) + IF s$ = "" OR s$ = "0" THEN + STR.is_falsey% = -1 + EXIT FUNCTION + END IF + STR.is_falsey% = 0 + END FUNCTION + + + '' + ' Check if string is null + ' + ' @param STRING s$ to check + ' @return INTEGER -1 if true, 0 if false + ' + FUNCTION STR.is_empty%(s$) + IF s$ = "" THEN + STR.is_empty% = -1 + EXIT FUNCTION + END IF + STR.is_empty% = 0 + END FUNCTION + + + '' + ' Check if string consists purely of space and tab characters + ' + ' @param STRING s$ to check + ' @return INTEGER -1 if true, 0 if false + ' + FUNCTION STR.is_blank%(s$) + DIM AS INTEGER i + IF s$ = "" THEN EXIT FUNCTION + i% = 1 + DO: + IF GJ_LIB_isblank(ASC(s$, i%)) = 0 THEN + STR.is_blank% = 0 + EXIT FUNCTION + END IF + i% = i% + 1 + LOOP UNTIL i% = LEN(s$) + 1 + STR.is_blank% = -1 + END FUNCTION + + + '' + ' Check if string consists purely of hexadecimal characters: + ' ASCII 0-9 A-F + ' + ' @param STRING s$ to check + ' @return INTEGER -1 if true, 0 if false + ' + FUNCTION STR.is_hexadecimal%(s$) + DIM AS INTEGER i + IF s$ = "" THEN EXIT FUNCTION + i% = 1 + DO: + IF GJ_LIB_isxdigit(ASC(s$, i%)) = 0 THEN + STR.is_hexadecimal% = 0 + EXIT FUNCTION + END IF + i% = i% + 1 + LOOP UNTIL i% = LEN(s$) + 1 + STR.is_hexadecimal% = -1 + END FUNCTION + + + '' + ' Check if string consists purely of control characters: + ' ASCII 0-31 + ' + ' @param STRING s$ to check + ' @return INTEGER -1 if true, 0 if false + ' + FUNCTION STR.is_control_chars%(s$) + DIM AS INTEGER i + IF s$ = "" THEN EXIT FUNCTION + i% = 1 + DO: + IF ASC(s$, i%) = 0 OR ASC(s$, i%) > 31 THEN + STR.is_control_chars% = 0 + EXIT FUNCTION + END IF + i% = i% + 1 + LOOP UNTIL i% = LEN(s$) + 1 + STR.is_control_chars% = -1 + END FUNCTION + + + '' + ' Check if string consists purely of punctuation characters: + ' !"#$%&'()*+,-./:;<=>?@[\]^_`{|}~ + ' + ' @param STRING s$ to check + ' @return INTEGER -1 if true, 0 if false + ' + FUNCTION STR.is_punctuation%(s$) + DIM AS INTEGER i + IF s$ = "" THEN EXIT FUNCTION + i% = 1 + DO: + IF GJ_LIB_ispunct%(ASC(s$, i%)) = 0 THEN + STR.is_punctuation% = 0 + EXIT FUNCTION + END IF + i% = i% + 1 + LOOP UNTIL i% = LEN(s$) + 1 + STR.is_punctuation% = -1 + END FUNCTION + + + '' + ' Check if string consists purely of graphic characters: + ' it is either a number (0123456789), + ' an uppercase letter (ABCDEFGHIJKLMNOPQRSTUVWXYZ), + ' a lowercase letter (abcdefghijklmnopqrstuvwxyz), + ' or a punctuation character(!"#$%&'()*+,-./:;<=>?@[\]^_`{|}~), + ' or any graphical character specific to the current C locale. + ' + ' @param STRING s$ to check + ' @return INTEGER -1 if true, 0 if false + ' + FUNCTION STR.is_graphical%(s$) + DIM AS INTEGER i + IF s$ = "" THEN EXIT FUNCTION + i% = 1 + DO: + IF GJ_LIB_isgraph%(ASC(s$, i%)) = 0 THEN + STR.is_graphical% = 0 + EXIT FUNCTION + END IF + i% = i% + 1 + LOOP UNTIL i% = LEN(s$) + 1 + STR.is_graphical% = -1 + END FUNCTION + + + '' + ' Check if string consists purely of printable characters: + ' ASCII: &H20 (" ") to &H7E (~) + ' + ' @param STRING s$ to check + ' @return INTEGER -1 if true, 0 if false + ' + FUNCTION STR.is_printable%(s$) + DIM AS INTEGER i + IF s$ = "" THEN EXIT FUNCTION + i% = 1 + DO: + IF GJ_LIB_isprint%(ASC(s$, i%)) = 0 THEN + STR.is_printable% = 0 + EXIT FUNCTION + END IF + i% = i% + 1 + LOOP UNTIL i% = LEN(s$) + 1 + STR.is_printable% = -1 + END FUNCTION + + + '' + ' Check if string consists purely of space characters: + ' space, formfeed, newline, return, tab, vertical tab + ' + ' @param STRING s$ to check + ' @return INTEGER -1 if true, 0 if false + ' + FUNCTION STR.is_white_space%(s$) + DIM AS INTEGER i + IF s$ = "" THEN EXIT FUNCTION + i% = 1 + DO: + IF GJ_LIB_isspace%(ASC(s$, i%)) = 0 THEN + STR.is_white_space% = 0 + EXIT FUNCTION + END IF + i% = i% + 1 + LOOP UNTIL i% = LEN(s$) + 1 + STR.is_white_space% = -1 + END FUNCTION + + + '' + ' Check if string consists purely of lower case characters + ' + ' @param STRING s$ to check + ' @return INTEGER -1 if true, 0 if false + ' + FUNCTION STR.is_lower_case%(s$) + DIM AS INTEGER i + IF s$ = "" THEN EXIT FUNCTION + i% = 1 + DO: + IF GJ_LIB_islower%(ASC(s$, i%)) = 0 THEN + STR.is_lower_case% = 0 + EXIT FUNCTION + END IF + i% = i% + 1 + LOOP UNTIL i% = LEN(s$) + 1 + STR.is_lower_case% = -1 + END FUNCTION + + + '' + ' Check if string consists purely of upper case characters + ' + ' @param STRING s$ to check + ' @return INTEGER -1 if true, 0 if false + ' + FUNCTION STR.is_upper_case%(s$) + DIM AS INTEGER i + IF s$ = "" THEN EXIT FUNCTION + i% = 1 + DO: + IF GJ_LIB_isupper%(ASC(s$, i%)) = 0 THEN + STR.is_upper_case% = 0 + EXIT FUNCTION + END IF + i% = i% + 1 + LOOP UNTIL i% = LEN(s$) + 1 + STR.is_upper_case% = -1 + END FUNCTION + + + '' + ' Check if string consists purely of numbers + ' + ' @param STRING s$ to check + ' @return INTEGER -1 if true, 0 if false + ' + FUNCTION STR.is_numeric%(s$) + DIM AS INTEGER i + IF s$ = "" THEN EXIT FUNCTION + i% = 1 + DO: + IF GJ_LIB_isdigit%(ASC(s$, i%)) = 0 THEN + STR.is_numeric% = 0 + EXIT FUNCTION + END IF + i% = i% + 1 + LOOP UNTIL i% = LEN(s$) + 1 + STR.is_numeric% = -1 + END FUNCTION + + + '' + ' Check if string consists purely of alphabetical characters + ' + ' @param STRING s$ to check + ' @return INTEGER -1 if true, 0 if false + ' + FUNCTION STR.is_alpha%(s$) + DIM AS INTEGER i + IF s$ = "" THEN EXIT FUNCTION + i% = 1 + DO: + IF GJ_LIB_isalpha%(ASC(s$, i%)) = 0 THEN + STR.is_alpha% = 0 + EXIT FUNCTION + END IF + i% = i% + 1 + LOOP UNTIL i% = LEN(s$) + 1 + STR.is_alpha% = -1 + END FUNCTION + + + '' + ' Check if string consists purely of alphabet characters or numbers + ' + ' @param STRING s$ to check + ' @return INTEGER -1 if true, 0 if false + ' + FUNCTION STR.is_alpha_numeric%(s$) + DIM AS INTEGER i + IF s$ = "" THEN EXIT FUNCTION + i% = 1 + DO: + IF GJ_LIB_isalnum%(ASC(s$, i%)) = 0 THEN + STR.is_alpha_numeric% = 0 + EXIT FUNCTION + END IF + i% = i% + 1 + LOOP UNTIL i% = LEN(s$) + 1 + STR.is_alpha_numeric% = -1 + END FUNCTION + + + '' + ' Implodes a string array into a string using delimiter as glue + ' + ' @param STRING ARRAY arr$() to implode from + ' @param STRING delim$ Delimiter to glue the array parts together with + ' @return STRING of array parts glued together with delimiter + ' + FUNCTION STR.implode$(arr$(), delim$) + DIM AS STRING res + DIM AS INTEGER lb, ub, i + res$ = "" + lb% = LBOUND(arr$) : ub% = UBOUND(arr$) + FOR i% = lb% TO ub% + IF i% + 1 <= ub% THEN + res$ = res$ + arr$(i) + delim$ + ELSE + res$ = res$ + arr$(i) + END IF + NEXT i% + STR.implode$ = res$ + END FUNCTION + + + '' + ' Explodes a string into an array of strings using a delimiter + ' + ' If the delimiter is not found, returns the target as dest$(0) + ' + ' @param STRING target$ to explode + ' @param STRING delim$ delimiter + ' @param STRING ARRAY dest$() to explode into + ' @param INTEGER numParts% the number of strings in the array + ' + SUB STR.explode(target$, delim$, dest$(), numParts%) + DIM AS INTEGER length, delimLen, numFound, i + length% = LEN(target$) : delimLen% = LEN(delim$) : numFound% = 0 + DIM delimsPos(length%) AS INTEGER + IF length% = 0 THEN EXIT SUB + + CALL STR.find_pos(target$, delim$, delimsPos%(), numFound%) + + IF numFound% <= 0 THEN + numParts% = 0 + dest$(0) = target$ + ELSE + REDIM _PRESERVE delimsPos%(numFound% - 1) + IF numFound% = 1 THEN + numParts% = 1 + dest$(0) = LEFT$(target$, delimsPos%(0) - delimLen%) + dest$(1) = MID$(target$, delimsPos%(0) + delimLen%) + ELSEIF numFound% > 1 THEN + dest$(0) = LEFT$(target$, delimsPos%(0) - delimLen%) + FOR i% = 1 TO numFound% + IF i% + 1 <= numFound% THEN + dest$(i%) = MID$( _ + target$, _ + delimsPos%(i% - 1) + delimLen%, _ + delimsPos%(i%) - delimsPos%(i% - 1) - delimLen% _ + ) + END IF + NEXT i% + dest$(numFound%) = MID$( _ + target$, delimsPos%(numFound% - 1) + delimLen% _ + ) + numParts% = numFound% + END IF + END IF + END SUB + + + '' + ' Searches for strings inside of strings and fills array with found positions + ' + ' @param STRING target$ to search + ' @param STRING search$ for in target + ' @param INTEGER ARRAY arrFound%() populate with positions search found + ' @param INTEGER numFound% times search found a match + ' + SUB STR.find_pos(target$, search$, arrFound%(), numFound%) + DIM AS INTEGER length, found, x, i, ub, searchLen + length% = LEN(target$) : found% = -1 : x% = 0: i% = 0 + ub% = UBOUND(arrFound%) + searchLen% = LEN(search$) + DO WHILE i% <= length% + found% = INSTR(i%, target$, search$) + IF found% > 0 AND x% <= ub% THEN + arrFound%(x%) = found% + i% = found% + searchLen% + x% = x% + 1 + ELSE + i% = i% + 1 + END IF + LOOP + numFound% = x% + END SUB + + + '' + ' Insert a string into another string at position + ' + ' @param STRING s$ to insert into + ' @param STRING ins$ insert + ' @param INTEGER p% position to insert + ' @return STRING with insertion + ' + FUNCTION STR.insert$(s$, ins$, p%) + IF p% < LEN(s$) AND ins$ <> "" THEN + IF p% = 0 THEN + STR.insert$ = ins$ + RIGHT$(s$, LEN(s$) + LEN(ins$) - 1) + ELSE + STR.insert$ = LEFT$(s$, p%+1) + ins$ + RIGHT$(s$, LEN(s$) - p%-1) + END IF + ELSE + STR.insert$ = s$ + END IF + END FUNCTION + + + '' + ' Remove a string from a string + ' + ' @param STRING s$ to remove from + ' @param STRING del$ to delete + ' @param INTEGER count% times to remove + ' @return STRING with del$ removed + ' + FUNCTION STR.remove$(s$, del$, count%) + DIM AS INTEGER p + IF count% = -1 THEN + DO + p% = INSTR(s$, del$) + s$ = STR.del$(s$, del$) + LOOP UNTIL p% = 0 + ELSE + DO + p% = INSTR(s$, del$) + s$ = STR.del$(s$, del$) + count% = count% - 1 + LOOP UNTIL p% = 0 OR count% = 0 + END IF + STR.remove$ = s$ + END FUNCTION + + + '' + ' Delete a string from a string once (helper for STR.remove$) + ' + ' @param STRING s$ to delete from + ' @param STRING del$ to delete + ' @return STRING with del$ deleted + ' + FUNCTION STR.del$(s$, del$) + DIM AS INTEGER i + i% = INSTR(s$, del$) + IF i% THEN + STR.del$ = LEFT$(s$, i%-1) + RIGHT$(s$, LEN(s$) - (i% + LEN(del$))+1) + ELSE + STR.del$ = s$ + END IF + END FUNCTION + + + '' + ' Replaces a string with another string inside a string + ' + ' @param STRING s$ to replace within + ' @param STRING search$ + ' @param STRING rep$ string to replace search with if found + ' @param INTEGER count% number of times to replace + ' @return STRING with replacements + ' + FUNCTION STR.replace$(s$, search$, rep$, count%) + DIM AS INTEGER p + IF count% = -1 THEN + DO + p% = INSTR(s$, search$) + s$ = STR.rep$(s$, search$, rep$) + LOOP UNTIL p% = 0 + ELSE + DO + p% = INSTR(s$, search$) + s$ = STR.rep$(s$, search$, rep$) + count% = count% - 1 + LOOP UNTIL p% = 0 OR count% = 0 + END IF + STR.replace$ = s$ + END FUNCTION + + + '' + ' Reverses a string + ' + ' @param STRING s$ to reverse + ' @return STRING reversed string + ' + FUNCTION STR.reverse$(s$) + DIM AS INTEGER i, l + DIM AS STRING res + res$ = "" + l% = LEN(s$) + IF l% = 0 THEN EXIT FUNCTION + FOR i% = l% TO 1 STEP -1 + res$ = res$ + CHR$(ASC(s$, i%)) + NEXT i% + STR.reverse$ = res$ + END FUNCTION + + + '' + ' Shuffles (randomizes) the characters in a string + ' + ' @param STRING s$ string to reverse + ' @return STRING shuffled string + ' + FUNCTION STR.shuffle$(s$) + DIM AS INTEGER r + DIM AS STRING c, ls, rs, res + IF LEN(s$) = 0 THEN EXIT FUNCTION + RANDOMIZE TIMER + DO + r% = INT(RND * LEN(s$) + 1) ' random pos in diminishing string + c$ = MID$(s$, r%, 1) ' random char at pos from diminishing string + ls$ = MID$(s$, 1, r% - 1) ' left side of diminishing string sans c$ + rs$ = MID$(s$, r% + 1) ' right side of diminishing string sans c$ + s$ = ls$ + rs$ ' diminish the string (remove c$) + res$ = res$ + c$ ' build the returned string + LOOP UNTIL LEN(s$) = 0 + STR.shuffle$ = res$ + END FUNCTION + + + '' + ' Pads both sides of a string with num% chars + ' + ' @param STRING s$ string to pad + ' @param STRING char$ character to use for padding + ' @param INTEGER num% number of characters to pad to + ' @return STRING padded at the end + FUNCTION STR.pad_both$(s$, char$, num%) + STR.pad_both$ = STR.pad_end$(STR.pad_start$(s$, char$, num%), char$, num%) + END FUNCTION + + + '' + ' Pads the end of a string with num% chars + ' + ' @param STRING s$ string to pad + ' @param STRING char$ character to use for padding + ' @param INTEGER num% number of characters to pad to + ' @return STRING padded at the end + FUNCTION STR.pad_end$(s$, char$, num%) + STR.pad_end$ = s$ + STRING$(num%, char$) + END FUNCTION + + + '' + ' Repeats a string num times + ' + ' @param STRING s$ string to repeat + ' @param INTEGER num% number of times to repeat + ' @return STRING repeated + FUNCTION STR.repeat$(s$, num%) + DIM i AS INTEGER + DIM res AS STRING + res$ = "" + FOR i% = 1 TO num% + res$ = res$ + s$ + NEXT i% + STR.repeat$ = res$ + END FUNCTION + + + '' + ' Determines if a string starts with another string + ' + ' @param STRING s$ string to check + ' @param INTEGER chars$ chars to check if string starts with + ' @return INTEGER -1 if starts with 0 if not + FUNCTION STR.starts_with%(s$, chars$) + STR.starts_with% = (LEFT$(s$, LEN(chars$)) = chars$) + END FUNCTION + + + '' + ' Determines if a string ends with another string + ' + ' @param STRING s$ string to check + ' @param INTEGER chars$ chars to check if string ends with + ' @return INTEGER -1 if ends with 0 if not + FUNCTION STR.ends_with%(s$, chars$) + STR.ends_with% = (RIGHT$(s$, LEN(chars$)) = chars$) + END FUNCTION + + + '' + ' Pads the start of a string with num% chars + ' + ' @param STRING s$ string to pad + ' @param STRING char$ character to use for padding + ' @param INTEGER num% number of characters to pad to + ' @return STRING padded at the end + FUNCTION STR.pad_start$(s$, char$, num%) + STR.pad_start$ = STRING$(num%, char$) + s$ + END FUNCTION + + + '' + ' Replaces a string with another string once (helper for STR.replace$) + ' + ' @param STRING s$ to replace within + ' @param STRING search$ + ' @param STRING rep$ string to replace search with if found + ' @return STRING with replacement + ' + FUNCTION STR.rep$(s$, search$, rep$) + DIM AS INTEGER p + p% = INSTR(s$, search$) + IF p% THEN + s$ = LEFT$(s$, p%-1) + RIGHT$(s$, LEN(s$) - p% - LEN(search$)+1) + STR.rep$ = LEFT$(s$, p%-1) + rep$ + RIGHT$(s$, LEN(s$) - p%+1) + ELSE + STR.rep$ = s$ + END IF + END FUNCTION + + + '' + ' Returns part of a string from start pos. to end pos. + ' NOTE: This is different than MID$ as MID$ specifies a start and a length, + ' NOT an end position. + ' + ' @param STRING s$ to slice from + ' @param INTEGER startPos% to start slice from + ' @param INTEGER endPos% to end slice from + ' @return STRING of sliced portion of original stright + ' + FUNCTION STR.slice_pos$(s$, startPos%, endPos%) + IF startPos% <= 0 THEN + startPos% = 1 + END IF + IF endPos% > 0 THEN + STR.slice_pos$ = MID$(s$, startPos%, endPos%-startPos%) + ELSE + STR.slice_pos$ = MID$(s$, startPos%) + END IF + END FUNCTION + + + '' + ' Returns a space trimmed _UNSIGNED _BYTE as a string + ' + ' @param _UNSIGNED _BYTE n~%% number to return + ' @return STRING space trimmed number + ' + FUNCTION STR.ub$(n~%%) + STR.ub$ = _TRIM$(STR$(n~%%)) + END FUNCTION + + + '' + ' Returns a space trimmed _UNSIGNED INTEGER as a string + ' + ' @param _UNSIGNED INTEGER n~% number to return + ' @return STRING space trimmed number + ' + FUNCTION STR.ui$(n~%) + STR.ui$ = _TRIM$(STR$(n~%)) + END FUNCTION + + + '' + ' Returns a space trimmed _UNSIGNED LONG as a string + ' + ' @param _UNSIGNED LONG n~& number to return + ' @return STRING space trimmed number + ' + FUNCTION STR.ul$(n~&) + STR.ul$ = _TRIM$(STR$(n~&)) + END FUNCTION + + + '' + ' Returns a space trimmed _BYTE as a string + ' + ' @param _BYTE n~% number to return + ' @return STRING space trimmed number + ' + FUNCTION STR.b$(n%%) + STR.b$ = _TRIM$(STR$(n%%)) + END FUNCTION + + + '' + ' Returns a space trimmed INTEGER as a string + ' + ' @param INTEGER n% number to return + ' @return STRING space trimmed number + ' + FUNCTION STR.i$(n%) + STR.i$ = _TRIM$(STR$(n%)) + END FUNCTION + + + '' + ' Returns a space trimmed LONG as a string + ' + ' @param LONG n& number to return + ' @return STRING space trimmed number + ' + FUNCTION STR.l$(n&) + STR.l$ = _TRIM$(STR$(n&)) + END FUNCTION + + + '' + ' Returns a space trimmed SINGLE as a string + ' + ' @param SINGLE n! number to return + ' @return STRING space trimmed number + ' + FUNCTION STR.s$(n!) + STR.s$ = _TRIM$(STR$(n!)) + END FUNCTION + + + '' + ' Returns a space trimmed DOUBLE as a string + ' + ' @param DOUBLE n& number to return + ' @return STRING space trimmed number + ' + FUNCTION STR.d$(n#) + STR.d$ = _TRIM$(STR$(n#)) + END FUNCTION + + + '' + ' Returns a space trimmed _FLOAT as a string + ' + ' @param _FLOAT n& number to return + ' @return STRING space trimmed number + ' + FUNCTION STR.f$(n##) + STR.f$ = _TRIM$(STR$(n##)) + END FUNCTION + $END IF + $IF GJ_LIB_PIPEPRINT_INC_BM = UNDEFINED THEN + '' + ' QB64_GJ_LIB + ' GRYMMJACK'S PIPEPRINT LIB + ' + ' Pipe (|) Print emulates Mystic BBS pipe parsing + ' + ' USAGE: + ' Insert '$INCLUDE:'path_to_GJ_LIB/PIPEPRINT/PIPEPRINT.BI' at the top of file + ' Insert '$INCLUDE:'path_to_GJ_LIB/PIPEPRINT/PIPEPRINT.BM' at the bottom of file + ' + ' @author Rick Christy + ' @uses PIPEPRINT.BI + ' @uses DICT/DICT.BM + ' @uses DUMP/DUMP.BM + ' @uses ANSI/ANSI.BM + ' @uses STRINGS/STRINGS.BM + ' + $LET GJ_LIB_PIPEPRINT_INC_BM = 1 + + + + '' + ' Parses pipe codes and returns ANSI (can emulate ANSI via QB) + ' + ' @param STRING s$ to parse + ' @return STRING parsed with pipe codes replaced by ANSI codes + ' + FUNCTION PIPEPRINT$(s$) + DIM AS INTEGER w, nums, p, ub, i, r, l, ansi_x, ansi_y + DIM AS STRING sout, code, args, find, spaces, txt, t, nopi, repl, char + w% = _WIDTH + ansi_x% = POS(0) : ansi_y% = CSRLIN + sout$ = s$ + + ' Reset the working variables (redim without _PRESERVE = erase $DYNAMIC) + NUM_PIPES_FOUND = 0 + REDIM PIPES_POSITIONS(MAX_PIPES) AS INTEGER + + ' Find the pipes + CALL STR.find_pos(s$, "|", PIPES_POSITIONS%(), NUM_PIPES_FOUND) + IF NUM_PIPES_FOUND = 0 THEN + PIPEPRINT$ = s$ + EXIT FUNCTION + ELSE + REDIM _PRESERVE PIPES_POSITIONS(NUM_PIPES_FOUND-1) AS INTEGER + ub% = UBOUND(PIPES_POSITIONS) + END IF + + ' Replace the pipe codes with ANSI codes + IF NUM_PIPES_FOUND THEN + FOR i% = 0 TO ub% + p% = PIPES_POSITIONS(i%) + code$ = MID$(s$, p%, 3) + args$ = "" : char$ = "" : txt$ = "" : t$ = "" : nums% = 0 + find$ = "" : repl$ = "" : spaces$ = "" : nopi$ = "" + SELECT CASE code$ + CASE "|[X", "|[Y", "|[A", "|[B", "|[C", "|[D", "|@D": + args$ = _TRIM$(MID$(s$, p% + 3, 2)) + nums% = ABS(VAL(args$)) + find$ = code$ + args$ + END SELECT + SELECT CASE code$ + CASE "|[X": repl$ = ANSI.move_column(nums%) + CASE "|[Y": repl$ = ANSI.locate(nums%, ansi_x%) + CASE "|[A": repl$ = ANSI.move_up(nums%) + CASE "|[B": repl$ = ANSI.move_down(nums%) + CASE "|[C": repl$ = ANSI.move_right(nums%) + CASE "|[D": repl$ = ANSI.move_left(nums%) + CASE "|@D": + IF args$ = "00" THEN + nums% = w% + END IF + char$ = MID$(s$, p% + 5, 1) + find$ = code$ + args$ + char$ + repl$ = STRING$(nums%, char$) + END SELECT + SELECT CASE code$ + CASE "|@R", "|@L", "|@C": + l% = INSTR(p%, s$, "{")+1 + r% = INSTR(p%, s$, "}") + txt$ = MID$(s$, l%, r%-l%) + find$ = code$ + "{" + txt$ + "}" + nopi$ = PIPESTRIP(txt$) + END SELECT + SELECT CASE code$ + CASE "|@R": + spaces$ = STRING$(w% - LEN(nopi$), " ") + repl$ = spaces$ + txt$ + repl$ = ANSI.move_column(1) + repl$ + CASE "|@L": + spaces$ = STRING$(w% - LEN(nopi$), " ") + repl$ = txt$ + spaces$ + repl$ = ANSI.move_column(1) + repl$ + CASE "|@C": + spaces$ = STRING$((w% - LEN(nopi$)) \ 2, " ") + repl$ = spaces$ + txt$ + spaces$ + repl$ = ANSI.move_column(1) + repl$ + END SELECT + SELECT CASE code$ + CASE "|[0", "|[1", "|[K", "|CL", "|CN", "|CY", "|CR", _ + "|PI", "|00", "|01", "|02", "|03", "|04", "|05", _ + "|06", "|07", "|08", "|09", "|10", "|11", "|12", _ + "|13", "|14", "|15", "|16", "|17", "|18", "|19", _ + "|20", "|21", "|22", "|23", "|24", "|25", "|26", _ + "|27", "|28", "|29", "|30", "|31": + find$ = code$ + END SELECT + SELECT CASE code$ + CASE "|[0": repl$ = ANSI.hide_cursor + CASE "|[1": repl$ = ANSI.show_cursor + CASE "|[K": repl$ = ANSI.erase_to_eol + CASE "|CL": repl$ = ANSI.erase_screen + CASE "|CN": repl$ = ANSI.mode_blinking + CASE "|CY": repl$ = ANSI.mode_blinking_reset + CASE "|CR": repl$ = ANSI.move_down(1) + ANSI.move_column(1) + CASE "|PI": repl$ = "|" + CASE "|00": repl$ = ANSI.fg_black + CASE "|01": repl$ = ANSI.fg_blue + CASE "|02": repl$ = ANSI.fg_green + CASE "|03": repl$ = ANSI.fg_cyan + CASE "|04": repl$ = ANSI.fg_red + CASE "|05": repl$ = ANSI.fg_magenta + CASE "|06": repl$ = ANSI.fg_yellow + CASE "|07": repl$ = ANSI.fg_white + CASE "|08": repl$ = ANSI.fg_bright_black + CASE "|09": repl$ = ANSI.fg_bright_blue + CASE "|10": repl$ = ANSI.fg_bright_green + CASE "|11": repl$ = ANSI.fg_bright_cyan + CASE "|12": repl$ = ANSI.fg_bright_red + CASE "|13": repl$ = ANSI.fg_bright_magenta + CASE "|14": repl$ = ANSI.fg_bright_yellow + CASE "|15": repl$ = ANSI.fg_bright_white + CASE "|16": repl$ = ANSI.bg_black + CASE "|17": repl$ = ANSI.bg_blue + CASE "|18": repl$ = ANSI.bg_green + CASE "|19": repl$ = ANSI.bg_cyan + CASE "|20": repl$ = ANSI.bg_red + CASE "|21": repl$ = ANSI.bg_magenta + CASE "|22": repl$ = ANSI.bg_yellow + CASE "|23": repl$ = ANSI.bg_white + CASE "|24": repl$ = ANSI.bg_bright_black + CASE "|25": repl$ = ANSI.bg_bright_blue + CASE "|26": repl$ = ANSI.bg_bright_green + CASE "|27": repl$ = ANSI.bg_bright_cyan + CASE "|28": repl$ = ANSI.bg_bright_red + CASE "|29": repl$ = ANSI.bg_bright_magenta + CASE "|30": repl$ = ANSI.bg_bright_yellow + CASE "|31": repl$ = ANSI.bg_bright_white + END SELECT + sout$ = STR.replace$(sout$, find$, repl$, 1) + NEXT i% + END IF + PIPEPRINT$ = sout$ + END FUNCTION + + + '' + ' Strips all pipe codes from a string + ' + ' @param STRING s$ to strip codes from + ' @return STRING with pipe codes stripped + ' + FUNCTION PIPESTRIP$(s$) + DIM AS INTEGER w, nums, p, ub, i, r, l + DIM AS STRING sout, code, args, find, spaces, txt, t, repl, char + sout$ = s$ + + ' Reset the working variables (redim without _PRESERVE = erase $DYNAMIC) + NUM_PIPES_STRIP_FOUND = 0 + REDIM PIPES_STRIP_POSITIONS(MAX_PIPES_STRIP) AS INTEGER + + ' Find the pipes + CALL STR.find_pos(s$, "|", PIPES_STRIP_POSITIONS%(), NUM_PIPES_STRIP_FOUND) + IF NUM_PIPES_STRIP_FOUND = 0 THEN + PIPESTRIP$ = s$ + EXIT FUNCTION + ELSE + REDIM _PRESERVE PIPES_STRIP_POSITIONS(NUM_PIPES_STRIP_FOUND-1) AS INTEGER + ub% = UBOUND(PIPES_STRIP_POSITIONS) + END IF + + ' Replace the pipe codes with ANSI codes + IF NUM_PIPES_STRIP_FOUND THEN + FOR i% = 0 TO ub% + p% = PIPES_STRIP_POSITIONS(i%) + code$ = MID$(s$, p%, 3) + args$ = "" : char$ = "" : txt$ = "" : t$ = "" : nums% = 0 + find$ = "" : repl$ = "" : spaces$ = "" + SELECT CASE code$ + CASE "|[X", "|[Y", "|[A", "|[B", "|[C", "|[D", "|@D": + args$ = _TRIM$(MID$(s$, p% + 3, 2)) + nums% = ABS(VAL(args$)) + find$ = code$ + args$ + END SELECT + SELECT CASE code$ + CASE "|@D": + IF args$ = "00" THEN + nums% = w% + END IF + char$ = MID$(s$, p% + 5, 1) + find$ = code$ + args$ + char$ + END SELECT + SELECT CASE code$ + CASE "|@R", "|@L", "|@C": + l% = INSTR(p%, s$, "{")+1 + r% = INSTR(p%, s$, "}") + txt$ = MID$(s$, l%, r%-l%) + find$ = code$ + "{" + txt$ + "}" + END SELECT + SELECT CASE code$ + CASE "|[0", "|[1", "|[K", "|CL", "|CN", "|CY", "|CR", _ + "|PI", "|00", "|01", "|02", "|03", "|04", "|05", _ + "|06", "|07", "|08", "|09", "|10", "|11", "|12", _ + "|13", "|14", "|15", "|16", "|17", "|18", "|19", _ + "|20", "|21", "|22", "|23", "|24", "|25", "|26", _ + "|27", "|28", "|29", "|30", "|31": + find$ = code$ + END SELECT + repl$ = "" + sout$ = STR.replace$(sout$, find$, repl$, 1) + NEXT i% + END IF + PIPESTRIP$ = sout$ + END FUNCTION + + + + $IF GJ_LIB_DICT_INC_BM = UNDEFINED THEN + '' + ' QB64_GJ_LIB + ' GRYMMJACK'S DICTIONARY Object (part of _GJ_LIB) + ' + ' Simulates a dictionary object as found in other languages. + ' + ' USAGE FOR Dict Object alone: + ' Insert '$INCLUDE:'path_to_GJ_LIB/DICT/DICT.BI' at the top of file + ' Insert '$INCLUDE:'path_to_GJ_LIB/DICT/DICT.BM' at the bottom of file + ' + ' @author Rick Christy + ' @uses DICT.BI + ' + $LET GJ_LIB_DICT_INC_BM = 1 + $IF GJ_LIB_DICT_INC_BI = UNDEFINED THEN + '' + ' QB64_GJ_LIB + ' GRYMMJACK'S DICT Object + ' + ' Simulates a dictionary object as found in other languages. + ' + ' USAGE: + ' Insert '$INCLUDE:'path_to_GJ_LIB/DICT/DICT.BI' at the top of file + ' Insert '$INCLUDE:'path_to_GJ_LIB/DICT/DICT.BM' at the bottom of file + ' + ' @author Rick Christy + ' @uses DICT.BM + ' + $LET GJ_LIB_DICT_INC_BI = 1 + + + + ' DICTIONARY type consists of keys and values and is intended for array use + TYPE DICTIONARY + key AS STRING + val AS STRING + END TYPE + $END IF + + + '' + ' Populates a dictionary with arrays of keys and values + ' + ' @param DICTIONARY d() object to populate + ' @param STRING ARRAY keys$() keys to use for dict keys + ' @param STRING ARRAY vals$() vals to use for dict vals + ' @return Nothing, but the d() passed is populated by keys and values + ' + SUB DICT.populate(d() AS DICTIONARY, keys$(), vals$()) + DIM AS INTEGER uk, uv, i + uk% = UBOUND(keys$) : uv% = UBOUND(vals$) + IF uk% <> uv% THEN EXIT SUB + FOR i% = 0 TO uk% + d(i%).key$ = keys$(i) + d(i%).val$ = vals$(i) + NEXT i + END SUB + + + '' + ' Fills a dictionary with serialized keys and values + ' + ' @param DICTIONARY d() object to fill + ' @return Nothing, but the d() passed in is filled + ' + SUB DICT.fill(d() AS DICTIONARY) + DIM AS INTEGER ub, lb, i + ub% = UBOUND(d) : lb% = LBOUND(d) + FOR i% = lb% TO ub% + d(i%).key$ = "key" + _TRIM$(STR$(i%)) + d(i%).val$ = _TRIM$(STR$(i%)) + NEXT i + END SUB + + + '' + ' Gets a dictionary array index by key + ' + ' @param DICTIONARY d() to look in + ' @param STRING ARRAY key$ to find the index for + ' @return INTEGER array index if found or 0 if not found + ' + FUNCTION DICT.get_index_by_key%(d() AS DICTIONARY, key$) + DIM AS INTEGER ub, lb, i + ub% = UBOUND(d) : lb% = LBOUND(d) + FOR i% = lb% TO ub% + IF d(i%).key$ = key$ THEN + DICT.get_index_by_key% = i% + EXIT FUNCTION + END IF + NEXT i% + DICT.get_index_by_key% = 0 + END FUNCTION + + + '' + ' Gets a dictionary items key by its array index + ' + ' @param DICTIONARY d() to look in + ' @param INTEGER index% to lookup the key for + ' @return STRING key of item at index + ' + FUNCTION DICT.get_key_by_index$(d() AS DICTIONARY, index%) + DIM AS INTEGER ub, lb + ub% = UBOUND(d) : lb% = LBOUND(d) + IF index% >= lb% AND index% <= ub% THEN + DICT.get_key_by_index$ = d(index%).key$ + END IF + END FUNCTION + + + '' + ' Gets a dictionary items value by its array index + ' + ' @param DICTIONARY d() to look in + ' @param INTEGER index% to lookup the value for + ' @return STRING value of item at index + ' + FUNCTION DICT.get_val_by_index$(d() AS DICTIONARY, index%) + DIM AS INTEGER ub, lb + ub% = UBOUND(d) : lb% = LBOUND(d) + IF index% >= lb% AND index% <= ub% THEN + DICT.get_val_by_index$ = d(index%).val$ + END IF + END FUNCTION + + + '' + ' Gets a dictionary items value by its key + ' + ' @param DICTIONARY d() to look in + ' @param STRING key$ to get the value for + ' @return STRING value of dictionary item by key + ' + FUNCTION DICT.get_val_by_key$(d() AS DICTIONARY, key$) + DIM AS INTEGER ub, lb, i + ub% = UBOUND(d) : lb% = LBOUND(d) + FOR i% = lb% TO ub% + IF d(i%).key$ = key$ THEN + DICT.get_val_by_key$ = d(i%).val$ + EXIT FUNCTION + END IF + NEXT i% + END FUNCTION + + + '' + ' Get all dictionary object keys as an array of strings + ' + ' @param DICTIONARY d() to look in + ' @param STRING ARRAY keys$() to store dict object keys into + ' @return Nothing, but the keys$() array is populated + ' + SUB DICT.get_keys(d() AS DICTIONARY, keys$()) + DIM AS INTEGER ub, lb, i, c + ub = UBOUND(d) : lb = LBOUND(d) : c% = ub% - lb% + REDIM keys$(c%) + FOR i% = lb% TO ub% + keys$(i%) = d(i%).key$ + NEXT i% + END SUB + + + '' + ' Get all dictionary object values as an array of strings + ' + ' @param DICTIONARY d() to look in + ' @param STRING ARRAY vals$() to store dict object vals into + ' @return Nothing, but the vals$() array is populated + ' + SUB DICT.get_vals(d() AS DICTIONARY, vals$()) + DIM AS INTEGER ub, lb, i, c + ub = UBOUND(d) : lb = LBOUND(d) : c% = ub% - lb% + REDIM vals$(c%) + FOR i% = lb% TO ub% + vals$(i%) = d(i%).val$ + NEXT i% + END SUB + + + '' + ' Swaps a dictionary objects keys for its values + ' + ' @param DICTIONARY d() to operate on + ' @return Nothing, but the dict() passed in is operated on directly + ' + SUB DICT.swap_keys_for_vals(d() AS DICTIONARY) + DIM AS INTEGER ub, lb, i, c + ub = UBOUND(d) : lb = LBOUND(d) : c% = ub% - lb% + DIM res(c%) AS DICTIONARY + FOR i% = lb% TO ub% + res(i%).key$ = d(i%).val$ + res(i%).val$ = d(i%).key$ + SWAP d(i%).key$, res(i%).key$ + SWAP d(i%).val$, res(i%).val$ + NEXT i% + END SUB + $END IF + $IF GJ_LIB_DUMP_INC_BM = UNDEFINED THEN + '' + ' QB64_GJ_LIB + ' GRYMMJACK'S DUMP LIB + ' + ' Dumps variables in a human friendly way to assist in debugging. + ' Inspired by PHP print_r() [1] which I missed when writing in QB64. + ' + ' So, why "dump"? + ' + ' [0] dump: + ' to copy (data in a computer's internal storage) to an external storage + ' or output device + ' ^^^^^^^^^^^^^ + ' this + ' + ' USAGE: + ' Insert '$INCLUDE:'path_to_GJ_LIB/DUMP/DUMP.BI' at the top of file + ' Insert '$INCLUDE:'path_to_GJ_LIB/DUMP/DUMP.BM' at the bottom of file + ' + ' TL;DR: + ' + ' Every function returns a string called a dump block. The idea is that + ' You can then PRINT the string in your code where you need to see what vars + ' have inside them quickly. + ' + ' A dump block consists of: + ' - A blank line + ' - The type of variable being dumped + ' - The number of elements in an array (if applicable) + ' - A label for reference + ' + ' Here is an example of a dump of a string array: + ' + ' DIM words$(2) + ' words$(0) = "foo" : words$(1) = "bar" : words$(2) = "baz" + ' PRINT DUMP.string_array(words$(), "words") + ' + ' OUTPUT: + ' + ' STRING ARRAY: words$(2) { + ' (0): "foo" [3] + ' (1): "bar" [3] + ' (2): "baz" [3] + ' } + ' + ' Because QB64 lacks any reflection support, has no way to pass an optional + ' argument, has no ability to identify a variable type, or even eval() it's + ' own dialect, there are separate functions for each common type. If you don't + ' see the one you need, it's easy enough to copy an existing one and make what + ' you want while staying in the spirit of DUMP LIB. + ' + ' FUNCTION NOTES + ' DUMP.bit_array$ Returns string with a dump of an array of bits + ' DUMP.unsigned_bit_array$ Returns string with a dump of an array of unsigned bits + ' DUMP.byte_array$ Returns string with a dump of an array of bytes + ' DUMP.unsigned_byte_array$ Returns string with a dump of an array of unsigned bytes + ' DUMP.unsigned_integer$ Returns string with a dump of an array of unsigned integers + ' DUMP.unsigned_byte_array_as_hex$ Returns string with a dump of an array of unsigned bytes as hex + ' DUMP.unsigned_byte_array_as_ascii$ Returns string with a dump of an array of unsigned bytes as hex and ASCII + ' DUMP.string$ Includes handy output of the strings length. + ' DUMP.string_array$ Works on 1 dimensional arrays only (right now). + ' DUMP.integer_array$ Works on 1 dimensional arrays only (right now). + ' DUMP.single_array$ Works on 1 dimensional arrays only (right now). + ' DUMP.long_array$ Works on 1 dimensional arrays only (right now). + ' DUMP.double_array$ Works on 1 dimensional arrays only (right now). + ' DUMP.dict$ Dump a dictionary object and it's contents. + ' + ' @author Rick Christy + ' @depends DUMP.BI + ' @see [0] https://www.merriam-webster.com/dictionary/dump + ' @see [1] https://www.php.net/manual/en/function.print-r.php + ' + $LET GJ_LIB_DUMP_INC_BM = 1 + + + + '' + ' Returns string with a dump of a string + ' + ' @param STRING s$ to dump + ' @param STRING label$ to give the dump block + ' @return STRING dump block + ' + FUNCTION DUMP.string$(s$, label$) + DIM AS STRING l, r + l$ = _TRIM$(STR$(LEN(s$))) + r$ = GJ_LIB_NL$ + "STRING: " + label$ + "$ {" + GJ_LIB_NL$ + r$ = r$ + " " + CHR$(34) + s$ + CHR$(34) + " [" + l$ + "]" + GJ_LIB_NL$ + r$ = r$ + "} " + DUMP.string$ = r$ + END FUNCTION + + + '' + ' Returns string with a dump of an array of bits + ' + ' @param _BIT ARRAY arr`() of bits to dump + ' @param STRING label$ to give the dump block + ' @return STRING dump block + ' + FUNCTION DUMP.bit_array$(arr`(), label$) + DIM AS INTEGER lb, ub, i + DIM AS STRING t, r + lb% = LBOUND(arr`) : ub% = UBOUND(arr`) : t$ = _TRIM$(STR$(ub% - lb%)) + r$ = GJ_LIB_NL$ + "STRING ARRAY: " + label$ + "`(" + t$ + ") {" + GJ_LIB_NL$ + FOR i% = lb% TO ub% + r$ = r$ + " (" + _TRIM$(STR$(i%)) + "): " + STR$(arr`(i%)) + r$ = r$ + " [" + t$ + "]" + GJ_LIB_NL$ + NEXT i% + r$ = r$ + "}" + DUMP.bit_array$ = r$ + END FUNCTION + + + '' + ' Returns string with a dump of an array of unsigned bits + ' + ' @param _UNSIGNED _BIT arr~`() of unsigned bits to dump + ' @param STRING label$ to give the dump block + ' @return STRING dump block + ' + FUNCTION DUMP.unsigned_bit_array$(arr~`(), label$) + DIM AS INTEGER lb, ub, i + DIM AS STRING t, r + lb% = LBOUND(arr~`) : ub% = UBOUND(arr~`) : t$ = _TRIM$(STR$(ub% - lb%)) + r$ = GJ_LIB_NL$ + "STRING ARRAY: " + label$ + "~`(" + t$ + ") {" + GJ_LIB_NL$ + FOR i% = lb% TO ub% + r$ = r$ + " (" + _TRIM$(STR$(i%)) + "): " + STR$(arr~`(i%)) + r$ = r$ + " [" + t$ + "]" + GJ_LIB_NL$ + NEXT i% + r$ = r$ + "}" + DUMP.unsigned_bit_array$ = r$ + END FUNCTION + + + '' + ' Returns string with a dump of an array of strings + ' + ' @param STRING ARRAY arr$() of strings to dump + ' @param STRING label$ to give the dump block + ' @return STRING dump block + ' + FUNCTION DUMP.string_array$(arr$(), label$) + DIM AS INTEGER lb, ub, i + DIM AS STRING t, r + lb% = LBOUND(arr$) : ub% = UBOUND(arr$) : t$ = _TRIM$(STR$(ub% - lb%)) + r$ = GJ_LIB_NL$ + "STRING ARRAY: " + label$ + "$(" + t$ + ") {" + GJ_LIB_NL$ + FOR i% = lb% TO ub% + r$ = r$ + " (" + _TRIM$(STR$(i%)) + "): " + CHR$(34) + arr$(i%) + CHR$(34) + r$ = r$ + " [" + t$ + "]" + GJ_LIB_NL$ + NEXT i% + r$ = r$ + "}" + DUMP.string_array$ = r$ + END FUNCTION + + + '' + ' Returns string with a dump of an array of integers + ' + ' @param INTEGER ARRAY arr%() of integers to dump + ' @param STRING label$ to give the dump block + ' @return STRING dump block + ' + FUNCTION DUMP.integer_array$(arr%(), label$) + DIM AS INTEGER lb, ub, i + DIM AS STRING t, r, num + lb% = LBOUND(arr%) : ub% = UBOUND(arr%) : t$ = _TRIM$(STR$(ub% - lb%)) + r$ = GJ_LIB_NL$ + "INTEGER ARRAY: " + label$ + "%(" + t$ + ") {" + GJ_LIB_NL$ + FOR i% = lb% TO ub% + num$ = _TRIM$(STR$(arr%(i%))) + r$ = r$ + " (" + _TRIM$(STR$(i%)) + "): " + num$ + GJ_LIB_NL$ + NEXT i% + r$ = r$ + "}" + DUMP.integer_array$ = r$ + END FUNCTION + + + '' + ' Returns string with a dump of an array of unsigned integers + ' + ' @param _UNSIGNED INTEGER arr%() of unsigned integers to dump + ' @param STRING label$ to give the dump block + ' @return STRING dump block + ' + FUNCTION DUMP.unsigned_integer_array$(arr~%(), label$) + DIM AS INTEGER lb, ub, i + DIM AS STRING r, t, num + lb% = LBOUND(arr~%) : ub% = UBOUND(arr~%) : t$ = _TRIM$(STR$(ub% - lb%)) + r$ = GJ_LIB_NL$ + "INTEGER ARRAY: " + label$ + "~%(" + t$ + ") {" + GJ_LIB_NL$ + FOR i% = lb% TO ub% + num$ = _TRIM$(STR$(arr~%(i%))) + r$ = r$ + " (" + _TRIM$(STR$(i%)) + "): " + num$ + GJ_LIB_NL$ + NEXT i% + r$ = r$ + "}" + DUMP.unsigned_integer_array$ = r$ + END FUNCTION + + + '' + ' Returns a string containing a dump of an array of singles + ' + ' @param SINGLE ARRAY arr!() of singles to dump + ' @param STRING label$ to give the dump block + ' @return STRING dump block + ' + FUNCTION DUMP.single_array$(arr!(), label$) + DIM AS INTEGER lb, ub, i + DIM AS STRING r, t, num + lb% = LBOUND(arr!) : ub% = UBOUND(arr!) : t$ = _TRIM$(STR$(ub% - lb%)) + r$ = GJ_LIB_NL$ + "SINGLE ARRAY: " + label$ + "!(" + t$ + ") {" + GJ_LIB_NL$ + FOR i% = lb% TO ub% + num$ = _TRIM$(STR$(arr!(i%))) + r$ = r$ + " (" + _TRIM$(STR$(i%)) + "): " + num$ + GJ_LIB_NL$ + NEXT i% + r$ = r$ + "}" + DUMP.single_array$ = r$ + END FUNCTION + + + '' + ' Dumps an array of longs + ' + ' @param LONG ARRAY arr&() of longs to dump + ' @param STRING label$ to give the dump block + ' @return STRING dump block + ' + FUNCTION DUMP.long_array$(arr&(), label$) + DIM AS INTEGER lb, ub, i + DIM AS STRING r, t, num + lb% = LBOUND(arr&) : ub% = UBOUND(arr&) : t$ = _TRIM$(STR$(ub% - lb%)) + r$ = GJ_LIB_NL$ + "LONG ARRAY: " + label$ + "&(" + t$ + ") {" + GJ_LIB_NL$ + FOR i% = lb% TO ub% + num$ = _TRIM$(STR$(arr&(i%))) + r$ = r$ + " (" + _TRIM$(STR$(i%)) + "): " + num$ + GJ_LIB_NL$ + NEXT i% + r$ = r$ + "}" + DUMP.long_array$ = r$ + END FUNCTION + + + '' + ' Dumps an array of doubles + ' + ' @param DOUBLE ARRAY arr#() of doubles to dump + ' @param STRING label$ to give the dump block + ' @return STRING dump block + ' + FUNCTION DUMP.double_array$(arr#(), label$) + DIM AS INTEGER lb, ub, i + DIM AS STRING r, t, num + lb% = LBOUND(arr#) : ub% = UBOUND(arr#) : t$ = _TRIM$(STR$(ub% - lb%)) + r$ = GJ_LIB_NL$ + "DOUBLE ARRAY: " + label$ + "#(" + t$ + ") {" + GJ_LIB_NL$ + FOR i% = lb% TO ub% + num$ = _TRIM$(STR$(arr#(i%))) + r$ = r$ + " (" + _TRIM$(STR$(i%)) + "): " + num$ + GJ_LIB_NL$ + NEXT i% + r$ = r$ + "}" + DUMP.double_array$ = r$ + END FUNCTION + + + '' + ' Returns string with a dump of an array of bytes + ' + ' @param _BYTE ARRAY arr%%() of bytes to dump + ' @param STRING label$ to give the dump block + ' @return STRING dump block + ' + FUNCTION DUMP.byte_array$(arr%%(), label$) + DIM AS INTEGER lb, ub, i + DIM AS STRING r, t, num, si + lb% = LBOUND(arr%%) : ub% = UBOUND(arr%%) : t$ = _TRIM$(STR$(ub% - lb%)) + r$ = GJ_LIB_NL$ + "BYTE ARRAY: " + label$ + "%%(" + t$ + ") {" + FOR i% = lb% TO ub% + si$ = _TRIM$(STR$(i%)) + IF LEN(si$) = 1 THEN si$ = "0" + si$ + num$ = _TRIM$(STR$(arr%%(i%))) + IF SGN(arr%%(i%)) = 1 THEN + IF LEN(num$) = 2 THEN + num$ = " 0" + num$ + ELSEIF LEN(num$) = 1 THEN + num$ = " 00" + num$ + END IF + ELSEIF SGN(arr%%(i%)) = 0 THEN + num$ = " 000" + ELSE + IF LEN(num$) = 3 THEN + num$ = "-0" + MID$(num$, 2, 2) + ELSEIF LEN(num$) = 2 THEN + num$ = "-00" + MID$(num$, 2, 1) + END IF + END IF + IF i% MOD 8 = 0 THEN + r$ = r$ + GJ_LIB_NL$ + r$ = r$ + STRING$(4, " ") + si$ + ": " + END IF + r$ = r$ + num$ + " " + NEXT i% + r$ = r$ + GJ_LIB_NL$ + "}" + DUMP.byte_array$ = r$ + END FUNCTION + + + '' + ' Returns string with a dump of an array of unsigned bytes + ' + ' @param _UNSIGNED _BYTE ARRAY arr~%%() of unsigned bytes to dump + ' @param STRING label$ to give the dump block + ' @return STRING dump block + ' + FUNCTION DUMP.unsigned_byte_array$(arr~%%(), label$) + DIM AS INTEGER lb, ub, i + DIM AS STRING r, t, num, si + lb% = LBOUND(arr~%%) : ub% = UBOUND(arr~%%) : t$ = _TRIM$(STR$(ub% - lb%)) + r$ = GJ_LIB_NL$ + "UNSIGNED BYTE ARRAY: " + label$ + "~%%(" + t$ + ") {" + FOR i% = lb% TO ub% + si$ = _TRIM$(STR$(i%)) + IF LEN(si$) = 1 THEN si$ = "0" + si$ + num$ = _TRIM$(STR$(arr~%%(i%))) + IF LEN(num$) = 2 THEN + num$ = "0" + num$ + ELSEIF LEN(num$) = 1 THEN + num$ = "00" + num$ + END IF + IF i% MOD 16 = 0 THEN + r$ = r$ + GJ_LIB_NL$ + r$ = r$ + STRING$(4, " ") + si$ + ": " + END IF + r$ = r$ + num$ + " " + NEXT i% + r$ = r$ + GJ_LIB_NL$ + "}" + DUMP.unsigned_byte_array$ = r$ + END FUNCTION + + + '' + ' Returns string with a dump of an array of unsigned bytes as hex + ' + ' @param _UNSIGNED _BYTE ARRAY arr~%%() of unsigned bytes to dump + ' @param STRING label$ to give the dump block + ' @return STRING dump block + ' + FUNCTION DUMP.unsigned_byte_array_as_hex$(arr~%%(), label$) + DIM AS INTEGER lb, ub, i + DIM AS STRING r, t, num, si, h + lb% = LBOUND(arr~%%) : ub% = UBOUND(arr~%%) : t$ = _TRIM$(STR$(ub% - lb%)) + r$ = GJ_LIB_NL$ + "UNSIGNED BYTE ARRAY: " + label$ + "~%%(" + t$ + ") {" + FOR i% = lb% TO ub% + si$ = _TRIM$(STR$(i%)) + IF LEN(si$) = 1 THEN si$ = "0" + si$ + h$ = HEX$(arr~%%(i%)) + IF LEN(h$) = 1 THEN h$ = "0" + h$ + num$ = _TRIM$(STR$(arr~%%(i%))) + IF i% MOD 16 = 0 THEN + r$ = r$ + GJ_LIB_NL$ + r$ = r$ + STRING$(4, " ") + si$ + ": " + END IF + r$ = r$ + h$ + " " + NEXT i% + r$ = r$ + GJ_LIB_NL$ + "}" + DUMP.unsigned_byte_array_as_hex$ = r$ + END FUNCTION + + + '' + ' Returns string with a dump of an array of unsigned bytes as ascii + ' + ' @param _UNSIGNED _BYTE ARRAY arr~%%() of unsigned bytes to dump + ' @param STRING label$ to give the dump block + ' @return STRING dump block + ' + FUNCTION DUMP.unsigned_byte_array_as_ascii$(arr~%%(), label$) + DIM AS INTEGER lb, ub, i + DIM AS STRING r, t, num, si, h, c + lb% = LBOUND(arr~%%) : ub% = UBOUND(arr~%%) : t$ = _TRIM$(STR$(ub% - lb%)) + r$ = GJ_LIB_NL$ + "UNSIGNED BYTE ARRAY: " + label$ + "~%%(" + t$ + ") {" + FOR i% = lb% TO ub% + si$ = _TRIM$(STR$(i%)) + DO WHILE LEN(si$) < LEN(STR$(ub%))-1 + si$ = "0" + si$ + LOOP + h$ = HEX$(arr~%%(i%)) + IF LEN(h$) = 1 THEN h$ = "0" + h$ + num$ = _TRIM$(STR$(arr~%%(i%))) + IF i% MOD 16 = 0 THEN + r$ = r$ + GJ_LIB_NL$ + r$ = r$ + STRING$(4, " ") + si$ + ": " + END IF + r$ = r$ + h$ + " " + NEXT i% + r$ = r$ + GJ_LIB_NL$ + "ASCII:" + FOR i% = lb% TO ub% + si$ = _TRIM$(STR$(i%)) + DO WHILE LEN(si$) < LEN(STR$(ub%))-1 + si$ = "0" + si$ + LOOP + IF arr~%%(i%) < 33 OR arr~%%(i%) > 254 THEN + c$ = ".." + ELSE + c$ = CHR$(arr~%%(i%)) + " " + END IF + IF i% MOD 16 = 0 THEN + r$ = r$ + GJ_LIB_NL$ + r$ = r$ + STRING$(4, " ") + si$ + ": " + END IF + r$ = r$ + c$ + " " + NEXT i% + r$ = r$ + GJ_LIB_NL$ + "}" + DUMP.unsigned_byte_array_as_ascii$ = r$ + END FUNCTION + + + '' + ' Dumps a dictionary object and its contents + ' + ' @param DICTIONARY d() object to dump + ' @param STRING label$ to give the dump block + ' @return STRING dump block + ' + FUNCTION DUMP.dict$(d() AS DICTIONARY, label$) + DIM AS INTEGER lb, ub, i + DIM AS STRING r, t, q, skey, sval + lb% = LBOUND(d) : ub% = UBOUND(d) : t$ = _TRIM$(STR$(ub% - lb%)) + r$ = GJ_LIB_NL$ + "DICT: " + label$ + "(" + t$ + ") {" + GJ_LIB_NL$ + q$ = CHR$(34) + FOR i% = lb% TO ub% + skey$ = d(i%).key$ + sval$ = d(i%).val$ + r$ = r$ + " (" + _TRIM$(STR$(i%)) + ") " + q$ + skey$ + q$ + r$ = r$ + ": " + q$ + sval$ + q$ + GJ_LIB_NL$ + NEXT i% + r$ = r$ + "}" + DUMP.dict$ = r$ + END FUNCTION + $END IF + $IF GJ_LIB_ANSI_INC_BM = UNDEFINED THEN + '' + ' QB64_GJ_LIB + ' GRYMMJACK'S ANSI LIB + ' + ' Support for ANSI.SYS and extended codes for terminal. + ' + ' To emulate ANSI functionality using QB internals set this var to TRUE: + ' GJ_LIB_ANSI_EMU = TRUE + ' This variable can be toggled between TRUE and FALSE whenever needed, as many + ' times as desired as it is not a CONST. + ' + ' NOTE: + ' There is a QB64 bug on MacOS/Linux where $CONSOLE:ONLY does not read input + ' in the same way as on Windows. This bug is described here: + ' https://github.com/QB64Official/qb64/issues/33 + ' + ' @author Rick Christy + ' @uses ANSI.BI + ' @see https://gist.github.com/grymmjack/9dae29a60ea65f086d0b35df96fe2291 + ' + $LET GJ_LIB_ANSI_INC_BM = 1 + + + + '' + ' Clamps a value from going below 0 + ' + ' @param INTEGER var% to clamp to zero + ' @return INTEGER var clamped to 0 or more + ' + FUNCTION ANSI.clamp_zero%(var%) + IF var% < 0 THEN + ANSI.clamp_zero% = 0 + ELSE + ANSI.clamp_zero% = var% + END IF + END FUNCTION + + + '' + ' Safely locates within ranges 1+ on row and col + ' + ' @param INTEGER row% for locate + ' @param INTEGER col% for locate + ' + SUB ANSI.safe_locate(row%, col%) + IF row% <= 0 THEN row% = 1 + IF col% <= 0 THEN col% = 1 + LOCATE row%, col% + END SUB + + + '' + ' Safely locates within ranges 1+ on col + ' + ' @param INTEGER col% for locate + ' + SUB ANSI.safe_locate_x(col%) + IF col% <= 0 THEN col% = 1 + LOCATE , col% + END SUB + + + '' + ' Safely locates within ranges 1+ on row + ' + ' @param INTEGER row% for locate + ' + SUB ANSI.safe_locate_y(row%) + IF row% <= 0 THEN row% = 1 + LOCATE row% + END SUB + + + '' + ' Hides cursor + ' + ' @return STRING with ANSI escape codes to hide cursor + ' + FUNCTION ANSI.hide_cursor$() + DIM AS STRING sout + sout$ = CHR$(ANSI.ESC) + "[?25l" + IF GJ_LIB_ANSI_EMU THEN LOCATE ,,0 + IF GJ_LIB_ANSI_OUTPUT THEN ANSI.hide_cursor$ = sout$ + END FUNCTION + + + '' + ' Shows cursor + ' + ' @return STRING with ANSI escape codes to show cursor + ' + FUNCTION ANSI.show_cursor$() + DIM AS STRING sout + sout$ = CHR$(ANSI.ESC) + "[?25h" + IF GJ_LIB_ANSI_EMU THEN LOCATE ,,1 + IF GJ_LIB_ANSI_OUTPUT THEN ANSI.show_cursor$ = sout$ + END FUNCTION + + + '' + ' Moves cursor to home position (0,0) + ' + ' @return STRING with ANSI escape codes to move cursor + ' + FUNCTION ANSI.home$() + DIM AS STRING sout + sout$ = CHR$(ANSI.ESC) + "[H" + ANSI.x% = 0 : ANSI.y% = 0 + IF GJ_LIB_ANSI_EMU THEN CALL ANSI.safe_locate(ANSI.y%, ANSI.x%) + IF GJ_LIB_ANSI_OUTPUT THEN ANSI.home$ = sout$ + END FUNCTION + + + '' + ' Moves cursor to desired row and column + ' + ' @param INTEGER row% to move cursor to + ' @param INTEGER col% to move cursor to + ' @return STRING with ANSI escape codes to move cursor + ' + FUNCTION ANSI.locate$(row%, col%) + DIM AS STRING sout + sout$ = CHR$(ANSI.ESC) + "[" + sout$ = sout$ + _TRIM$(STR$(row%)) + ";" + sout$ = sout$ + _TRIM$(STR$(col%)) + "H" + ANSI.x% = col% : ANSI.y% = row% + IF GJ_LIB_ANSI_EMU THEN CALL ANSI.safe_locate(ANSI.y%, ANSI.x%) + IF GJ_LIB_ANSI_OUTPUT THEN ANSI.locate$ = sout$ + END FUNCTION + + + '' + ' Moves cursor up n lines + ' + ' @param INTEGER n% Number of lines to move cursor up + ' @return STRING with ANSI escape codes to move cursor + ' + FUNCTION ANSI.move_up$(n%) + DIM AS STRING sout + sout$ = CHR$(ANSI.ESC) + "[" + sout$ = sout$ + _TRIM$(STR$(n%)) + "A" + ANSI.y% = ANSI.clamp_zero(ANSI.y% - n%) + IF GJ_LIB_ANSI_EMU THEN CALL ANSI.safe_locate_y(ANSI.y%) + IF GJ_LIB_ANSI_OUTPUT THEN ANSI.move_up$ = sout$ + END FUNCTION + + + '' + ' Moves cursor down n lines + ' + ' @param INTEGER n% Number of lines to move cursor down + ' @return STRING with ANSI escape codes to move cursor + ' + FUNCTION ANSI.move_down$(n%) + DIM AS STRING sout + sout$ = CHR$(ANSI.ESC) + "[" + sout$ = sout$ + _TRIM$(STR$(n%)) + "B" + ANSI.y% = ANSI.y% + n% + IF GJ_LIB_ANSI_EMU THEN CALL ANSI.safe_locate_y(ANSI.y%) + IF GJ_LIB_ANSI_OUTPUT THEN ANSI.move_down$ = sout$ + END FUNCTION + + + '' + ' Moves cursor right n lines + ' + ' @param INTEGER n% Number of lines to move cursor right + ' @return STRING with ANSI escape codes to move cursor + ' + FUNCTION ANSI.move_right$(n%) + DIM AS STRING sout + sout$ = CHR$(ANSI.ESC) + "[" + sout$ = sout$ + _TRIM$(STR$(n%)) + "C" + ANSI.x% = ANSI.x% + n% + IF GJ_LIB_ANSI_EMU THEN CALL ANSI.safe_locate_x(ANSI.x%) + IF GJ_LIB_ANSI_OUTPUT THEN ANSI.move_right$ = sout$ + END FUNCTION + + + '' + ' Moves cursor left n lines + ' + ' @param INTEGER n% Number of lines to move cursor left + ' @return STRING with ANSI escape codes to move cursor + ' + FUNCTION ANSI.move_left$(n%) + DIM AS STRING sout + sout$ = CHR$(ANSI.ESC) + "[" + sout$ = sout$ + _TRIM$(STR$(n%)) + "D" + ANSI.x% = ANSI.clamp_zero(ANSI.x% - n%) + IF GJ_LIB_ANSI_EMU THEN CALL ANSI.safe_locate_x(ANSI.x%) + IF GJ_LIB_ANSI_OUTPUT THEN ANSI.move_left$ = sout$ + END FUNCTION + + + '' + ' Moves cursor to beginning of next line, n lines down + ' + ' @param INTEGER n% Number of lines to move cursor down + ' @return STRING with ANSI escape codes to move cursor + ' + FUNCTION ANSI.move_lines_down$(n%) + DIM AS STRING sout + sout$ = CHR$(ANSI.ESC) + "[" + sout$ = sout$ + _TRIM$(STR$(n%)) + "E" + ANSI.y% = ANSI.y% + n% + IF GJ_LIB_ANSI_EMU THEN CALL ANSI.safe_locate_y(ANSI.y%) + IF GJ_LIB_ANSI_OUTPUT THEN ANSI.move_lines_down$ = sout$ + END FUNCTION + + + '' + ' Moves cursor to beginning of previous line, n lines up + ' + ' @param INTEGER n% Number of lines to move cursor up + ' @return STRING with ANSI escape codes to move cursor + ' + FUNCTION ANSI.move_lines_up$(n%) + DIM AS STRING sout + sout$ = CHR$(ANSI.ESC) + "[" + sout$ = sout$ + _TRIM$(STR$(n%)) + "F" + ANSI.y% = ANSI.clamp_zero(ANSI.y% - n%) + IF GJ_LIB_ANSI_EMU THEN CALL ANSI.safe_locate_y(ANSI.y%) + IF GJ_LIB_ANSI_OUTPUT THEN ANSI.move_lines_up$ = sout$ + END FUNCTION + + + '' + ' Moves cursor to column position n + ' + ' @param INTEGER n% Column to move cursor to + ' @return STRING with ANSI escape codes to move cursor + ' + FUNCTION ANSI.move_column$(n%) + DIM AS STRING sout + sout$ = CHR$(ANSI.ESC) + "[" + sout$ = sout$ + _TRIM$(STR$(n%)) + "G" + ANSI.x% = n% + IF GJ_LIB_ANSI_EMU THEN CALL ANSI.safe_locate_x(ANSI.x%) + IF GJ_LIB_ANSI_OUTPUT THEN ANSI.move_column$ = sout$ + END FUNCTION + + + '' + ' Moves cursor one line up, scrolling if needed + ' + ' @return STRING with ANSI escape codes to move cursor + ' + FUNCTION ANSI.move_line_up$() + DIM AS STRING sout + sout$ = CHR$(ANSI.ESC) + "M" + ANSI.y% = ANSI.clamp_zero(ANSI.y% - 1) + IF GJ_LIB_ANSI_EMU THEN CALL ANSI.safe_locate_y(ANSI.y%) + IF GJ_LIB_ANSI_OUTPUT THEN ANSI.move_line_up$ = sout$ + END FUNCTION + + + '' + ' Save cursor position + ' + ' @return STRING with ANSI escape codes + ' + FUNCTION ANSI.save_pos$() + DIM AS STRING sout + sout$ = CHR$(ANSI.ESC) + "[s" + ANSI.save_x% = ANSI.x% : ANSI.save_y% = ANSI.y% + IF GJ_LIB_ANSI_OUTPUT THEN ANSI.save_pos$ = sout$ + END FUNCTION + + + '' + ' Restore cursor position + ' + ' @return STRING with ANSI escape codes + ' + FUNCTION ANSI.restore_pos$() + DIM AS STRING sout, nil + sout$ = CHR$(ANSI.ESC) + "[u" + nil$ = ANSI.locate(ANSI.save_y%, ANSI.save_x%) + IF GJ_LIB_ANSI_EMU THEN CALL ANSI.safe_locate(ANSI.save_y%, ANSI.save_x%) + IF GJ_LIB_ANSI_OUTPUT THEN ANSI.restore_pos$ = sout$ + END FUNCTION + + + '' + ' Erase from cursor to end of screen + ' + ' @return STRING with ANSI escape codes + ' + FUNCTION ANSI.erase_to_eos$() + DIM AS STRING sout + DIM AS INTEGER w, h, x, y, row + sout$ = CHR$(ANSI.ESC) + "[0J" + IF GJ_LIB_ANSI_EMU THEN + w = _WIDTH + h = _HEIGHT + x = ANSI.x% + y = ANSI.y% + PRINT SPC(w-x) + FOR row = y TO h + LOCATE row, 1 + PRINT SPC(w) + NEXT row + CALL ANSI.safe_locate(ANSI.y%, ANSI.x%) + END IF + IF GJ_LIB_ANSI_OUTPUT THEN ANSI.erase_to_eos$ = sout$ + END FUNCTION + + + '' + ' Erase from cursor to beginning of screen + ' + ' @return STRING with ANSI escape codes + ' + FUNCTION ANSI.erase_to_bos$() + DIM AS STRING sout + DIM AS INTEGER w, h, row + sout$ = CHR$(ANSI.ESC) + "[1J" + IF GJ_LIB_ANSI_EMU THEN + w = _WIDTH + h = _HEIGHT + LOCATE ,1 + PRINT SPC(ANSI.x%-1) + FOR row = h TO 1 STEP - 1 + LOCATE row, 1 + PRINT SPC(w) + NEXT row + CALL ANSI.safe_locate(ANSI.y%, ANSI.x%) + END IF + IF GJ_LIB_ANSI_OUTPUT THEN ANSI.erase_to_bos$ = sout$ + END FUNCTION + + + '' + ' Erase entire screen + ' + ' @return STRING with ANSI escape codes + ' + FUNCTION ANSI.erase_screen$() + DIM AS STRING sout + sout$ = CHR$(ANSI.ESC) + "[2J" + sout$ = sout$ + ANSI.locate(1,1) + ANSI.x% = 0 : ANSI.y% = 0 + IF GJ_LIB_ANSI_EMU THEN + CALL ANSI.safe_locate(ANSI.y%, ANSI.x%) + CLS + END IF + IF GJ_LIB_ANSI_OUTPUT THEN ANSI.erase_screen$ = sout$ + END FUNCTION + + + '' + ' Erase from cursor to end of line + ' + ' @return STRING with ANSI escape codes + ' + FUNCTION ANSI.erase_to_eol$() + DIM AS STRING sout + DIM AS INTEGER w + sout$ = CHR$(ANSI.ESC) + "[0K" + IF GJ_LIB_ANSI_EMU THEN + w = _WIDTH + PRINT SPC(w-ANSI.x%) + CALL ANSI.safe_locate(ANSI.y%, ANSI.x%) + END IF + IF GJ_LIB_ANSI_OUTPUT THEN ANSI.erase_to_eol$ = sout$ + END FUNCTION + + + '' + ' Erase from start of line to cursor + ' + ' @return STRING with ANSI escape codes + ' + FUNCTION ANSI.erase_from_sol$() + DIM AS STRING sout + DIM AS INTEGER w + sout$ = CHR$(ANSI.ESC) + "[1K" + IF GJ_LIB_ANSI_EMU THEN + w = _WIDTH + CALL ANSI.safe_locate(ANSI.y%, ANSI.x%) + PRINT SPC(w-ANSI.x%) + END IF + IF GJ_LIB_ANSI_OUTPUT THEN ANSI.erase_from_sol$ = sout$ + END FUNCTION + + + '' + ' Erase line + ' + ' @return STRING with ANSI escape codes + ' + FUNCTION ANSI.erase_line$() + DIM AS STRING sout + DIM AS INTEGER w, x, y + sout$ = CHR$(ANSI.ESC) + "[2K" + IF GJ_LIB_ANSI_EMU THEN + w = _WIDTH + x = ANSI.x% + y = ANSI.y% + LOCATE ,1 + PRINT SPC(w) + CALL ANSI.safe_locate(ANSI.y%, ANSI.x%) + END IF + IF GJ_LIB_ANSI_OUTPUT THEN ANSI.erase_line$ = sout$ + END FUNCTION + + + '' + ' Reset modes + ' + ' @return STRING with ANSI escape codes for resetting 1modes + ' + FUNCTION ANSI.mode_reset_all$() + DIM AS STRING sout + sout$ = CHR$(ANSI.ESC) + "[0m" + IF GJ_LIB_ANSI_EMU THEN + ANSI.fg_color& = 7 : ANSI.bg_color& = 0 + COLOR ANSI.fg_color&, ANSI.bg_color& + _BLINK ON + END IF + IF GJ_LIB_ANSI_OUTPUT THEN ANSI.mode_reset_all$ = sout$ + END FUNCTION + + + '' + ' Set bold mode + ' + ' @return STRING with ANSI escape codes for setting mode + ' + FUNCTION ANSI.mode_bold$() + DIM AS STRING sout + sout$ = CHR$(ANSI.ESC) + "[1m" + IF GJ_LIB_ANSI_EMU THEN + IF ANSI.fg_color& <= 7 THEN ANSI.fg_color& = ANSI.fg_color& + 8 + COLOR ANSI.fg_color& + END IF + IF GJ_LIB_ANSI_OUTPUT THEN ANSI.mode_bold$ = sout$ + END FUNCTION + + + '' + ' Reset bold mode + ' + ' @return STRING with ANSI escape codes for resetting mode + ' + FUNCTION ANSI.mode_bold_reset$() + DIM AS STRING sout + sout$ = CHR$(ANSI.ESC) + "[22m" + IF GJ_LIB_ANSI_EMU THEN + IF ANSI.fg_color& >= 8 THEN ANSI.fg_color& = ANSI.fg_color& - 8 + COLOR ANSI.fg_color& + END IF + IF GJ_LIB_ANSI_OUTPUT THEN ANSI.mode_bold_reset$ = sout$ + END FUNCTION + + + '' + ' Set dim mode + ' + ' @return STRING with ANSI escape codes for setting mode + ' + FUNCTION ANSI.mode_dim$() + DIM AS STRING sout, nil + sout$ = CHR$(ANSI.ESC) + "[2m" + IF GJ_LIB_ANSI_EMU THEN nil$ = ANSI.mode_bold_reset + IF GJ_LIB_ANSI_OUTPUT THEN ANSI.mode_dim$ = sout$ + END FUNCTION + + + '' + ' Reset dim mode + ' + ' @return STRING with ANSI escape codes for resetting mode + ' + FUNCTION ANSI.mode_dim_reset$() + DIM AS STRING sout, nil + sout$ = CHR$(ANSI.ESC) + "[22m" + IF GJ_LIB_ANSI_EMU THEN nil$ = ANSI.mode_bold_reset + IF GJ_LIB_ANSI_OUTPUT THEN ANSI.mode_dim_reset$ = sout$ + END FUNCTION + + + '' + ' Set italic mode + ' + ' @return STRING with ANSI escape codes for setting mode + ' + FUNCTION ANSI.mode_italic$() + DIM AS STRING sout, nil + sout$ = CHR$(ANSI.ESC) + "[3m" + IF GJ_LIB_ANSI_EMU THEN nil$ = ANSI.mode_bold + IF GJ_LIB_ANSI_OUTPUT THEN ANSI.mode_italic$ = sout$ + END FUNCTION + + + '' + ' Reset italic mode + ' + ' @return STRING with ANSI escape codes for resetting mode + ' + FUNCTION ANSI.mode_italic_reset$() + DIM AS STRING sout, nil + sout$ = CHR$(ANSI.ESC) + "[23m" + IF GJ_LIB_ANSI_EMU THEN nil$ = ANSI.mode_bold_reset + IF GJ_LIB_ANSI_OUTPUT THEN ANSI.mode_italic_reset$ = sout$ + END FUNCTION + + + '' + ' Set underline mode + ' + ' @return STRING with ANSI escape codes for setting mode + ' + FUNCTION ANSI.mode_underline$() + DIM AS STRING sout, nil + sout$ = CHR$(ANSI.ESC) + "[4m" + IF GJ_LIB_ANSI_EMU THEN nil$ = ANSI.mode_bold + IF GJ_LIB_ANSI_OUTPUT THEN ANSI.mode_underline$ = sout$ + END FUNCTION + + + '' + ' Reset underline mode + ' + ' @return STRING with ANSI escape codes for resetting mode + ' + FUNCTION ANSI.mode_underline_reset$() + DIM AS STRING sout, nil + sout$ = CHR$(ANSI.ESC) + "[24m" + IF GJ_LIB_ANSI_EMU THEN nil$ = ANSI.mode_bold_reset + IF GJ_LIB_ANSI_OUTPUT THEN ANSI.mode_underline_reset$ = sout$ + END FUNCTION + + + '' + ' Set blinking mode + ' + ' @return STRING with ANSI escape codes for setting mode + ' + FUNCTION ANSI.mode_blinking$() + DIM AS STRING sout + sout$ = CHR$(ANSI.ESC) + "[5m" + IF GJ_LIB_ANSI_EMU THEN _BLINK ON + IF GJ_LIB_ANSI_OUTPUT THEN ANSI.mode_blinking$ = sout$ + END FUNCTION + + + '' + ' Reset blinking mode + ' + ' @return STRING with ANSI escape codes for resetting mode + ' + FUNCTION ANSI.mode_blinking_reset$() + DIM AS STRING sout + sout$ = CHR$(ANSI.ESC) + "[25m" + IF GJ_LIB_ANSI_EMU THEN _BLINK OFF + IF GJ_LIB_ANSI_OUTPUT THEN ANSI.mode_blinking_reset$ = sout$ + END FUNCTION + + + '' + ' Set inverse mode + ' + ' @return STRING with ANSI escape codes for setting mode + ' + FUNCTION ANSI.mode_inverse$() + DIM AS STRING sout + sout$ = CHR$(ANSI.ESC) + "[?5h" + sout$ = sout$ + CHR$(ANSI.ESC) + "[7m" + IF GJ_LIB_ANSI_EMU THEN + ANSI.old_fg_color& = ANSI.fg_color& + ANSI.old_bg_color& = ANSI.bg_color& + ANSI.fg_color& = ANSI.bg_color& + ANSI.bg_color& = ANSI.old_fg_color& + COLOR ANSI.fg_color&, ANSI.bg_color& + END IF + IF GJ_LIB_ANSI_OUTPUT THEN ANSI.mode_inverse$ = sout$ + END FUNCTION + + + '' + ' Reset inverse mode + ' + ' @return STRING with ANSI escape codes for resetting mode + ' + FUNCTION ANSI.mode_inverse_reset$() + DIM AS STRING sout + sout$ = CHR$(ANSI.ESC) + "[?5l" + sout$ = sout$ + CHR$(ANSI.ESC) + "[27m" + IF GJ_LIB_ANSI_EMU THEN COLOR ANSI.old_fg_color&, ANSI.old_bg_color& + IF GJ_LIB_ANSI_OUTPUT THEN ANSI.mode_inverse_reset$ = sout$ + END FUNCTION + + + '' + ' Set invisible mode + ' + ' @return STRING with ANSI escape codes for setting mode + ' + FUNCTION ANSI.mode_invisible$() + DIM AS STRING sout + sout$ = CHR$(ANSI.ESC) + "[8m" + IF GJ_LIB_ANSI_EMU THEN + ANSI.old_fg_color& = ANSI.fg_color& + ANSI.old_bg_color& = ANSI.bg_color& + ANSI.fg_color& = ANSI.bg_color& + COLOR ANSI.fg_color&, ANSI.bg_color& + END IF + IF GJ_LIB_ANSI_OUTPUT THEN ANSI.mode_invisible$ = sout$ + END FUNCTION + + + '' + ' Reset invisible mode + ' + ' @return STRING with ANSI escape codes for resetting mode + ' + FUNCTION ANSI.mode_invisible_reset$() + DIM AS STRING sout + sout$ = CHR$(ANSI.ESC) + "[28m" + IF GJ_LIB_ANSI_EMU THEN COLOR ANSI.old_fg_color&, ANSI.old_bg_color& + IF GJ_LIB_ANSI_OUTPUT THEN ANSI.mode_invisible_reset$ = sout$ + END FUNCTION + + + '' + ' Set strikethrough mode + ' + ' @return STRING with ANSI escape codes for setting mode + ' + FUNCTION ANSI.mode_strikethrough$() + DIM AS STRING sout + sout$ = CHR$(ANSI.ESC) + "[9m" + IF GJ_LIB_ANSI_OUTPUT THEN ANSI.mode_strikethrough$ = sout$ + END FUNCTION + + + '' + ' Reset strikethrough mode + ' + ' @return STRING with ANSI escape codes for resetting mode + ' + FUNCTION ANSI.mode_strikethrough_reset$() + DIM AS STRING sout + sout$ = CHR$(ANSI.ESC) + "[29m" + IF GJ_LIB_ANSI_OUTPUT THEN ANSI.mode_strikethrough_reset$ = sout$ + END FUNCTION + + + '' + ' Reset foreground color + ' + ' @return STRING with ANSI escape codes for resetting foreground color + ' + FUNCTION ANSI.fg_reset$() + DIM AS STRING sout + sout$ = CHR$(ANSI.ESC) + "[0m" + IF GJ_LIB_ANSI_EMU THEN COLOR 7 + IF GJ_LIB_ANSI_OUTPUT THEN ANSI.fg_reset$ = sout$ + END FUNCTION + + + '' + ' Reset background color + ' + ' @return STRING with ANSI escape codes for resetting background color + ' + FUNCTION ANSI.bg_reset$() + DIM AS STRING sout + sout$ = CHR$(ANSI.ESC) + "[0m" + IF GJ_LIB_ANSI_EMU THEN COLOR ,0 + IF GJ_LIB_ANSI_OUTPUT THEN ANSI.bg_reset$ = sout$ + END FUNCTION + + + '' + ' Set foreground color to black + ' + ' @return STRING with ANSI escape codes for setting color + ' + FUNCTION ANSI.fg_black$() + DIM AS STRING sout + sout$ = CHR$(ANSI.ESC) + "[30m" + IF GJ_LIB_ANSI_EMU THEN COLOR 0 + IF GJ_LIB_ANSI_OUTPUT THEN ANSI.fg_black$ = sout$ + END FUNCTION + + + '' + ' Set foreground color to bright black + ' + ' @return STRING with ANSI escape codes for setting color + ' + FUNCTION ANSI.fg_bright_black$() + DIM AS STRING sout + sout$ = CHR$(ANSI.ESC) + "[30;1m" + IF GJ_LIB_ANSI_EMU THEN COLOR 8 + IF GJ_LIB_ANSI_OUTPUT THEN ANSI.fg_bright_black$ = sout$ + END FUNCTION + + + '' + ' Set background color to black + ' + ' @return STRING with ANSI escape codes for setting color + ' + FUNCTION ANSI.bg_black$() + DIM AS STRING sout + sout$ = CHR$(ANSI.ESC) + "[0;40m" + IF GJ_LIB_ANSI_EMU THEN COLOR ,0 + IF GJ_LIB_ANSI_OUTPUT THEN ANSI.bg_black$ = sout$ + END FUNCTION + + + '' + ' Set background color to bright black + ' + ' @return STRING with ANSI escape codes for setting color + ' + FUNCTION ANSI.bg_bright_black$() + DIM AS STRING sout + sout$ = CHR$(ANSI.ESC) + "[100;1m" + IF GJ_LIB_ANSI_EMU THEN COLOR ,8 + IF GJ_LIB_ANSI_OUTPUT THEN ANSI.bg_bright_black$ = sout$ + END FUNCTION + + + '' + ' Set foreground color to blue + ' + ' @return STRING with ANSI escape codes for setting color + ' + FUNCTION ANSI.fg_blue$() + DIM AS STRING sout + sout$ = CHR$(ANSI.ESC) + "[0;34m" + IF GJ_LIB_ANSI_EMU THEN COLOR 1 + IF GJ_LIB_ANSI_OUTPUT THEN ANSI.fg_blue$ = sout$ + END FUNCTION + + + '' + ' Set foreground color to bright blue + ' + ' @return STRING with ANSI escape codes for setting color + ' + FUNCTION ANSI.fg_bright_blue$() + DIM AS STRING sout + sout$ = CHR$(ANSI.ESC) + "[34;1m" + IF GJ_LIB_ANSI_EMU THEN COLOR 9 + IF GJ_LIB_ANSI_OUTPUT THEN ANSI.fg_bright_blue$ = sout$ + END FUNCTION + + + '' + ' Set background color to blue + ' + ' @return STRING with ANSI escape codes for setting color + ' + FUNCTION ANSI.bg_blue$() + DIM AS STRING sout + sout$ = CHR$(ANSI.ESC) + "[44m" + IF GJ_LIB_ANSI_EMU THEN COLOR ,1 + IF GJ_LIB_ANSI_OUTPUT THEN ANSI.bg_blue$ = sout$ + END FUNCTION + + + '' + ' Set background color to bright blue + ' + ' @return STRING with ANSI escape codes for setting color + ' + FUNCTION ANSI.bg_bright_blue$() + DIM AS STRING sout + sout$ = CHR$(ANSI.ESC) + "[104;1m" + IF GJ_LIB_ANSI_EMU THEN COLOR ,9 + IF GJ_LIB_ANSI_OUTPUT THEN ANSI.bg_bright_blue$ = sout$ + END FUNCTION + + + '' + ' Set foreground color to green + ' + ' @return STRING with ANSI escape codes for setting color + ' + FUNCTION ANSI.fg_green$() + DIM AS STRING sout + sout$ = CHR$(ANSI.ESC) + "[0;32m" + IF GJ_LIB_ANSI_EMU THEN COLOR 2 + IF GJ_LIB_ANSI_OUTPUT THEN ANSI.fg_green$ = sout$ + END FUNCTION + + + '' + ' Set foreground color to bright green + ' + ' @return STRING with ANSI escape codes for setting color + ' + FUNCTION ANSI.fg_bright_green$() + DIM AS STRING sout + sout$ = CHR$(ANSI.ESC) + "[32;1m" + IF GJ_LIB_ANSI_EMU THEN COLOR 10 + IF GJ_LIB_ANSI_OUTPUT THEN ANSI.fg_bright_green$ = sout$ + END FUNCTION + + '' + ' Set background color to green + ' + ' @return STRING with ANSI escape codes for setting color + ' + FUNCTION ANSI.bg_green$() + DIM AS STRING sout + sout$ = CHR$(ANSI.ESC) + "[42m" + IF GJ_LIB_ANSI_EMU THEN COLOR ,2 + IF GJ_LIB_ANSI_OUTPUT THEN ANSI.bg_green$ = sout$ + END FUNCTION + + + '' + ' Set background color to bright green + ' + ' @return STRING with ANSI escape codes for setting color + ' + FUNCTION ANSI.bg_bright_green$() + DIM AS STRING sout + sout$ = CHR$(ANSI.ESC) + "[102;1m" + IF GJ_LIB_ANSI_EMU THEN COLOR ,10 + IF GJ_LIB_ANSI_OUTPUT THEN ANSI.bg_bright_green$ = sout$ + END FUNCTION + + + '' + ' Set foreground color to cyan + ' + ' @return STRING with ANSI escape codes for setting color + ' + FUNCTION ANSI.fg_cyan$() + DIM AS STRING sout + sout$ = CHR$(ANSI.ESC) + "[0;36m" + IF GJ_LIB_ANSI_EMU THEN COLOR 3 + IF GJ_LIB_ANSI_OUTPUT THEN ANSI.fg_cyan$ = sout$ + END FUNCTION + + + '' + ' Set foreground color to bright_cyan + ' + ' @return STRING with ANSI escape codes for setting color + ' + FUNCTION ANSI.fg_bright_cyan$() + DIM AS STRING sout + sout$ = CHR$(ANSI.ESC) + "[36;1m" + IF GJ_LIB_ANSI_EMU THEN COLOR 11 + IF GJ_LIB_ANSI_OUTPUT THEN ANSI.fg_bright_cyan$ = sout$ + END FUNCTION + + + '' + ' Set background color to cyan + ' + ' @return STRING with ANSI escape codes for setting color + ' + FUNCTION ANSI.bg_cyan$() + DIM AS STRING sout + sout$ = CHR$(ANSI.ESC) + "[46m" + IF GJ_LIB_ANSI_EMU THEN COLOR ,3 + IF GJ_LIB_ANSI_OUTPUT THEN ANSI.bg_cyan$ = sout$ + END FUNCTION + + + '' + ' Set background color to bright cyan + ' + ' @return STRING with ANSI escape codes for setting color + ' + FUNCTION ANSI.bg_bright_cyan$() + DIM AS STRING sout + sout$ = CHR$(ANSI.ESC) + "[106;1m" + IF GJ_LIB_ANSI_EMU THEN COLOR ,11 + IF GJ_LIB_ANSI_OUTPUT THEN ANSI.bg_bright_cyan$ = sout$ + END FUNCTION + + + '' + ' Set foreground color to red + ' + ' @return STRING with ANSI escape codes for setting color + ' + FUNCTION ANSI.fg_red$() + DIM AS STRING sout + sout$ = CHR$(ANSI.ESC) + "[0;31m" + IF GJ_LIB_ANSI_EMU THEN COLOR 4 + IF GJ_LIB_ANSI_OUTPUT THEN ANSI.fg_red$ = sout$ + END FUNCTION + + + '' + ' Set foreground color to bright red + ' + ' @return STRING with ANSI escape codes for setting color + ' + FUNCTION ANSI.fg_bright_red$() + DIM AS STRING sout + sout$ = CHR$(ANSI.ESC) + "[31;1m" + IF GJ_LIB_ANSI_EMU THEN COLOR 12 + IF GJ_LIB_ANSI_OUTPUT THEN ANSI.fg_bright_red$ = sout$ + END FUNCTION + + + '' + ' Set background color to red + ' + ' @return STRING with ANSI escape codes for setting color + ' + FUNCTION ANSI.bg_red$() + DIM AS STRING sout + sout$ = CHR$(ANSI.ESC) + "[41m" + IF GJ_LIB_ANSI_EMU THEN COLOR ,4 + IF GJ_LIB_ANSI_OUTPUT THEN ANSI.bg_red$ = sout$ + END FUNCTION + + + '' + ' Set background color to bright red + ' + ' @return STRING with ANSI escape codes for setting color + ' + FUNCTION ANSI.bg_bright_red$() + DIM AS STRING sout + sout$ = CHR$(ANSI.ESC) + "[101;1m" + IF GJ_LIB_ANSI_EMU THEN COLOR ,12 + IF GJ_LIB_ANSI_OUTPUT THEN ANSI.bg_bright_red$ = sout$ + END FUNCTION + + + '' + ' Set foreground color to magenta + ' + ' @return STRING with ANSI escape codes for setting color + ' + FUNCTION ANSI.fg_magenta$() + DIM AS STRING sout + sout$ = CHR$(ANSI.ESC) + "[0;35m" + IF GJ_LIB_ANSI_EMU THEN COLOR 5 + IF GJ_LIB_ANSI_OUTPUT THEN ANSI.fg_magenta$ = sout$ + END FUNCTION + + + '' + ' Set foreground color to bright magenta + ' + ' @return STRING with ANSI escape codes for setting color + ' + FUNCTION ANSI.fg_bright_magenta$() + DIM AS STRING sout + sout$ = CHR$(ANSI.ESC) + "[35;1m" + IF GJ_LIB_ANSI_EMU THEN COLOR 13 + IF GJ_LIB_ANSI_OUTPUT THEN ANSI.fg_bright_magenta$ = sout$ + END FUNCTION + + + '' + ' Set background color to magenta + ' + ' @return STRING with ANSI escape codes for setting color + ' + FUNCTION ANSI.bg_magenta$() + DIM AS STRING sout + sout$ = CHR$(ANSI.ESC) + "[45m" + IF GJ_LIB_ANSI_EMU THEN COLOR ,5 + IF GJ_LIB_ANSI_OUTPUT THEN ANSI.bg_magenta$ = sout$ + END FUNCTION + + + '' + ' Set background color to bright magenta + ' + ' @return STRING with ANSI escape codes for setting color + ' + FUNCTION ANSI.bg_bright_magenta$() + DIM AS STRING sout + sout$ = CHR$(ANSI.ESC) + "[105;1m" + IF GJ_LIB_ANSI_EMU THEN COLOR ,13 + IF GJ_LIB_ANSI_OUTPUT THEN ANSI.bg_bright_magenta$ = sout$ + END FUNCTION + + + '' + ' Set foreground color to yellow + ' + ' @return STRING with ANSI escape codes for setting color + ' + FUNCTION ANSI.fg_yellow$() + DIM AS STRING sout + sout$ = CHR$(ANSI.ESC) + "[0;33m" + IF GJ_LIB_ANSI_EMU THEN COLOR 6 + IF GJ_LIB_ANSI_OUTPUT THEN ANSI.fg_yellow$ = sout$ + END FUNCTION + + + '' + ' Set foreground color to bright yellow + ' + ' @return STRING with ANSI escape codes for setting color + ' + FUNCTION ANSI.fg_bright_yellow$() + DIM AS STRING sout + sout$ = CHR$(ANSI.ESC) + "[33;1m" + IF GJ_LIB_ANSI_EMU THEN COLOR 14 + IF GJ_LIB_ANSI_OUTPUT THEN ANSI.fg_bright_yellow$ = sout$ + END FUNCTION + + + '' + ' Set background color to yellow + ' + ' @return STRING with ANSI escape codes for setting color + ' + FUNCTION ANSI.bg_yellow$() + DIM AS STRING sout + sout$ = CHR$(ANSI.ESC) + "[43m" + IF GJ_LIB_ANSI_EMU THEN COLOR ,6 + IF GJ_LIB_ANSI_OUTPUT THEN ANSI.bg_yellow$ = sout$ + END FUNCTION + + + '' + ' Set background color to bright yellow + ' + ' @return STRING with ANSI escape codes for setting color + ' + FUNCTION ANSI.bg_bright_yellow$() + DIM AS STRING sout + sout$ = CHR$(ANSI.ESC) + "[103;1m" + IF GJ_LIB_ANSI_EMU THEN COLOR ,14 + IF GJ_LIB_ANSI_OUTPUT THEN ANSI.bg_bright_yellow$ = sout$ + END FUNCTION + + + '' + ' Set foreground color to white + ' + ' @return STRING with ANSI escape codes for setting color + ' + FUNCTION ANSI.fg_white$() + DIM AS STRING sout + sout$ = CHR$(ANSI.ESC) + "[0;37m" + IF GJ_LIB_ANSI_EMU THEN COLOR 7 + IF GJ_LIB_ANSI_OUTPUT THEN ANSI.fg_white$ = sout$ + END FUNCTION + + + '' + ' Set foreground color to bright white + ' + ' @return STRING with ANSI escape codes for setting color + ' + FUNCTION ANSI.fg_bright_white$() + DIM AS STRING sout + sout$ = CHR$(ANSI.ESC) + "[37;1m" + IF GJ_LIB_ANSI_EMU THEN COLOR 15 + IF GJ_LIB_ANSI_OUTPUT THEN ANSI.fg_bright_white$ = sout$ + END FUNCTION + + + '' + ' Set background color to white + ' + ' @return STRING with ANSI escape codes for setting color + ' + FUNCTION ANSI.bg_white$() + DIM AS STRING sout + sout$ = CHR$(ANSI.ESC) + "[47m" + IF GJ_LIB_ANSI_EMU THEN COLOR ,7 + IF GJ_LIB_ANSI_OUTPUT THEN ANSI.bg_white$ = sout$ + END FUNCTION + + + '' + ' Set background color to bright white + ' + ' @return STRING with ANSI escape codes for setting color + ' + FUNCTION ANSI.bg_bright_white$() + DIM AS STRING sout + sout$ = CHR$(ANSI.ESC) + "[107;1m" + IF GJ_LIB_ANSI_EMU THEN COLOR ,15 + IF GJ_LIB_ANSI_OUTPUT THEN ANSI.bg_bright_white$ = sout$ + END FUNCTION + + + '' + ' Sets text color foreground using 256 color mode + ' + ' @param INTEGER c% Color number (see link for color table) + ' @return STRING with ANSI escape codes for setting color + ' + FUNCTION ANSI.fg_256$(c%) + DIM AS STRING sout + sout$ = CHR$(ANSI.ESC) + "[38;5" + sout$ = sout$ + ";" + _TRIM$(STR$(c%)) + sout$ = sout$ + "m" + IF GJ_LIB_ANSI_EMU THEN COLOR c%, ANSI.bg_color& + IF GJ_LIB_ANSI_OUTPUT THEN ANSI.fg_256$ = sout$ + END FUNCTION + + + '' + ' Sets text color background using 256 color mode + ' + ' @param INTEGER c% Color number (see link for color table) + ' @return STRING with ANSI escape codes for setting color + ' + FUNCTION ANSI.bg_256$(c%) + DIM AS STRING sout + sout$ = CHR$(ANSI.ESC) + "[48;5" + sout$ = sout$ + ";" + _TRIM$(STR$(c%)) + sout$ = sout$ + "m" + IF GJ_LIB_ANSI_EMU THEN COLOR ANSI.fg_color& ,c% + IF GJ_LIB_ANSI_OUTPUT THEN ANSI.bg_256$ = sout$ + END FUNCTION + + + '' + ' Sets text color foreground using RGB 8-bit mode + ' + ' @param INTEGER r% Red value 0-255 + ' @param INTEGER g% Green value 0-255 + ' @param INTEGER b% Blue value 0-255 + ' @return STRING with ANSI escape codes for setting color + ' + FUNCTION ANSI.fg_rgb$(r%, g%, b%) + DIM AS STRING sout + sout$ = CHR$(ANSI.ESC) + "[38;2" + sout$ = sout$ + ";" + _TRIM$(STR$(r%)) + sout$ = sout$ + ";" + _TRIM$(STR$(g%)) + sout$ = sout$ + ";" + _TRIM$(STR$(b%)) + sout$ = sout$ + "m" + IF GJ_LIB_ANSI_EMU THEN COLOR _RGB(r%, g%, b%), ANSI.bg_color& + IF GJ_LIB_ANSI_OUTPUT THEN ANSI.fg_rgb$ = sout$ + END FUNCTION + + + '' + ' Sets text color background using RGB 8-bit mode + ' + ' @param INTEGER r% Red value 0-255 + ' @param INTEGER g% Green value 0-255 + ' @param INTEGER b% Blue value 0-255 + ' @return STRING with ANSI escape codes for setting color + ' + FUNCTION ANSI.bg_rgb$(r%, g%, b%) + DIM AS STRING sout + sout$ = CHR$(ANSI.ESC) + "[48;2" + sout$ = sout$ + ";" + _TRIM$(STR$(r%)) + sout$ = sout$ + ";" + _TRIM$(STR$(g%)) + sout$ = sout$ + ";" + _TRIM$(STR$(b%)) + sout$ = sout$ + "m" + IF GJ_LIB_ANSI_EMU THEN COLOR ANSI.fg_color& ,_RGB(r%, g%, b%) + IF GJ_LIB_ANSI_OUTPUT THEN ANSI.bg_rgb$ = sout$ + END FUNCTION + $END IF + $IF GJ_LIB_STRINGS_INC_BM = UNDEFINED THEN + '' + ' GRYMMJACK'S STRINGS LIB + ' + ' Some commonly used functions that I missed in QB64 coming from PHP + ' + ' @author Rick Christy + ' @uses STRINGS.BI + ' + $LET GJ_LIB_STRINGS_INC_BM = 1 + $LET DEBUGGING = 1 + + + + '' + ' Returns a string if n is true or false + ' + ' @param INTEGER n% to check + ' @param STRING if_false$ string + ' @param STRING if_true$ string + ' @return STRING representing true or false + ' + FUNCTION STR.bool$(n%, if_true$, if_false$) + IF n% = 0 THEN + STR.bool$ = if_false$ + ELSEIF n% = -1 THEN + STR.bool$ = if_true$ + END IF + END FUNCTION + + + '' + ' Check if string is a sentence: ends in .!? + ' + ' @param STRING s$ to check + ' @return INTEGER -1 if true, 0 if false + ' + FUNCTION STR.is_sentence%(s$) + DIM last_char AS STRING + last_char$ = RIGHT$(s$, 1) + IF last_char$ = "." OR last_char$ = "!" OR last_char$ = "?" THEN + STR.is_sentence% = -1 + ELSE + STR.is_sentence% = 0 + END IF + END FUNCTION + + + '' + ' Check if string is truthy: not null or -1 + ' + ' @param STRING s$ to check + ' @return INTEGER -1 if true, 0 if false + ' + FUNCTION STR.is_truthy%(s$) + IF s$ <> "" OR s$ = "-1" THEN + STR.is_truthy% = -1 + EXIT FUNCTION + END IF + STR.is_truthy% = 0 + END FUNCTION + + + '' + ' Check if string is falsy: null or 0 + ' + ' @param STRING s$ to check + ' @return INTEGER -1 if true, 0 if false + ' + FUNCTION STR.is_falsey%(s$) + IF s$ = "" OR s$ = "0" THEN + STR.is_falsey% = -1 + EXIT FUNCTION + END IF + STR.is_falsey% = 0 + END FUNCTION + + + '' + ' Check if string is null + ' + ' @param STRING s$ to check + ' @return INTEGER -1 if true, 0 if false + ' + FUNCTION STR.is_empty%(s$) + IF s$ = "" THEN + STR.is_empty% = -1 + EXIT FUNCTION + END IF + STR.is_empty% = 0 + END FUNCTION + + + '' + ' Check if string consists purely of space and tab characters + ' + ' @param STRING s$ to check + ' @return INTEGER -1 if true, 0 if false + ' + FUNCTION STR.is_blank%(s$) + DIM AS INTEGER i + IF s$ = "" THEN EXIT FUNCTION + i% = 1 + DO: + IF GJ_LIB_isblank(ASC(s$, i%)) = 0 THEN + STR.is_blank% = 0 + EXIT FUNCTION + END IF + i% = i% + 1 + LOOP UNTIL i% = LEN(s$) + 1 + STR.is_blank% = -1 + END FUNCTION + + + '' + ' Check if string consists purely of hexadecimal characters: + ' ASCII 0-9 A-F + ' + ' @param STRING s$ to check + ' @return INTEGER -1 if true, 0 if false + ' + FUNCTION STR.is_hexadecimal%(s$) + DIM AS INTEGER i + IF s$ = "" THEN EXIT FUNCTION + i% = 1 + DO: + IF GJ_LIB_isxdigit(ASC(s$, i%)) = 0 THEN + STR.is_hexadecimal% = 0 + EXIT FUNCTION + END IF + i% = i% + 1 + LOOP UNTIL i% = LEN(s$) + 1 + STR.is_hexadecimal% = -1 + END FUNCTION + + + '' + ' Check if string consists purely of control characters: + ' ASCII 0-31 + ' + ' @param STRING s$ to check + ' @return INTEGER -1 if true, 0 if false + ' + FUNCTION STR.is_control_chars%(s$) + DIM AS INTEGER i + IF s$ = "" THEN EXIT FUNCTION + i% = 1 + DO: + IF ASC(s$, i%) = 0 OR ASC(s$, i%) > 31 THEN + STR.is_control_chars% = 0 + EXIT FUNCTION + END IF + i% = i% + 1 + LOOP UNTIL i% = LEN(s$) + 1 + STR.is_control_chars% = -1 + END FUNCTION + + + '' + ' Check if string consists purely of punctuation characters: + ' !"#$%&'()*+,-./:;<=>?@[\]^_`{|}~ + ' + ' @param STRING s$ to check + ' @return INTEGER -1 if true, 0 if false + ' + FUNCTION STR.is_punctuation%(s$) + DIM AS INTEGER i + IF s$ = "" THEN EXIT FUNCTION + i% = 1 + DO: + IF GJ_LIB_ispunct%(ASC(s$, i%)) = 0 THEN + STR.is_punctuation% = 0 + EXIT FUNCTION + END IF + i% = i% + 1 + LOOP UNTIL i% = LEN(s$) + 1 + STR.is_punctuation% = -1 + END FUNCTION + + + '' + ' Check if string consists purely of graphic characters: + ' it is either a number (0123456789), + ' an uppercase letter (ABCDEFGHIJKLMNOPQRSTUVWXYZ), + ' a lowercase letter (abcdefghijklmnopqrstuvwxyz), + ' or a punctuation character(!"#$%&'()*+,-./:;<=>?@[\]^_`{|}~), + ' or any graphical character specific to the current C locale. + ' + ' @param STRING s$ to check + ' @return INTEGER -1 if true, 0 if false + ' + FUNCTION STR.is_graphical%(s$) + DIM AS INTEGER i + IF s$ = "" THEN EXIT FUNCTION + i% = 1 + DO: + IF GJ_LIB_isgraph%(ASC(s$, i%)) = 0 THEN + STR.is_graphical% = 0 + EXIT FUNCTION + END IF + i% = i% + 1 + LOOP UNTIL i% = LEN(s$) + 1 + STR.is_graphical% = -1 + END FUNCTION + + + '' + ' Check if string consists purely of printable characters: + ' ASCII: &H20 (" ") to &H7E (~) + ' + ' @param STRING s$ to check + ' @return INTEGER -1 if true, 0 if false + ' + FUNCTION STR.is_printable%(s$) + DIM AS INTEGER i + IF s$ = "" THEN EXIT FUNCTION + i% = 1 + DO: + IF GJ_LIB_isprint%(ASC(s$, i%)) = 0 THEN + STR.is_printable% = 0 + EXIT FUNCTION + END IF + i% = i% + 1 + LOOP UNTIL i% = LEN(s$) + 1 + STR.is_printable% = -1 + END FUNCTION + + + '' + ' Check if string consists purely of space characters: + ' space, formfeed, newline, return, tab, vertical tab + ' + ' @param STRING s$ to check + ' @return INTEGER -1 if true, 0 if false + ' + FUNCTION STR.is_white_space%(s$) + DIM AS INTEGER i + IF s$ = "" THEN EXIT FUNCTION + i% = 1 + DO: + IF GJ_LIB_isspace%(ASC(s$, i%)) = 0 THEN + STR.is_white_space% = 0 + EXIT FUNCTION + END IF + i% = i% + 1 + LOOP UNTIL i% = LEN(s$) + 1 + STR.is_white_space% = -1 + END FUNCTION + + + '' + ' Check if string consists purely of lower case characters + ' + ' @param STRING s$ to check + ' @return INTEGER -1 if true, 0 if false + ' + FUNCTION STR.is_lower_case%(s$) + DIM AS INTEGER i + IF s$ = "" THEN EXIT FUNCTION + i% = 1 + DO: + IF GJ_LIB_islower%(ASC(s$, i%)) = 0 THEN + STR.is_lower_case% = 0 + EXIT FUNCTION + END IF + i% = i% + 1 + LOOP UNTIL i% = LEN(s$) + 1 + STR.is_lower_case% = -1 + END FUNCTION + + + '' + ' Check if string consists purely of upper case characters + ' + ' @param STRING s$ to check + ' @return INTEGER -1 if true, 0 if false + ' + FUNCTION STR.is_upper_case%(s$) + DIM AS INTEGER i + IF s$ = "" THEN EXIT FUNCTION + i% = 1 + DO: + IF GJ_LIB_isupper%(ASC(s$, i%)) = 0 THEN + STR.is_upper_case% = 0 + EXIT FUNCTION + END IF + i% = i% + 1 + LOOP UNTIL i% = LEN(s$) + 1 + STR.is_upper_case% = -1 + END FUNCTION + + + '' + ' Check if string consists purely of numbers + ' + ' @param STRING s$ to check + ' @return INTEGER -1 if true, 0 if false + ' + FUNCTION STR.is_numeric%(s$) + DIM AS INTEGER i + IF s$ = "" THEN EXIT FUNCTION + i% = 1 + DO: + IF GJ_LIB_isdigit%(ASC(s$, i%)) = 0 THEN + STR.is_numeric% = 0 + EXIT FUNCTION + END IF + i% = i% + 1 + LOOP UNTIL i% = LEN(s$) + 1 + STR.is_numeric% = -1 + END FUNCTION + + + '' + ' Check if string consists purely of alphabetical characters + ' + ' @param STRING s$ to check + ' @return INTEGER -1 if true, 0 if false + ' + FUNCTION STR.is_alpha%(s$) + DIM AS INTEGER i + IF s$ = "" THEN EXIT FUNCTION + i% = 1 + DO: + IF GJ_LIB_isalpha%(ASC(s$, i%)) = 0 THEN + STR.is_alpha% = 0 + EXIT FUNCTION + END IF + i% = i% + 1 + LOOP UNTIL i% = LEN(s$) + 1 + STR.is_alpha% = -1 + END FUNCTION + + + '' + ' Check if string consists purely of alphabet characters or numbers + ' + ' @param STRING s$ to check + ' @return INTEGER -1 if true, 0 if false + ' + FUNCTION STR.is_alpha_numeric%(s$) + DIM AS INTEGER i + IF s$ = "" THEN EXIT FUNCTION + i% = 1 + DO: + IF GJ_LIB_isalnum%(ASC(s$, i%)) = 0 THEN + STR.is_alpha_numeric% = 0 + EXIT FUNCTION + END IF + i% = i% + 1 + LOOP UNTIL i% = LEN(s$) + 1 + STR.is_alpha_numeric% = -1 + END FUNCTION + + + '' + ' Implodes a string array into a string using delimiter as glue + ' + ' @param STRING ARRAY arr$() to implode from + ' @param STRING delim$ Delimiter to glue the array parts together with + ' @return STRING of array parts glued together with delimiter + ' + FUNCTION STR.implode$(arr$(), delim$) + DIM AS STRING res + DIM AS INTEGER lb, ub, i + res$ = "" + lb% = LBOUND(arr$) : ub% = UBOUND(arr$) + FOR i% = lb% TO ub% + IF i% + 1 <= ub% THEN + res$ = res$ + arr$(i) + delim$ + ELSE + res$ = res$ + arr$(i) + END IF + NEXT i% + STR.implode$ = res$ + END FUNCTION + + + '' + ' Explodes a string into an array of strings using a delimiter + ' + ' If the delimiter is not found, returns the target as dest$(0) + ' + ' @param STRING target$ to explode + ' @param STRING delim$ delimiter + ' @param STRING ARRAY dest$() to explode into + ' @param INTEGER numParts% the number of strings in the array + ' + SUB STR.explode(target$, delim$, dest$(), numParts%) + DIM AS INTEGER length, delimLen, numFound, i + length% = LEN(target$) : delimLen% = LEN(delim$) : numFound% = 0 + DIM delimsPos(length%) AS INTEGER + IF length% = 0 THEN EXIT SUB + + CALL STR.find_pos(target$, delim$, delimsPos%(), numFound%) + + IF numFound% <= 0 THEN + numParts% = 0 + dest$(0) = target$ + ELSE + REDIM _PRESERVE delimsPos%(numFound% - 1) + IF numFound% = 1 THEN + numParts% = 1 + dest$(0) = LEFT$(target$, delimsPos%(0) - delimLen%) + dest$(1) = MID$(target$, delimsPos%(0) + delimLen%) + ELSEIF numFound% > 1 THEN + dest$(0) = LEFT$(target$, delimsPos%(0) - delimLen%) + FOR i% = 1 TO numFound% + IF i% + 1 <= numFound% THEN + dest$(i%) = MID$( _ + target$, _ + delimsPos%(i% - 1) + delimLen%, _ + delimsPos%(i%) - delimsPos%(i% - 1) - delimLen% _ + ) + END IF + NEXT i% + dest$(numFound%) = MID$( _ + target$, delimsPos%(numFound% - 1) + delimLen% _ + ) + numParts% = numFound% + END IF + END IF + END SUB + + + '' + ' Searches for strings inside of strings and fills array with found positions + ' + ' @param STRING target$ to search + ' @param STRING search$ for in target + ' @param INTEGER ARRAY arrFound%() populate with positions search found + ' @param INTEGER numFound% times search found a match + ' + SUB STR.find_pos(target$, search$, arrFound%(), numFound%) + DIM AS INTEGER length, found, x, i, ub, searchLen + length% = LEN(target$) : found% = -1 : x% = 0: i% = 0 + ub% = UBOUND(arrFound%) + searchLen% = LEN(search$) + DO WHILE i% <= length% + found% = INSTR(i%, target$, search$) + IF found% > 0 AND x% <= ub% THEN + arrFound%(x%) = found% + i% = found% + searchLen% + x% = x% + 1 + ELSE + i% = i% + 1 + END IF + LOOP + numFound% = x% + END SUB + + + '' + ' Insert a string into another string at position + ' + ' @param STRING s$ to insert into + ' @param STRING ins$ insert + ' @param INTEGER p% position to insert + ' @return STRING with insertion + ' + FUNCTION STR.insert$(s$, ins$, p%) + IF p% < LEN(s$) AND ins$ <> "" THEN + IF p% = 0 THEN + STR.insert$ = ins$ + RIGHT$(s$, LEN(s$) + LEN(ins$) - 1) + ELSE + STR.insert$ = LEFT$(s$, p%+1) + ins$ + RIGHT$(s$, LEN(s$) - p%-1) + END IF + ELSE + STR.insert$ = s$ + END IF + END FUNCTION + + + '' + ' Remove a string from a string + ' + ' @param STRING s$ to remove from + ' @param STRING del$ to delete + ' @param INTEGER count% times to remove + ' @return STRING with del$ removed + ' + FUNCTION STR.remove$(s$, del$, count%) + DIM AS INTEGER p + IF count% = -1 THEN + DO + p% = INSTR(s$, del$) + s$ = STR.del$(s$, del$) + LOOP UNTIL p% = 0 + ELSE + DO + p% = INSTR(s$, del$) + s$ = STR.del$(s$, del$) + count% = count% - 1 + LOOP UNTIL p% = 0 OR count% = 0 + END IF + STR.remove$ = s$ + END FUNCTION + + + '' + ' Delete a string from a string once (helper for STR.remove$) + ' + ' @param STRING s$ to delete from + ' @param STRING del$ to delete + ' @return STRING with del$ deleted + ' + FUNCTION STR.del$(s$, del$) + DIM AS INTEGER i + i% = INSTR(s$, del$) + IF i% THEN + STR.del$ = LEFT$(s$, i%-1) + RIGHT$(s$, LEN(s$) - (i% + LEN(del$))+1) + ELSE + STR.del$ = s$ + END IF + END FUNCTION + + + '' + ' Replaces a string with another string inside a string + ' + ' @param STRING s$ to replace within + ' @param STRING search$ + ' @param STRING rep$ string to replace search with if found + ' @param INTEGER count% number of times to replace + ' @return STRING with replacements + ' + FUNCTION STR.replace$(s$, search$, rep$, count%) + DIM AS INTEGER p + IF count% = -1 THEN + DO + p% = INSTR(s$, search$) + s$ = STR.rep$(s$, search$, rep$) + LOOP UNTIL p% = 0 + ELSE + DO + p% = INSTR(s$, search$) + s$ = STR.rep$(s$, search$, rep$) + count% = count% - 1 + LOOP UNTIL p% = 0 OR count% = 0 + END IF + STR.replace$ = s$ + END FUNCTION + + + '' + ' Reverses a string + ' + ' @param STRING s$ to reverse + ' @return STRING reversed string + ' + FUNCTION STR.reverse$(s$) + DIM AS INTEGER i, l + DIM AS STRING res + res$ = "" + l% = LEN(s$) + IF l% = 0 THEN EXIT FUNCTION + FOR i% = l% TO 1 STEP -1 + res$ = res$ + CHR$(ASC(s$, i%)) + NEXT i% + STR.reverse$ = res$ + END FUNCTION + + + '' + ' Shuffles (randomizes) the characters in a string + ' + ' @param STRING s$ string to reverse + ' @return STRING shuffled string + ' + FUNCTION STR.shuffle$(s$) + DIM AS INTEGER r + DIM AS STRING c, ls, rs, res + IF LEN(s$) = 0 THEN EXIT FUNCTION + RANDOMIZE TIMER + DO + r% = INT(RND * LEN(s$) + 1) ' random pos in diminishing string + c$ = MID$(s$, r%, 1) ' random char at pos from diminishing string + ls$ = MID$(s$, 1, r% - 1) ' left side of diminishing string sans c$ + rs$ = MID$(s$, r% + 1) ' right side of diminishing string sans c$ + s$ = ls$ + rs$ ' diminish the string (remove c$) + res$ = res$ + c$ ' build the returned string + LOOP UNTIL LEN(s$) = 0 + STR.shuffle$ = res$ + END FUNCTION + + + '' + ' Pads both sides of a string with num% chars + ' + ' @param STRING s$ string to pad + ' @param STRING char$ character to use for padding + ' @param INTEGER num% number of characters to pad to + ' @return STRING padded at the end + FUNCTION STR.pad_both$(s$, char$, num%) + STR.pad_both$ = STR.pad_end$(STR.pad_start$(s$, char$, num%), char$, num%) + END FUNCTION + + + '' + ' Pads the end of a string with num% chars + ' + ' @param STRING s$ string to pad + ' @param STRING char$ character to use for padding + ' @param INTEGER num% number of characters to pad to + ' @return STRING padded at the end + FUNCTION STR.pad_end$(s$, char$, num%) + STR.pad_end$ = s$ + STRING$(num%, char$) + END FUNCTION + + + '' + ' Repeats a string num times + ' + ' @param STRING s$ string to repeat + ' @param INTEGER num% number of times to repeat + ' @return STRING repeated + FUNCTION STR.repeat$(s$, num%) + DIM i AS INTEGER + DIM res AS STRING + res$ = "" + FOR i% = 1 TO num% + res$ = res$ + s$ + NEXT i% + STR.repeat$ = res$ + END FUNCTION + + + '' + ' Determines if a string starts with another string + ' + ' @param STRING s$ string to check + ' @param INTEGER chars$ chars to check if string starts with + ' @return INTEGER -1 if starts with 0 if not + FUNCTION STR.starts_with%(s$, chars$) + STR.starts_with% = (LEFT$(s$, LEN(chars$)) = chars$) + END FUNCTION + + + '' + ' Determines if a string ends with another string + ' + ' @param STRING s$ string to check + ' @param INTEGER chars$ chars to check if string ends with + ' @return INTEGER -1 if ends with 0 if not + FUNCTION STR.ends_with%(s$, chars$) + STR.ends_with% = (RIGHT$(s$, LEN(chars$)) = chars$) + END FUNCTION + + + '' + ' Pads the start of a string with num% chars + ' + ' @param STRING s$ string to pad + ' @param STRING char$ character to use for padding + ' @param INTEGER num% number of characters to pad to + ' @return STRING padded at the end + FUNCTION STR.pad_start$(s$, char$, num%) + STR.pad_start$ = STRING$(num%, char$) + s$ + END FUNCTION + + + '' + ' Replaces a string with another string once (helper for STR.replace$) + ' + ' @param STRING s$ to replace within + ' @param STRING search$ + ' @param STRING rep$ string to replace search with if found + ' @return STRING with replacement + ' + FUNCTION STR.rep$(s$, search$, rep$) + DIM AS INTEGER p + p% = INSTR(s$, search$) + IF p% THEN + s$ = LEFT$(s$, p%-1) + RIGHT$(s$, LEN(s$) - p% - LEN(search$)+1) + STR.rep$ = LEFT$(s$, p%-1) + rep$ + RIGHT$(s$, LEN(s$) - p%+1) + ELSE + STR.rep$ = s$ + END IF + END FUNCTION + + + '' + ' Returns part of a string from start pos. to end pos. + ' NOTE: This is different than MID$ as MID$ specifies a start and a length, + ' NOT an end position. + ' + ' @param STRING s$ to slice from + ' @param INTEGER startPos% to start slice from + ' @param INTEGER endPos% to end slice from + ' @return STRING of sliced portion of original stright + ' + FUNCTION STR.slice_pos$(s$, startPos%, endPos%) + IF startPos% <= 0 THEN + startPos% = 1 + END IF + IF endPos% > 0 THEN + STR.slice_pos$ = MID$(s$, startPos%, endPos%-startPos%) + ELSE + STR.slice_pos$ = MID$(s$, startPos%) + END IF + END FUNCTION + + + '' + ' Returns a space trimmed _UNSIGNED _BYTE as a string + ' + ' @param _UNSIGNED _BYTE n~%% number to return + ' @return STRING space trimmed number + ' + FUNCTION STR.ub$(n~%%) + STR.ub$ = _TRIM$(STR$(n~%%)) + END FUNCTION + + + '' + ' Returns a space trimmed _UNSIGNED INTEGER as a string + ' + ' @param _UNSIGNED INTEGER n~% number to return + ' @return STRING space trimmed number + ' + FUNCTION STR.ui$(n~%) + STR.ui$ = _TRIM$(STR$(n~%)) + END FUNCTION + + + '' + ' Returns a space trimmed _UNSIGNED LONG as a string + ' + ' @param _UNSIGNED LONG n~& number to return + ' @return STRING space trimmed number + ' + FUNCTION STR.ul$(n~&) + STR.ul$ = _TRIM$(STR$(n~&)) + END FUNCTION + + + '' + ' Returns a space trimmed _BYTE as a string + ' + ' @param _BYTE n~% number to return + ' @return STRING space trimmed number + ' + FUNCTION STR.b$(n%%) + STR.b$ = _TRIM$(STR$(n%%)) + END FUNCTION + + + '' + ' Returns a space trimmed INTEGER as a string + ' + ' @param INTEGER n% number to return + ' @return STRING space trimmed number + ' + FUNCTION STR.i$(n%) + STR.i$ = _TRIM$(STR$(n%)) + END FUNCTION + + + '' + ' Returns a space trimmed LONG as a string + ' + ' @param LONG n& number to return + ' @return STRING space trimmed number + ' + FUNCTION STR.l$(n&) + STR.l$ = _TRIM$(STR$(n&)) + END FUNCTION + + + '' + ' Returns a space trimmed SINGLE as a string + ' + ' @param SINGLE n! number to return + ' @return STRING space trimmed number + ' + FUNCTION STR.s$(n!) + STR.s$ = _TRIM$(STR$(n!)) + END FUNCTION + + + '' + ' Returns a space trimmed DOUBLE as a string + ' + ' @param DOUBLE n& number to return + ' @return STRING space trimmed number + ' + FUNCTION STR.d$(n#) + STR.d$ = _TRIM$(STR$(n#)) + END FUNCTION + + + '' + ' Returns a space trimmed _FLOAT as a string + ' + ' @param _FLOAT n& number to return + ' @return STRING space trimmed number + ' + FUNCTION STR.f$(n##) + STR.f$ = _TRIM$(STR$(n##)) + END FUNCTION + $END IF + $END IF + $IF GJ_LIB_SYS_INC_BM = UNDEFINED THEN + '' + ' QB64_GJ_LIB + ' GRYMMJACK'S SYS LIB + ' + ' Contains misc. helpful utils/tools + ' + ' USAGE: + ' Insert '$INCLUDE:'path_to_GJ_LIB/SYS/SYS.BI' at the top of file + ' Insert '$INCLUDE:'path_to_GJ_LIB/SYS/SYS.BM' at the bottom of file + ' + ' @author Rick Christy + ' + $LET GJ_LIB_SYS_INC_BM = 1 + + + + '' + ' Opens a URL in the default web browser + ' + ' ex: open_url_in_browser "https://youtube.com/grymmjack" + ' + ' @param STRING url$ to open (any protocol just passes through) + ' + SUB open_url_in_browser(url$) + DIM AS STRING cmd + DIM AS INTEGER ret + $IF WINDOWS THEN + cmd$ = "start " + url$ + $ELSEIF MAC THEN + cmd$ = "open " + url$ + $ELSEIF LINUX THEN + cmd$ = "xdg-open " + url$ + $END IF + ret% = _SHELLHIDE(cmd$) + END SUB + + + '' + ' Get system information as a big string + ' + ' @return STRING of system information + ' + FUNCTION sys_info$() + DIM AS STRING sout, nl + nl$ = CHR$(10) ' For some reason CHR$(13) won't combine strings + sout$ = "" + sout$ = sout$ + "QB64 " + _OS$ + nl$ + sout$ = sout$ + " _CWD$: " + _CWD$ + nl$ + sout$ = sout$ + " _STARTDIR$: " + _STARTDIR$ + nl$ + sout$ = sout$ + " COMMAND$: " + COMMAND$ + nl$ + sout$ = sout$ + "SCREEN" + nl$ + sout$ = sout$ + " Width: " + _TRIM$(STR$(_WIDTH)) + nl$ + sout$ = sout$ + " Height: " + _TRIM$(STR$(_HEIGHT)) + nl$ + sout$ = sout$ + " X: " + _TRIM$(STR$(_SCREENX)) + nl$ + sout$ = sout$ + " Y: " + _TRIM$(STR$(_SCREENY)) + nl$ + sout$ = sout$ + " BPP: " + _TRIM$(STR$(_PIXELSIZE)) + nl$ + sout$ = sout$ + " _DISPLAY: " + _TRIM$(STR$(_DISPLAY)) + nl$ + sout$ = sout$ + " _DEST: " + _TRIM$(STR$(_DEST)) + nl$ + sout$ = sout$ + " _SOURCE: " + _TRIM$(STR$(_SOURCE)) + nl$ + sout$ = sout$ + "DESKTOP" + nl$ + sout$ = sout$ + " Width: " + _TRIM$(STR$(_DESKTOPWIDTH)) + nl$ + sout$ = sout$ + " Height: " + _TRIM$(STR$(_DESKTOPHEIGHT)) + nl$ + sout$ = sout$ + "FONTS" + nl$ + sout$ = sout$ + " Font Width: " + _TRIM$(STR$(_FONTWIDTH)) + nl$ + sout$ = sout$ + " Font Height: " + _TRIM$(STR$(_FONTHEIGHT)) + nl$ + sout$ = sout$ + " _PRINTMODE: " + _TRIM$(STR$(_PRINTMODE)) + nl$ + sout$ = sout$ + "MISC" + nl$ + sout$ = sout$ + " _CONTROLCHR: " + _TRIM$(STR$(_CONTROLCHR)) + nl$ + sout$ = sout$ + " _BLINK: " + _TRIM$(STR$(_BLINK)) + nl$ + sout$ = sout$ + " _DEFAULTCOLOR: " + _TRIM$(STR$(_DEFAULTCOLOR)) + nl$ + sout$ = sout$ + "_BACKGROUNDCOLOR: " + _TRIM$(STR$(_BACKGROUNDCOLOR)) + nl$ + sout$ = sout$ + " _CLIPBOARD$:" + nl$ + sout$ = sout$ + _CLIPBOARD$ + nl$ + sys_info$ = sout$ + END FUNCTION + + + '' + ' Get devices as a big string + ' + ' @return STRING of devices and button information + ' + FUNCTION device_info$() + DIM AS STRING sout, nl, device_name, snum_buttons + DIM AS INTEGER i, num_buttons, device_count + nl$ = CHR$(10) ' For some reason CHR$(13) won't combine strings + sout$ = "" + device_count% = _DEVICES + FOR i% = 1 TO device_count% + device_name$ = _DEVICE$(i%) + num_buttons% = _LASTBUTTON(i%) + snum_buttons$ = _TRIM$(STR$(num_buttons%)) + sout$ = sout$ + "DEVICE: " + device_name$ + " #BTN: " + snum_buttons$ + nl$ + NEXT i% + device_info$ = sout$ + END FUNCTION + $END IF + $IF GJ_LIB_VECT2D_INC_BM = UNDEFINED THEN + '' + ' QB64_GJ_LIB + ' GRYMMJACK'S VECT2D LIB + ' + ' 2D Vector support for QB64 + ' + ' USAGE: + ' Insert '$INCLUDE:'path_to_GJ_LIB/VECT2D/VECT2D.BI' at the top of file + ' Insert '$INCLUDE:'path_to_GJ_LIB/VECT2D/VECT2D.BM' at the bottom of file + ' + ' @author Rick Christy + ' @author Evan Shortiss + ' @support William Barnes + ' + $LET GJ_LIB_VECT2D_INC_BM = 1 + + + '' + ' Sets both x and y axes of the vector + ' + ' @param VECT2D vret Vector with both axes set + ' @param SINGLE x x axis + ' @param SINGLE y y axis + ' @return VECT2D inside vret + ' + SUB VECT2D.setAxes(vret AS VECT2D, x AS SINGLE, y AS SINGLE) + vret.x! = x! + vret.y! = y! + END SUB + + + '' + ' Sets x axis of the vector + ' + ' @param VECT2D vret Vector with x axis set + ' @param SINGLE x x axis + ' @return VECT2D inside vret + ' + SUB VECT2D.setX(vret AS VECT2D, x AS SINGLE) + vret.x! = x! + END SUB + + + '' + ' Sets y axis of the vector + ' + ' @param VECT2D vret Vector with y axis set + ' @param SINGLE y y axis + ' @return VECT2D inside vret + ' + SUB VECT2D.setY(vret AS VECT2D, y AS SINGLE) + vret.y! = y! + END SUB + + + '' + ' Returns VECT2D as a string + ' + ' @param VECT2D vec1 Vector to return as string + ' @param INTEGER (TRUE/FALSE) round x and y? + ' @return STRING representation of VECT2D + ' + FUNCTION VECT2D$(vec1 AS VECT2D, rounded AS INTEGER) + IF rounded% = FALSE THEN + VECT2D$ = "(" _ + + _TRIM$(STR$(vec1.x!)) _ + + ", " + _TRIM$(STR$(vec1.y!)) _ + + ")" + ELSE + DIM vret AS VECT2D + VECT2D.round vret, vec1 + VECT2D$ = "(" _ + + _TRIM$(STR$(vret.x!)) _ + + ", " + _TRIM$(STR$(vret.y!)) _ + + ")" + END IF + END FUNCTION + + + '' + ' Get x axis of VECT2D + ' + ' @param VECT2D vec1 Vector to get x axis for + ' @return SINGLE x axis + ' + FUNCTION VECT2D.getX!(vec1 AS VECT2D) + VECT2D.getX! = vec1.x! + END FUNCTION + + + + '' + ' Get y axis of VECT2D + ' + ' @param VECT2D vec1 Vector to get y axis for + ' @return SINGLE y axis + ' + FUNCTION VECT2D.getY!(vec1 AS VECT2D) + VECT2D.getY! = vec1.y! + END FUNCTION + + + '' + ' Add two VECT2D axes together + ' + ' @param VECT2D vret Return vector with result of addition + ' @param VECT2D vec1 Left VECT2D operand + ' @param VECT2D vec2 Right VECT2D operand + ' @return VECT2D inside vret + ' + SUB VECT2D.add(vret AS VECT2D, vec1 AS VECT2D, vec2 AS VECT2D) + vret.x! = vec1.x! + vec2.x! + vret.y! = vec1.y! + vec2.y! + END SUB + + + '' + ' Subtract two VECT2D axes from each other + ' + ' @param VECT2D vret Return vector with result of subtraction + ' @param VECT2D vec1 Left VECT2D operand + ' @param VECT2D vec2 Right VECT2D operand + ' @return VECT2D inside vret + ' + SUB VECT2D.sub(vret AS VECT2D, vec1 AS VECT2D, vec2 AS VECT2D) + vret.x! = vec1.x! - vec2.x! + vret.y! = vec1.y! - vec2.y! + END SUB + + + '' + ' Multiply two VECT2D axes together + ' + ' @param VECT2D vret Return vector with result of mulitplication + ' @param VECT2D vec1 Left VECT2D operand + ' @param VECT2D vec2 Right VECT2D operand + ' @return VECT2D inside vret + ' + SUB VECT2D.multByVECT2D(vret AS VECT2D, vec1 AS VECT2D, vec2 AS VECT2D) + vret.x! = vec1.x! * vec2.x! + vret.y! = vec1.y! * vec2.y! + END SUB + + + '' + ' Multiply VECT2D axes by a single number + ' + ' @param VECT2D vret Return vector with result of mulitplication + ' @param VECT2D vec1 VECT2D to multiply axes of + ' @param SINGLE n Number to mulitply by + ' @return VECT2D inside vret + ' + SUB VECT2D.multBySingle(vret AS VECT2D, vec1 AS VECT2D, n AS single) + vret.x! = vec1.x! * n! + vret.y! = vec1.y! * n! + END SUB + + + '' + ' Divide two VECT2D axes from each other + ' + ' @param VECT2D vret Return vector with result of division + ' @param VECT2D vec1 Left VECT2D operand + ' @param VECT2D vec2 Right VECT2D operand + ' @return VECT2D inside vret + ' + SUB VECT2D.divByVECT2D(vret AS VECT2D, vec1 AS VECT2D, vec2 AS VECT2D) + vret.x! = vec1.x! / vec2.x! + vret.y! = vec1.y! / vec2.y! + END SUB + + + '' + ' Divide VECT2D axes by a single number + ' + ' @param VECT2D vret Return vector with result of division + ' @param VECT2D vec1 VECT2D to divide axes of + ' @param SINGLE n Number to divide by + ' @return VECT2D inside vret + ' + SUB VECT2D.divBySingle(vret AS VECT2D, vec1 AS VECT2D, n AS SINGLE) + vret.x! = vec1.x! / n! + vret.y! = vec1.y! / n! + END SUB + + + '' + ' Normalize a VECT2D into a unit vector + ' + ' @param VECT2D vret Return vector normalized + ' @param VECT2D vec1 Vector to normalize + ' @return VECT2D inside vret + ' + SUB VECT2D.normalize(vret AS VECT2D, vec1 AS VECT2D) + DIM magnitude AS SINGLE + magnitude! = VECT2D.magnitude(vec1.x!, vec1.y!) + VECT2D.divBySingle vret, vec1, magnitude! + END SUB + + + '' + ' Normalize a VECT2D into a unit vector (alias) + ' + ' @param VECT2D vret Return vector normalized + ' @param VECT2D vec1 Vector to normalize + ' @return VECT2D inside vret + ' + SUB VECT2D.unit(vret AS VECT2D, vec1 AS VECT2D) + VECT2D.normalize vret, vec1 + END SUB + + + '' + ' Reverse both VECT2D axes (invert sign) + ' + ' @param VECT2D vret Return vector with axes reversed/inverted + ' @param VECT2D vec1 Vector to reverse/invert + ' @return VECT2D inside vret + ' + SUB VECT2D.reverse(vret AS VECT2D, vec1 AS VECT2D) + vret.x! = -vec1.x! + vret.y! = -vec1.y! + END SUB + + + '' + ' Get absolute values for VECT2D axes (ignore sign) + ' + ' @param VECT2D vret Return vector with unsigned axes + ' @param VECT2D vec1 Vector to get axes for + ' @return VECT2D inside vret + ' + SUB VECT2D.abs(vret AS VECT2D, vec1 AS VECT2D) + vret.x! = ABS(vec1.x!) + vret.y! = ABS(vec1.y!) + END SUB + + + '' + ' Set both VECT2D axes to 0 + ' + ' @param VECT2D vret Return vector with zeroed axes + ' @return VECT2D inside vret + ' + SUB VECT2D.zero(vret AS VECT2D) + vret.x! = 0 + vret.y! = 0 + END SUB + + + '' + ' Get distance between two VECT2Ds + ' + ' @param VECT2D vec1 Vector to measure distance from + ' @param VECT2D vec2 Vector to measure distance to + ' @return SINGLE distance between the vectors + ' + FUNCTION VECT2D.distance!(vec1 AS VECT2D, vec2 AS VECT2D) + DIM AS SINGLE x, y + x! = vec1.x! - vec2.x! + y! = vec1.y! - vec2.y! + VECT2D.distance! = SQR(x! * x! + y! * y!) + END FUNCTION + + + '' + ' Rotate a vector by radians + ' + ' @param VECT2D vret Return vector with rotated axes + ' @param VECT2D vec1 Vectore to rotate axes of + ' @param SINGLE radians Radians to rotate vector by + ' @return VECT2D inside vret + ' + SUB VECT2D.rotate(vret as VECT2D, vec1 AS VECT2D, radians as SINGLE) + DIM AS SINGLE cosine, sine + cosine! = COS(radians) + sine! = SIN(radians) + vret.x! = vec1.x! * cosine! - vec1.y! * sine! + vret.y! = vec1.x! * sine! + vec1.y! * cosine! + END SUB + + + '' + ' Round the axes of a vector + ' + ' @param VECT2D vret Return vector with rounded axes + ' @param VECT2D vec1 Vector to round axes for + ' @return VECT2D inside vret + ' + SUB VECT2D.round(vret AS VECT2D, vec1 AS VECT2D) + vret.x! = _ROUND(vec1.x!) + vret.y! = _ROUND(vec1.y!) + END SUB + + + '' + ' Return length squared of vector + ' + ' @param VECT2D vec1 Vector to operate on + ' @return SINGLE length squared of vector + ' + FUNCTION VECT2D.lengthsq!(vec1 AS VECT2D) + VECT2D.lengthsq! = vec1.x! * vec1.x! + vec1.y! * vec1.y! + END FUNCTION + + + '' + ' Return length(magnitude) of vector + ' + ' @param VECT2D vec1 Vector to get length(magnitude) for + ' @return SINGLE length of vector + ' + FUNCTION VECT2D.length!(vec1 AS VECT2D) + VECT2D.length! = VECT2D.magnitude(vec1.x!, vec1.y!) + END FUNCTION + + + '' + ' Get dot product of two vectors + ' + ' @param VECT2D vec1 Left VECT2D operand + ' @param VECT2D vec1 Right VECT2D operand + ' @return SINGLE dot product of two vectors + ' + FUNCTION VECT2D.dotproduct!(vec1 AS VECT2D, vec2 AS VECT2D) + VECT2D.dotproduct! = vec1.x! * vec2.x! + vec1.y! * vec2.y! + END FUNCTION + + + '' + ' Get cross product of two vectors + ' + ' @param VECT2D vec1 Left VECT2D operand + ' @param VECT2D vec1 Right VECT2D operand + ' @return SINGLE cross product of two vectors + ' + FUNCTION VECT2D.crossproduct!(vec1 AS VECT2D, vec2 AS VECT2D) + VECT2D.crossproduct! = vec1.x! * vec2.y! - vec1.y! * vec2.x! + END FUNCTION + + + '' + ' Get magnitude(length) of vector + ' + ' @param SINGLE x axis of vector + ' @param SINGLE y axis of vector + ' @return SINGLE magnitude(length) of vector + ' + FUNCTION VECT2D.magnitude!(x AS SINGLE, y AS SINGLE) + VECT2D.magnitude! = SQR(x * x + y * y) + END FUNCTION + + + '' + ' Check if two vectors have equal axes + ' + ' @param VECT2D vec1 Left VECT2D operand + ' @param VECT2D vec2 Right VECT2D operand + ' @return INTEGER (TRUE/FALSE) if vectors are equal + ' + FUNCTION VECT2D.eq%(vec1 AS VECT2D, vec2 AS VECT2D) + IF vec1.x! = vec2.x! AND vec1.y! = vec2.y! THEN + VECT2D.eq% = TRUE + ELSE + VECT2D.eq% = FALSE + END IF + END FUNCTION + + + '' + ' Converts radians to degress (wrapper to _R2D) + ' + ' @param SINGLE radians to convert to degrees + ' @return SINGLE degrees converted from radians + ' + FUNCTION VECT2D.radians_to_degrees!(radians AS SINGLE) + VECT2D.radians_to_degrees! = _R2D(radians) + ' Formula: radians_to_degrees! = radians! * 180 / _PI + END FUNCTION + + + '' + ' Converts degrees to radians (wrapper to _D2R) + ' + ' @param SINGLE radians to convert to degrees + ' @return SINGLE radians converted from degrees + ' + FUNCTION VECT2D.degrees_to_radians!(degrees AS SINGLE) + VECT2D.degrees_to_radians! = _D2R(degrees) + ' Formula: degrees_to_radians! = degrees! * _PI / 180 + END FUNCTION + $END IF