├── CODEOWNERS ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── LICENSE.md ├── README.md ├── assets ├── css │ └── pandoc.css └── images │ ├── headlines │ ├── features.png │ ├── goals.png │ ├── headlines.acorn │ ├── introduction.png │ ├── questions.png │ ├── thanks.png │ └── usage.png │ ├── screenshots │ ├── num-screenshot-01-basics.png │ ├── num-screenshot-02-systems.png │ └── num-screenshot-03-curls.png │ └── splash │ ├── splash-1920x1100.jpg │ ├── splash-750x430.jpg │ ├── splash-960x550.jpg │ └── splash-960x550@2x.jpg ├── bin ├── install.sh └── num ├── doc ├── articles.md ├── benchmarks.md ├── build.md ├── comparisons.md ├── donate.md ├── faq.md ├── files.md ├── functions.md ├── functions │ ├── absolute-value.md │ ├── all.md │ ├── coefficient-of-variance.md │ ├── difference.md │ ├── first.md │ ├── fourth-moment-about-the-mean.md │ ├── frequency-maximum.md │ ├── frequency-minimum.md │ ├── increment.md │ ├── index.md │ ├── interquartile-range.md │ ├── is-ascending.md │ ├── is-descending.md │ ├── is-non-ascending.md │ ├── is-non-descending.md │ ├── is-strictly-ascending.md │ ├── is-strictly-descending.md │ ├── is-unique.md │ ├── kurtosis.md │ ├── last.md │ ├── maximum.md │ ├── mean-absolute-deviation.md │ ├── mean.md │ ├── meanest.md │ ├── median-high.md │ ├── median-low.md │ ├── median.md │ ├── minimum.md │ ├── mode-max.md │ ├── mode-min.md │ ├── modes.md │ ├── n.md │ ├── normalize.md │ ├── product.md │ ├── quartile-0.md │ ├── quartile-1.md │ ├── quartile-2.md │ ├── quartile-3.md │ ├── quartile-4.md │ ├── range.md │ ├── round-down.md │ ├── round-off.md │ ├── round-up.md │ ├── round.md │ ├── second-moment-about-the-mean.md │ ├── sign.md │ ├── skewness.md │ ├── sort-ascending.md │ ├── sort-descending.md │ ├── standard-deviation.md │ ├── sum-of-cubes.md │ ├── sum-of-quads.md │ ├── sum-of-squares.md │ ├── sum.md │ ├── third-moment-about-the-mean.md │ ├── trimean.md │ ├── unique.md │ └── variance.md ├── helpers.md ├── input-output-separators.md ├── kickstarter-project.md ├── kickstarter-results.md ├── known-issues.md ├── porting.md ├── programmer-guide.md ├── programmer-library-of-awk-functions.md ├── test.md ├── thanks.md ├── todo.md ├── troubleshooting.md └── tutorial.md ├── screenshots ├── num-screenshot-01-basics.png ├── num-screenshot-02-systems.png └── num-screenshot-03-curls.png ├── src ├── num-absolute-value.awk ├── num-argv.awk ├── num-arr.awk ├── num-coefficient-of-variance.awk ├── num-conf.awk ├── num-frequency.awk ├── num-function-manager.awk ├── num-function.awk ├── num-help.awk ├── num-increment.awk ├── num-init.awk ├── num-insertion-sort.awk ├── num-kurtosis.awk ├── num-lint.awk ├── num-list.awk ├── num-main.awk ├── num-map-absolute-value.awk ├── num-map-increment.awk ├── num-map-normalize.awk ├── num-map-round.awk ├── num-map-sign.awk ├── num-map.awk ├── num-mean-absolute-deviation.awk ├── num-mean.awk ├── num-meanest.awk ├── num-median.awk ├── num-mode.awk ├── num-normalize.awk ├── num-out-err.awk ├── num-print.awk ├── num-product.awk ├── num-quartiles.awk ├── num-queue.awk ├── num-quicksort.awk ├── num-round.awk ├── num-scope.awk ├── num-shift.awk ├── num-sign.awk ├── num-skewness.awk ├── num-sort.awk ├── num-stack.awk ├── num-standard-deviation.awk ├── num-sum-of-mean-deviation.awk ├── num-sum.awk ├── num-trimean.awk ├── num-trimmed-mean.awk ├── num-unique.awk ├── num-variance.awk ├── num.awk └── num.sh ├── test ├── minitest.awk ├── minitest.sh ├── num-absolute-value-test.sh ├── num-coefficient-of-variance-test.sh ├── num-frequency-test.sh ├── num-help-test.sh ├── num-increment-test.sh ├── num-kurtosis-test.sh ├── num-list-test.sh ├── num-mean-absolute-deviation-test.sh ├── num-mean-test.sh ├── num-meanest-test.sh ├── num-median-test.sh ├── num-mode-test.sh ├── num-normalize-test.sh ├── num-ofmt-test.sh ├── num-product-test.sh ├── num-quartiles-test.sh ├── num-quicksort-test.awk ├── num-quicksort-test.sh ├── num-round-test.sh ├── num-separators-test.sh ├── num-sign-test.sh ├── num-skewness-test.sh ├── num-smoke-test.sh ├── num-sort-test.sh ├── num-standard-deviation-test.sh ├── num-sum-of-mean-deviation-test.sh ├── num-sum-test.sh ├── num-test ├── num-trimean-test.sh ├── num-trimmed-mean-test.sh ├── num-unique-test.sh └── num-variance-test.sh └── todo ├── num-test-todo.sh ├── num-todo.sh ├── num-trig.awk └── num_awk_lib_todo.awk /CODEOWNERS: -------------------------------------------------------------------------------- 1 | * codeowners@numcommand.com 2 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | 2 | # Contributor Covenant Code of Conduct 3 | 4 | ## Our Pledge 5 | 6 | We as members, contributors, and leaders pledge to make participation in our 7 | community a harassment-free experience for everyone, regardless of age, body 8 | size, visible or invisible disability, ethnicity, sex characteristics, gender 9 | identity and expression, level of experience, education, socio-economic status, 10 | nationality, personal appearance, race, caste, color, religion, or sexual 11 | identity and orientation. 12 | 13 | We pledge to act and interact in ways that contribute to an open, welcoming, 14 | diverse, inclusive, and healthy community. 15 | 16 | ## Our Standards 17 | 18 | Examples of behavior that contributes to a positive environment for our 19 | community include: 20 | 21 | * Demonstrating empathy and kindness toward other people 22 | * Being respectful of differing opinions, viewpoints, and experiences 23 | * Giving and gracefully accepting constructive feedback 24 | * Accepting responsibility and apologizing to those affected by our mistakes, 25 | and learning from the experience 26 | * Focusing on what is best not just for us as individuals, but for the overall 27 | community 28 | 29 | Examples of unacceptable behavior include: 30 | 31 | * The use of sexualized language or imagery, and sexual attention or advances of 32 | any kind 33 | * Trolling, insulting or derogatory comments, and personal or political attacks 34 | * Public or private harassment 35 | * Publishing others' private information, such as a physical or email address, 36 | without their explicit permission 37 | * Other conduct which could reasonably be considered inappropriate in a 38 | professional setting 39 | 40 | ## Enforcement Responsibilities 41 | 42 | Community leaders are responsible for clarifying and enforcing our standards of 43 | acceptable behavior and will take appropriate and fair corrective action in 44 | response to any behavior that they deem inappropriate, threatening, offensive, 45 | or harmful. 46 | 47 | Community leaders have the right and responsibility to remove, edit, or reject 48 | comments, commits, code, wiki edits, issues, and other contributions that are 49 | not aligned to this Code of Conduct, and will communicate reasons for moderation 50 | decisions when appropriate. 51 | 52 | ## Scope 53 | 54 | This Code of Conduct applies within all community spaces, and also applies when 55 | an individual is officially representing the community in public spaces. 56 | Examples of representing our community include using an official e-mail address, 57 | posting via an official social media account, or acting as an appointed 58 | representative at an online or offline event. 59 | 60 | ## Enforcement 61 | 62 | Instances of abusive, harassing, or otherwise unacceptable behavior may be 63 | reported to the community leaders responsible for enforcement at 64 | [INSERT CONTACT METHOD]. 65 | All complaints will be reviewed and investigated promptly and fairly. 66 | 67 | All community leaders are obligated to respect the privacy and security of the 68 | reporter of any incident. 69 | 70 | ## Enforcement Guidelines 71 | 72 | Community leaders will follow these Community Impact Guidelines in determining 73 | the consequences for any action they deem in violation of this Code of Conduct: 74 | 75 | ### 1. Correction 76 | 77 | **Community Impact**: Use of inappropriate language or other behavior deemed 78 | unprofessional or unwelcome in the community. 79 | 80 | **Consequence**: A private, written warning from community leaders, providing 81 | clarity around the nature of the violation and an explanation of why the 82 | behavior was inappropriate. A public apology may be requested. 83 | 84 | ### 2. Warning 85 | 86 | **Community Impact**: A violation through a single incident or series of 87 | actions. 88 | 89 | **Consequence**: A warning with consequences for continued behavior. No 90 | interaction with the people involved, including unsolicited interaction with 91 | those enforcing the Code of Conduct, for a specified period of time. This 92 | includes avoiding interactions in community spaces as well as external channels 93 | like social media. Violating these terms may lead to a temporary or permanent 94 | ban. 95 | 96 | ### 3. Temporary Ban 97 | 98 | **Community Impact**: A serious violation of community standards, including 99 | sustained inappropriate behavior. 100 | 101 | **Consequence**: A temporary ban from any sort of interaction or public 102 | communication with the community for a specified period of time. No public or 103 | private interaction with the people involved, including unsolicited interaction 104 | with those enforcing the Code of Conduct, is allowed during this period. 105 | Violating these terms may lead to a permanent ban. 106 | 107 | ### 4. Permanent Ban 108 | 109 | **Community Impact**: Demonstrating a pattern of violation of community 110 | standards, including sustained inappropriate behavior, harassment of an 111 | individual, or aggression toward or disparagement of classes of individuals. 112 | 113 | **Consequence**: A permanent ban from any sort of public interaction within the 114 | community. 115 | 116 | ## Attribution 117 | 118 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], 119 | version 2.1, available at 120 | [https://www.contributor-covenant.org/version/2/1/code_of_conduct.html][v2.1]. 121 | 122 | Community Impact Guidelines were inspired by 123 | [Mozilla's code of conduct enforcement ladder][Mozilla CoC]. 124 | 125 | For answers to common questions about this code of conduct, see the FAQ at 126 | [https://www.contributor-covenant.org/faq][FAQ]. Translations are available at 127 | [https://www.contributor-covenant.org/translations][translations]. 128 | 129 | [homepage]: https://www.contributor-covenant.org 130 | [v2.1]: https://www.contributor-covenant.org/version/2/1/code_of_conduct.html 131 | [Mozilla CoC]: https://github.com/mozilla/diversity 132 | [FAQ]: https://www.contributor-covenant.org/faq 133 | [translations]: https://www.contributor-covenant.org/translations 134 | 135 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing 2 | 3 | Thank you for using Num. We welcome contributions of all kinds. 4 | 5 | 6 | ## Contributing donations 7 | 8 | Thank you for helping, we appreciate your donations! 9 | 10 | * PayPal and credit cards: num@numcommand.com. 11 | 12 | * Patreon crowdfunding: [https://www.patreon.com/num](https://www.patreon.com/num) 13 | 14 | * Bitcoin BTC: 1366n1MzXZzcqqgxgDjab4TsQ3KjmRF4vK 15 | 16 | Easy button for PayPal credit card donations: 17 | 18 |
19 | 20 | 21 | 22 | 23 |
24 | 25 | Donations go to the lead developer Joel Parker Henderson. If you are hoping to donate more than $1000, please let us know ahead of time and we will apply to be an official United States 501(c)(3) nonprofit so you'll be able to get a tax deduction. 26 | 27 | 28 | ## Contributing feedback by using GitHub isses 29 | 30 | If you have ideas, or advice, or feedback, you can create a new GitHub issue. 31 | 32 | This is good because all the project programmers have access to the issue, and all the users will be able to see the issue, add comments, and follow progress. 33 | 34 | 35 | ## Contributing help by using a pull request 36 | 37 | We love pull requests for improvements to the project. 38 | 39 | Fork the repo, add your work, and send us a pull request. If you are able, add tests too. 40 | 41 | 42 | ## Contributing help via email 43 | 44 | If you want to reach us by email, please email num@numcommand.com. 45 | 46 | Email is good for any kind of one-on-one contact. 47 | 48 | 49 | ## Contributing security alerts 50 | 51 | Please email num@numcommand.com and include "SECURITY" in the subject line. 52 | 53 | If the security is an emergency, please also message the lead developer's mobile phone at 415-317-2700. 54 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | # License 2 | 3 | You may choose any of these open source licenses: 4 | 5 | * Apache License 6 | * BSD License 7 | * CreativeCommons License, Non-commercial Share Alike 8 | * GNU Affero General Public License (AGPL) 9 | * GNU General Public License Version (GPL) 10 | * GNU Lesser General Public License (LGPL) 11 | * MIT License 12 | * Perl Artistic License 13 | * Ruby License 14 | 15 | The software is provided "as is", without warranty of any kind, 16 | express or implied, including but not limited to the warranties of 17 | merchantability, fitness for a particular purpose and noninfringement. 18 | 19 | In no event shall the authors or copyright holders be liable for any 20 | claim, damages or other liability, whether in an action of contract, 21 | tort or otherwise, arising from, out of or in connection with the 22 | software or the use or other dealings in the software. 23 | 24 | This license is for the included software that is created by SixArm. 25 | Some of the included software may have its own licenses, copyrights, 26 | authors, etc. and these may take precedence over the SixArm license. 27 | 28 | Copyright (c) Joel Parker Henderson 29 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | # Num: number utilities for mathematics 4 | 5 | Num is a command line tool for mathematics and statistics data processing. 6 |
Num can calculate sum, median, quartiles, standard deviation, and much more. 7 |
Num can sort, map, filter, round, and use various input/output formats. 8 | 9 | Num on Patreon: donate to help 10 |
Num on Twitter: @numcommand 11 |
Num on the web: www.numcommand.com 12 | 13 | Example: 14 | 15 | $ num sum median variance stddev data.txt 16 | 10 2.5 1.66667 1.29099 17 | 18 | ## Dependencies 19 | 20 | 21 | 22 | ## Install 23 | 24 | $ git clone git@github.com:numcommand/num.git 25 | $ cd num 26 | $ bin/install.sh # for help => bin/install.sh --help 27 | 28 |
To troubleshoot please see the [Troubleshooting page](doc/troubleshooting.md). 29 | 30 | 31 | Start using Num: 32 | 33 | * Tutorial: quick start, input, output, variables, options, … 34 | * Functions: sum, min, max, var, iqr, sort, round, normalize, … 35 | * Helpers: awk, cut, sed, seq, wc, head, tail, parallel, … 36 | * Articles & Blogs: Unix, EDA, Datamash, qstats, MOOCs, … 37 | * FAQ: how to help, feature roadmap, … 38 | 39 | Project pages: 40 | 41 | * Donate: using PayPal, Patreon, Bitcoin, GitHub, etc. 42 | * Thanks: our coders, advisors, sponsors, donors, and helpers 43 | * Comparisons: when to use Num vs. other tools, such as C, R, Python, etc. 44 | * Benchmarks 45 | * To do 46 | * Known issues 47 | 48 | Programmer pages: 49 | 50 | * Programmer guide 51 | * Programmer library of awk functions 52 | * Files 53 | * Build 54 | * Test 55 | * Porting to more languages 56 | 57 | Statistics help: 58 | 59 | * Unlearning descriptive statistics 60 | 61 | By Joel Parker Henderson 62 | -------------------------------------------------------------------------------- /assets/images/headlines/features.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/numcommand/num/933415f824126b7bf2a3d3940c2cb119cd91e71d/assets/images/headlines/features.png -------------------------------------------------------------------------------- /assets/images/headlines/goals.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/numcommand/num/933415f824126b7bf2a3d3940c2cb119cd91e71d/assets/images/headlines/goals.png -------------------------------------------------------------------------------- /assets/images/headlines/headlines.acorn: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/numcommand/num/933415f824126b7bf2a3d3940c2cb119cd91e71d/assets/images/headlines/headlines.acorn -------------------------------------------------------------------------------- /assets/images/headlines/introduction.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/numcommand/num/933415f824126b7bf2a3d3940c2cb119cd91e71d/assets/images/headlines/introduction.png -------------------------------------------------------------------------------- /assets/images/headlines/questions.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/numcommand/num/933415f824126b7bf2a3d3940c2cb119cd91e71d/assets/images/headlines/questions.png -------------------------------------------------------------------------------- /assets/images/headlines/thanks.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/numcommand/num/933415f824126b7bf2a3d3940c2cb119cd91e71d/assets/images/headlines/thanks.png -------------------------------------------------------------------------------- /assets/images/headlines/usage.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/numcommand/num/933415f824126b7bf2a3d3940c2cb119cd91e71d/assets/images/headlines/usage.png -------------------------------------------------------------------------------- /assets/images/screenshots/num-screenshot-01-basics.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/numcommand/num/933415f824126b7bf2a3d3940c2cb119cd91e71d/assets/images/screenshots/num-screenshot-01-basics.png -------------------------------------------------------------------------------- /assets/images/screenshots/num-screenshot-02-systems.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/numcommand/num/933415f824126b7bf2a3d3940c2cb119cd91e71d/assets/images/screenshots/num-screenshot-02-systems.png -------------------------------------------------------------------------------- /assets/images/screenshots/num-screenshot-03-curls.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/numcommand/num/933415f824126b7bf2a3d3940c2cb119cd91e71d/assets/images/screenshots/num-screenshot-03-curls.png -------------------------------------------------------------------------------- /assets/images/splash/splash-1920x1100.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/numcommand/num/933415f824126b7bf2a3d3940c2cb119cd91e71d/assets/images/splash/splash-1920x1100.jpg -------------------------------------------------------------------------------- /assets/images/splash/splash-750x430.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/numcommand/num/933415f824126b7bf2a3d3940c2cb119cd91e71d/assets/images/splash/splash-750x430.jpg -------------------------------------------------------------------------------- /assets/images/splash/splash-960x550.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/numcommand/num/933415f824126b7bf2a3d3940c2cb119cd91e71d/assets/images/splash/splash-960x550.jpg -------------------------------------------------------------------------------- /assets/images/splash/splash-960x550@2x.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/numcommand/num/933415f824126b7bf2a3d3940c2cb119cd91e71d/assets/images/splash/splash-960x550@2x.jpg -------------------------------------------------------------------------------- /bin/install.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # halt on any error (non-0 exit code) 4 | set -e 5 | 6 | # halt when referencing any unbound variable 7 | set -u 8 | 9 | 10 | out() { printf %s\\n "$*" ; } 11 | err() { >&2 printf %s\\n "$*" ; } 12 | die() { >&2 printf %s\\n "$*" ; exit 1 ; } 13 | 14 | display_help () { 15 | cat < data 11 | 12 | Time the Num command: 13 | 14 | time ( num sum data ) 15 | ... 0.788 total 16 | 17 | Time the Num command with more statistics: 18 | 19 | time ( num sum mean variance standarddeviation data ) 20 | ... 0.964 total 21 | 22 | Your actual speed may vary. Num speed depends on many factors, such as which `awk` implemenation is in use, which computer you choose to use, which statitics you choose to calculate, which other software you're running simultaneously, etc. 23 | 24 | 25 | ## Comparisons 26 | 27 | Comparisons of similar solutions using `awk`, `gawk`, `mawk`, and `perl`. 28 | 29 | time ( awk '{ x += $1 }' data ) 30 | ... 1.050 total 31 | 32 | time ( gawk '{ x += $1 }' data ) 33 | ... 0.396 total 34 | 35 | time ( mawk '{ x += $1 }' data ) 36 | ... 0.195 total 37 | 38 | time ( perl -ne '$x += $_' data) 39 | ... 0.200 total 40 | 41 | Summary: 42 | 43 | * mawk is .2 44 | 45 | * gawk is .4 46 | 47 | * awk is 1.0 48 | 49 | Details: 50 | 51 | * This is using Apple OS X 10.10.5 on a typical MacBook Pro laptop. 52 | 53 | * The `awk` command is the OS X default one: OS X 10.10 `awk` version 20070501. 54 | 55 | * The `gawk` and `mawk` commands are installed by using `brew`. 56 | 57 | Caveats: 58 | 59 | * For statistics that use ranking, such as median and trimean, Num is approximately the same speed as the awk implementation. 60 | 61 | * For statistics that do not use ranking, such as sum or mean, then Num is approximately 2x-time as the awk implementation. This is primarliy because Num stores all the numbers in an array, whereas awk code doesn't store the numbers. We plan to optimize this for Num 2.0. 62 | 63 | 64 |


68 | -------------------------------------------------------------------------------- /doc/build.md: -------------------------------------------------------------------------------- 1 | # Build 2 | 3 | To build the `num` script: 4 | 5 | awk 'FNR==1 && NR!=1 {print "\n"}{print}' \ 6 | $(cat src/num.awk | sed -n 's/^\@include "\(.*\)"/\1/p') | more 7 | 8 | We have a goal to make the build process simpler. 9 | -------------------------------------------------------------------------------- /doc/comparisons.md: -------------------------------------------------------------------------------- 1 | # Comparisons: when to use Num vs. other tools 2 | 3 | Use Num when: 4 | 5 | * small data and small tasks 6 | * or one-time needs 7 | * or development-quality stability 8 | 9 | Use other tools when you have: 10 | 11 | * large data and large tasks 12 | * or repeating needs and automation 13 | * or production-quality stability 14 | 15 | 16 | ### When to use datamash, qstats, num-utils, etc. 17 | 18 | We suggest using these small compiled binaries when: 19 | 20 | * You're a sysop or sysadmin. 21 | * Your needs include systems automation. 22 | * You think of numbers in terms of streams and pipes. 23 | * You already use system tools, such as awk, grep, sed, make, etc. 24 | * Your data set fits in available RAM and is suitable for Unix pipes. 25 | * You want a quick way to pipe text to commands, with no dependencies. 26 | * Your ideal tools are small, compiled once, with no dependencies, and no add ons. 27 | 28 | See: 29 | 30 | * [GNU datamash: command which performs basic numeric, textual and statistical operations](https://www.gnu.org/software/datamash/) 31 | * [qstats: quick and dirty statistics tool for the Unix pipeline](https://github.com/tonyfischetti/qstats) 32 | * [num-utils: a set of programs for dealing with numbers from the Unix command line](http://suso.suso.org/programs/num-utils/index.phtml) 33 | 34 | 35 | ### When to use R, Julia, Octave, etc. 36 | 37 | We suggest using these full featured statistics environments when: 38 | 39 | * You're a statistician. 40 | * Your needs include doing data exploration. 41 | * You think of numbers in terms of vectors and functions. 42 | * You want to use an app, such as R Studio, GNU Octave, MATLAB, or Mathematica. 43 | * Your data set fits comfortably in your computer's memory. 44 | * You want a quick easy way to try visualizations and algorithms for yourself. 45 | * Your ideal tools are on the leading edge of new statistics. 46 | 47 | See: 48 | 49 | * [R Project for Statistical Computing](https://www.r-project.org/), [Comprehensive R Archive Network](https://cran.r-project.org/) 50 | * [Julia language](http://julialang.org/) 51 | * [GNU Octave: high-level language for numerical computations](https://www.gnu.org/software/octave/) 52 | * [MATLAB: high-level language and interactive environment](http://www.mathworks.com/products/matlab/) 53 | * [Wolfram Mathematica: Modern Technical Computing](https://www.wolfram.com/mathematica/) 54 | * [Mirador: tool for visual exploration of complex data](https://github.com/mirador/mirador) 55 | 56 | 57 | ### When to use Python, Scala, J, etc. 58 | 59 | We suggest using these programming languages when: 60 | 61 | * You're a coder. 62 | * Your needs include doing data pre-processing or post-processing. 63 | * You think of numbers in terms of objects and messages, such as OOP methods. 64 | * You already code in Python, Scala, Java, Perl, Ruby, Go, J, etc. 65 | * Your data set exceeds your computer's memory. 66 | * You want production environment deployments of visualizations and algorithms. 67 | * Your ideal tools use a general purpose programming language. 68 | 69 | See: 70 | 71 | * [Python programming langauage](https://www.python.org/), [Python Data Analysis Library](https://pandas.pydata.org/) 72 | * [Scala programming language](https://www.scala-lang.org/) 73 | * [J programming language](https://www.jsoftware.com/) 74 | 75 |


79 | -------------------------------------------------------------------------------- /doc/donate.md: -------------------------------------------------------------------------------- 1 | # Donate 2 | 3 | Donate to Num using any of these: 4 | 5 | * [PayPal num@numcommand.com](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=VP8C3ACSHKW5E) 6 | 7 | * [Patreon](https://www.patreon.com/num) 8 | 9 | * Bitcoin BTC: 1366n1MzXZzcqqgxgDjab4TsQ3KjmRF4vK 10 | 11 | ## Donate help and feedback 12 | 13 | To donate ideas and advice, the best way is our GitHub page and "New Issue" link: [https://www.github.com/numcommand/num](https://www.github.com/numcommand/num) 14 | 15 | To donate help programming, the best way is our GithUb page and a pull request: fork the repo, add your work, and do a pull request. 16 | 17 | We like using GitHub because all the project programmers have access to the issues and files, and all the project users will be able to see our progress. 18 | 19 | 20 | ## Donate via email 21 | 22 | If you prefer to donate other ways, you can always reach us by email at num@numcommand.com. 23 | 24 | If you would like to donate a large amount, or become a corporate sponsor, the best way is to email us and we'll make it easy for you. We aim to apply to become an official United States 501(c)(3) nonprofit. 25 | 26 | 27 |


31 | -------------------------------------------------------------------------------- /doc/faq.md: -------------------------------------------------------------------------------- 1 | # Frequently Asked Questions 2 | 3 | 4 | ## General Questions 5 | 6 | ### Q. How can I help? 7 | 8 | Yes please, and thank you! 9 | 10 | If you know programming or UI/UX, we're building Num using GitHub. We welcome help with coding, UI/UX, documentation, examples, community building, and the like. 11 | 12 | If you know mathematics and statistics, we will love your opinions about what functionality we should build, and how to get the software to you and your peers. 13 | 14 | ### Q. How do I pronounce Num? 15 | 16 | Num is pronounced like num-ber. 17 | 18 | ### Q. What is the logo? 19 | 20 | The logo is a circle and number sign. The circle is the symbol for a function in a computer software data flow diagram. The number sign is also known as a hash, or octothorpe. If you're a logo creator or graphics designer, we're open to a new logo. 21 | 22 | ### Q. Is this a one-time project or an ongoing project? 23 | 24 | Ongoing. We expect to work on Num for a long time, to improve it and make it available on more systems, and using more implemenations. 25 | 26 | ### Q. Why do a Kickstarter? 27 | 28 | Kickstarter is to get the project launched, and also help more people learn about it. We will use the money to take our existing open source hobby project and turn it into a better, stronger, faster, longer lived project. 29 | 30 | ### Q. Are there ways to donate? 31 | 32 | Yes, lots. You can donate to the lead author directly by using PayPal, Amazon, or Venmo, all to joel@joelparkerhenderson.com, or by using Bitcoin identifier 1366n1MzXZzcqqgxgDjab4TsQ3KjmRF4vK. 33 | 34 | ### Q. Is the Num project a non-profit? 35 | 36 | Not yet, that's a goal. If you're a lawyer or open source advocate who can give pro bono advice about making Num a non-profit, or contributing Num to an existing non-profit, then we'd love to hear from you. 37 | 38 | 39 | ## Technical Questions 40 | 41 | ### Q. Can Num add feature X? 42 | 43 | Generally yes, if the feature is in line with what Num does. We can add the feature to the roadmap for you. If you're able to write the code, or contribute to help fund the feature, that's excellent. Contact us to let us know what you'd like. 44 | 45 | ### Q. How fast is Num? 46 | 47 | Num can process a million numbers per second on a current MacBook Pro. You can read our [benchmarks](benchmarks.md). If you need more speed, try running qstats, datamash, R, python, etc. 48 | 49 | ### Q. Could any programmer write this? 50 | 51 | Yes. Many of the functions can be written in a handful of lines in another tool such as datamash or qstats, or another language such as C or R. 52 | 53 | In our day-to-day work, we've written these kinds of functions in these other tools and languages, and now we want to create a software utility that is simpler, cleaner, and better for standards tracking. 54 | 55 | We want an easy way to make these functions widely available, even for systems that are locked down or can't have additional programming tools installed. We found that awk provides a good solution for now because it's widely available and very good at handling fields and records. 56 | 57 | 58 |


62 | -------------------------------------------------------------------------------- /doc/files.md: -------------------------------------------------------------------------------- 1 | # Files 2 | 3 | Top level directory: 4 | 5 | * `bin/num` is the main script that users download. 6 | 7 | * The script has the command line parsing, help, etc. 8 | 9 | * The script inlines all the code from the `src` files. 10 | 11 | `src` directory: 12 | 13 | * `num.awk` is the main library file that includes all the other library files. 14 | 15 | * `num-*.awk` files are the library files, typically one file per concept. 16 | 17 | * You can use these library files in your own projects if you like. 18 | 19 | * The code of all these files is already included in the `num` script. 20 | 21 | `test` directory: 22 | 23 | * `minitest.sh` which provides simple assertions. 24 | 25 | * `num-test.sh` is a test runner for all the test files. 26 | 27 | * `num-*-test.sh` are the test files that correspond to source files. 28 | 29 | * When a test runs, the script prints details, then SUCCESS or FAILURE. 30 | 31 | `todo` directory: 32 | 33 | * Contains code snippets that we need to do. 34 | 35 | * Ideally this directory is empty. 36 | 37 | -------------------------------------------------------------------------------- /doc/functions/absolute-value.md: -------------------------------------------------------------------------------- 1 | # absolute-value, abs, magnitude 2 | 3 | Convert to absolute value a.k.a. magnitude, positive numbers. 4 | 5 | echo "-1 1" | num absolute-value all 6 | 1 1 7 | -------------------------------------------------------------------------------- /doc/functions/all.md: -------------------------------------------------------------------------------- 1 | # all 2 | 3 | All the items. 4 | 5 | echo "1 2 4" | num all 6 | 1 2 4 7 | -------------------------------------------------------------------------------- /doc/functions/coefficient-of-variance.md: -------------------------------------------------------------------------------- 1 | # coefficient-of-variance, covar, cv, relative-standard-deviation, rsd 2 | 3 | The coefficient of variance (CV), a.k.a. relative standard deviation (RSD). 4 | 5 | echo "1 2 4" | num coefficient-of-variance 6 | 0.654654 7 | 8 | 9 | This calculates for a sample, not a population. 10 | 11 | These are also available: 12 | 16 | -------------------------------------------------------------------------------- /doc/functions/difference.md: -------------------------------------------------------------------------------- 1 | # difference, diff, interval 2 | 3 | The difference between each number and the next number. TODO. 4 | 5 | echo "2 3 1 5" | num difference all 6 | 1 -2 4 7 | -------------------------------------------------------------------------------- /doc/functions/first.md: -------------------------------------------------------------------------------- 1 | # first 2 | 3 | The first item. 4 | 5 | echo "1 2 4" | num first 6 | 1 7 | -------------------------------------------------------------------------------- /doc/functions/fourth-moment-about-the-mean.md: -------------------------------------------------------------------------------- 1 | # fourth-moment-about-the-mean, 4matm 2 | 3 | The fourth moment about the mean. A.k.a. kurtosis. 4 | 5 | echo "1 2 4" | num fourth-moment-about-the-mean 6 | 5.44444 7 | 8 | This is for a sample, not a population. 9 | 10 | These are also available: 11 | 15 | -------------------------------------------------------------------------------- /doc/functions/frequency-maximum.md: -------------------------------------------------------------------------------- 1 | ### frequency maximum 2 | 3 | The frequency maximum. Count occurances of each value, and return the maximum count. 4 | 5 | echo "10 10 11 11 11" 6 | 3 7 | -------------------------------------------------------------------------------- /doc/functions/frequency-minimum.md: -------------------------------------------------------------------------------- 1 | # frequency minimum 2 | 3 | The frequency minimum. Count occurances of each value, and return the minimum count. 4 | 5 | echo "10 10 11 11 11" 6 | 2 7 | -------------------------------------------------------------------------------- /doc/functions/increment.md: -------------------------------------------------------------------------------- 1 | # increment 2 | 3 | Increment each value, i.e. add 1. 4 | 5 | echo "1 2 3" | num increment all 6 | 2 3 4 7 | -------------------------------------------------------------------------------- /doc/functions/index.md: -------------------------------------------------------------------------------- 1 | # Functions 2 | 3 | 59 | -------------------------------------------------------------------------------- /doc/functions/interquartile-range.md: -------------------------------------------------------------------------------- 1 | # interquartile-range, iqr, midspread, middle-fifty 2 | 3 | The interquartile range. This is calculated by using Q3 - Q1. 4 | 5 | echo "6 7 15 36 39 40 41 42 43 47 49" | interquartile-range 6 | 22.5 7 | 8 | Note that Num quartile calculations for Q1 and Q3 use the proportional smoothing algorithm, rather than a median value algorithm or removal of values algorithm. The proportional smoothing algorithm is more stable, and more useful especially for ongoing streams of statitiscs. 9 | -------------------------------------------------------------------------------- /doc/functions/is-ascending.md: -------------------------------------------------------------------------------- 1 | # is-ascending, is-asc 2 | 3 | Is the data ascending, i.e. each next number is greater or equal? 4 | 5 | echo "1 2 3" | num is-ascending 6 | 1 7 | 8 | echo "3 2 1" | num is-ascending 9 | 0 10 | -------------------------------------------------------------------------------- /doc/functions/is-descending.md: -------------------------------------------------------------------------------- 1 | # is-descending, is-desc 2 | 3 | Is the data descending, i.e. each next number is lesser or equal? 4 | 5 | echo "3 2 1" | num is-descending 6 | 1 7 | 8 | echo "1 2 3" | num is-descending 9 | 0 10 | -------------------------------------------------------------------------------- /doc/functions/is-non-ascending.md: -------------------------------------------------------------------------------- 1 | # is-non-ascending, is-non-asc 2 | 3 | Is the data non-ascending, i.e. some next number is lesser? 4 | 5 | echo "1 2 3" | num is-non-ascending 6 | 0 7 | 8 | echo "3 2 1" | num is-non-ascending 9 | 1 10 | -------------------------------------------------------------------------------- /doc/functions/is-non-descending.md: -------------------------------------------------------------------------------- 1 | # is-non-descending, is-non-desc 2 | 3 | Is the data non-descending, i.e. some next number is greater? 4 | 5 | echo "3 2 1" | num is-non-descending 6 | 0 7 | 8 | echo "1 2 3" | num is-non-descending 9 | 1 10 | -------------------------------------------------------------------------------- /doc/functions/is-strictly-ascending.md: -------------------------------------------------------------------------------- 1 | # is-strictly-ascending, is-strict-asc 2 | 3 | Is the data strictly ascending, i.e. each next number is greater? 4 | 5 | echo "1 2 3" | num is-strictly-ascending 6 | 1 7 | 8 | echo "3 2 1" | num is-strictly-ascending 9 | 0 10 | -------------------------------------------------------------------------------- /doc/functions/is-strictly-descending.md: -------------------------------------------------------------------------------- 1 | # is-strictly-descending, is-strict-desc 2 | 3 | Is the data strictly descending, i.e. each next number is lesser? 4 | 5 | echo "3 2 1" | num is-strictly-descending 6 | 1 7 | 8 | echo "1 2 3" | num is-strictly-descending 9 | 0 10 | -------------------------------------------------------------------------------- /doc/functions/is-unique.md: -------------------------------------------------------------------------------- 1 | #### is-unique 2 | 3 | Is the data unique? 4 | 5 | echo "1 2 3" | num is-unique 6 | 1 7 | 8 | echo "2 2 2" | num is-unique 9 | 0 10 | -------------------------------------------------------------------------------- /doc/functions/kurtosis.md: -------------------------------------------------------------------------------- 1 | # kurtosis, kurt 2 | 3 | The kurtosis, e.g. the heaviness of a distribution's tails. 4 | Default uses sample, not population. 5 | 6 | echo "1 2 4" | num kurtosis 7 | 5.44444 8 | 9 | This calculates for a sample, not a population. 10 | 11 | The kurtosis is sometimes described as a curve's "peakness or flatness", or "shoulder shape". 12 | 13 | These are also available: 14 | 18 | -------------------------------------------------------------------------------- /doc/functions/last.md: -------------------------------------------------------------------------------- 1 | # last 2 | 3 | The last item. 4 | 5 | echo "1 2 4" | num last 6 | 4 7 | -------------------------------------------------------------------------------- /doc/functions/maximum.md: -------------------------------------------------------------------------------- 1 | # maximum, max 2 | 3 | The maximum value. 4 | 5 | echo "1 2 4" | num maximum 6 | 1 7 | -------------------------------------------------------------------------------- /doc/functions/mean-absolute-deviation.md: -------------------------------------------------------------------------------- 1 | # mean-absolute-deviation, mad 2 | 3 | The average distance between each value and the mean. 4 | 5 | echo "1 2 4" | num mean-absolute-deviation 6 | 1.11111 7 | -------------------------------------------------------------------------------- /doc/functions/mean.md: -------------------------------------------------------------------------------- 1 | # mean, average, avg 2 | 3 | The arithmetic mean a.ka. average. 4 | 5 | echo "1 2 4" | num mean 6 | 2.33333 7 | -------------------------------------------------------------------------------- /doc/functions/meanest.md: -------------------------------------------------------------------------------- 1 | # meanest 2 | 3 | The value that is closest numerically to the mean. 4 | 5 | echo "1 2 4" | num meanest 6 | 2 7 | -------------------------------------------------------------------------------- /doc/functions/median-high.md: -------------------------------------------------------------------------------- 1 | # median-high, med-high 2 | 3 | The high median, i.e. greater of two middle values. 4 | 5 | echo "1 2 3 4" | num median 6 | 3 7 | -------------------------------------------------------------------------------- /doc/functions/median-low.md: -------------------------------------------------------------------------------- 1 | # median-low, med-low 2 | 3 | The low median, i.e. lesser of two middle values. 4 | 5 | echo "1 2 3 4" | num median 6 | 2 7 | -------------------------------------------------------------------------------- /doc/functions/median.md: -------------------------------------------------------------------------------- 1 | # median, med 2 | 3 | The median. 4 | 5 | echo "1 2 3 4" | num median 6 | 2.5 7 | -------------------------------------------------------------------------------- /doc/functions/minimum.md: -------------------------------------------------------------------------------- 1 | # minimum, min 2 | 3 | The minimum value. 4 | 5 | echo "1 2 4" | num minimum 6 | 1 7 | -------------------------------------------------------------------------------- /doc/functions/mode-max.md: -------------------------------------------------------------------------------- 1 | # mode-max 2 | 3 | The maximum value of the mode values or UNDEF. TODO. 4 | 5 | echo "1 1 2 2 3 3" | num mode-high 6 | 3 7 | -------------------------------------------------------------------------------- /doc/functions/mode-min.md: -------------------------------------------------------------------------------- 1 | # mode-min 2 | 3 | The minimum value of the mode values or UNDEF. TODO. 4 | 5 | echo "1 1 2 2 3 3" | num mode-low 6 | 1 7 | -------------------------------------------------------------------------------- /doc/functions/modes.md: -------------------------------------------------------------------------------- 1 | # modes 2 | 3 | The modes or UNDEF. TODO. 4 | 5 | Unimodal: 6 | 7 | echo "1 2 2 3" | num modes 8 | 2 9 | 10 | Bimodal: 11 | 12 | echo "1 2 2 3 3" | num modes 13 | 2 3 14 | 15 | Nonmodal: 16 | 17 | echo "1 2 3" | num modes 18 | UNDEF 19 | -------------------------------------------------------------------------------- /doc/functions/n.md: -------------------------------------------------------------------------------- 1 | # n, count, length, size 2 | 3 | The number of items. 4 | 5 | echo "1 2 4" | num n 6 | 3 7 | -------------------------------------------------------------------------------- /doc/functions/normalize.md: -------------------------------------------------------------------------------- 1 | # normalize 2 | 3 | Normalize each value to be within 0..1. 4 | 5 | echo "1 2 4" | num normalize all 6 | 0 0.333333 1 7 | -------------------------------------------------------------------------------- /doc/functions/product.md: -------------------------------------------------------------------------------- 1 | # product 2 | 3 | The product all the items. 4 | 5 | echo "1 2 4" | num product 6 | 8 7 | -------------------------------------------------------------------------------- /doc/functions/quartile-0.md: -------------------------------------------------------------------------------- 1 | # quartile-0, q0 2 | 3 | Quartile 0, a.k.a. minimum. 4 | 5 | echo "6 7 15 36 39 40 41 42 43 47 49" | quartile-0 6 | 6 7 | -------------------------------------------------------------------------------- /doc/functions/quartile-1.md: -------------------------------------------------------------------------------- 1 | # quartile-1, q1 2 | 3 | Quartile 1, a.k.a. lower quartile, 25th percentile. 4 | 5 | echo "6 7 15 36 39 40 41 42 43 47 49" | quartile-1 6 | 20.25 7 | 8 | Note that Num quartile calculations for Q1 and Q3 use the proportional smoothing algorithm, rather than a median value algorithm or removal of values algorithm. The proportional smoothing algorithm is more stable, and more useful especially for ongoing streams of statitiscs. 9 | -------------------------------------------------------------------------------- /doc/functions/quartile-2.md: -------------------------------------------------------------------------------- 1 | # quartile-2, q2 2 | 3 | Quartile 2, a.k.a. median, 50th percentile. 4 | 5 | echo "6 7 15 36 39 40 41 42 43 47 49" | quartile-2 6 | 40 7 | -------------------------------------------------------------------------------- /doc/functions/quartile-3.md: -------------------------------------------------------------------------------- 1 | # quartile-3, q3 2 | 3 | Quartile 3, a.k.a. upper quartile, 75th percentile. 4 | 5 | echo "6 7 15 36 39 40 41 42 43 47 49" | quartile-3 6 | 42.75 7 | 8 | Note that Num quartile calculations for Q1 and Q3 use the proportional smoothing algorithm, rather than a median value algorithm or removal of values algorithm. The proportional smoothing algorithm is more stable, and more useful especially for ongoing streams of statitiscs. 9 | -------------------------------------------------------------------------------- /doc/functions/quartile-4.md: -------------------------------------------------------------------------------- 1 | # quartile-4, q4 2 | 3 | Quartile 4, a.k.a. maximum. 4 | 5 | echo "6 7 15 36 39 40 41 42 43 47 49" | quartile-4 6 | 49 7 | -------------------------------------------------------------------------------- /doc/functions/range.md: -------------------------------------------------------------------------------- 1 | # range, interval, breadth, spread 2 | 3 | The range, which is maximum - minimum. 4 | 5 | echo "1 2 4" | num range 6 | 3 7 | -------------------------------------------------------------------------------- /doc/functions/round-down.md: -------------------------------------------------------------------------------- 1 | # round-down, round-towards-negative-infinity, floor 2 | 3 | Convert to integer by rounding down.
4 | 5 | echo "-1.9 1.9" | num round-down all 6 | -2 1 7 | -------------------------------------------------------------------------------- /doc/functions/round-off.md: -------------------------------------------------------------------------------- 1 | # round-off, round-towards-zero, truncate 2 | 3 | Convert to integer by omitting the fractional part.
4 | 5 | echo "-1.9 1.9" | num round-off all 6 | -1 1 7 | -------------------------------------------------------------------------------- /doc/functions/round-up.md: -------------------------------------------------------------------------------- 1 | # round-up, round-towards-positive-infinity, ceiling 2 | 3 | Convert to integer by rounding up.
4 | 5 | echo "-1.9 1.9" | num round-up all 6 | -1 2 7 | -------------------------------------------------------------------------------- /doc/functions/round.md: -------------------------------------------------------------------------------- 1 | # round, round-toward-nearest, nint 2 | 3 | Convert to nearest integer.
4 | 5 | echo "-1.9 1.9" | num round all 6 | 2 2 7 | -------------------------------------------------------------------------------- /doc/functions/second-moment-about-the-mean.md: -------------------------------------------------------------------------------- 1 | # second-moment-about-the-mean, 2matm 2 | 3 | The second moment about the mean. A.k.a. variance. 4 | 5 | echo "1 2 4" | num second-moment-about-the-mean 6 | 2.33333 7 | 8 | This is for a sample, not a population. 9 | 10 | These are also available: 11 | 15 | -------------------------------------------------------------------------------- /doc/functions/sign.md: -------------------------------------------------------------------------------- 1 | # sign 2 | 3 | Convert to sign, which is one of -1, 0, 1. 4 | 5 | echo "-8 0 8" | num sign all 6 | -1 0 1 7 | -------------------------------------------------------------------------------- /doc/functions/skewness.md: -------------------------------------------------------------------------------- 1 | # skewness, skew 2 | 3 | The skewness, e.g. the lack of symmetry. 4 | 5 | echo "1 2 4" | num skewness 6 | 1.11111 7 | 8 | This calculates for a sample, not a population. 9 | 10 | These are also available: 11 | 15 | -------------------------------------------------------------------------------- /doc/functions/sort-ascending.md: -------------------------------------------------------------------------------- 1 | # sort-ascending, sort-asc, sort-up 2 | 3 | Sort items in ascending order. 4 | 5 | echo "3 1 2" | num sort-ascending all 6 | 1 2 3 7 | -------------------------------------------------------------------------------- /doc/functions/sort-descending.md: -------------------------------------------------------------------------------- 1 | # sort-descending, sort-desc, sort-up 2 | 3 | Sort items in descending order. 4 | 5 | echo "3 1 2" | num sort-descending all 6 | 3 2 1 7 | -------------------------------------------------------------------------------- /doc/functions/standard-deviation.md: -------------------------------------------------------------------------------- 1 | # standard-deviation, stdev, sd 2 | 3 | The standard deviation (SD). 4 | 5 | echo "1 2 4" | num standard-deviation 6 | 1.52753 7 | 8 | This calculates for a sample, not a population. 9 | 10 | These are also available: 11 | 15 | -------------------------------------------------------------------------------- /doc/functions/sum-of-cubes.md: -------------------------------------------------------------------------------- 1 | # sum-of-cubes 2 | 3 | The sum of cubes, a.k.a. sum of each mean deviation to the power of 3. 4 | 5 | echo "1 2 4" | num sum-of-cubes 6 | 2.22222 7 | -------------------------------------------------------------------------------- /doc/functions/sum-of-quads.md: -------------------------------------------------------------------------------- 1 | # sum-of-quads 2 | 3 | The sum of quads, a.k.a. sum of each mean deviation to the power of 4. 4 | 5 | echo "1 2 4" | num sum-of-quads 6 | 10.8889 7 | -------------------------------------------------------------------------------- /doc/functions/sum-of-squares.md: -------------------------------------------------------------------------------- 1 | # sum-of-squares, ss 2 | 3 | The sum of squares, a.k.a. sum of each mean deviation to the power of 2. 4 | 5 | echo "1 2 4" | num sum-of-squares 6 | 4.66667 7 | -------------------------------------------------------------------------------- /doc/functions/sum.md: -------------------------------------------------------------------------------- 1 | # sum, total 2 | 3 | The sum total of all the items. 4 | 5 | echo "1 2 4" | num sum 6 | 7 7 | -------------------------------------------------------------------------------- /doc/functions/third-moment-about-the-mean.md: -------------------------------------------------------------------------------- 1 | # third-moment-about-the-mean, 3matm 2 | 3 | The third moment about the mean. A.k.a. skewness. 4 | 5 | echo "1 2 4" | num third-moment-about-the-mean 6 | 1.11111 7 | 8 | This is for a sample, not a population. 9 | 10 | These are also available: 11 | 15 | -------------------------------------------------------------------------------- /doc/functions/trimean.md: -------------------------------------------------------------------------------- 1 | #### trimean. 2 | 3 | The trimean. 4 | 5 | echo "1 2 4" | num trimean 6 | 8.875 7 | 8 | The advantage of using the trimean vs. the mean: the trimean is robust to outliers. 9 | The trimean equals (quartile-1 + 2 * median + quartile-3) / 4. 10 | -------------------------------------------------------------------------------- /doc/functions/unique.md: -------------------------------------------------------------------------------- 1 | # unique 2 | 3 | Keep unique values; omit the rest. TODO. 4 | 5 | echo "1 2 2 3 3 3" | num unique all 6 | 1 2 3 7 | -------------------------------------------------------------------------------- /doc/functions/variance.md: -------------------------------------------------------------------------------- 1 | # variance, var 2 | 3 | The variance. 4 | 5 | echo "1 2 4" | num variance 6 | 2.33333 7 | 8 | This calculates for a sample, not a population. 9 | 10 | These are also available: 11 | 15 | -------------------------------------------------------------------------------- /doc/input-output-separators.md: -------------------------------------------------------------------------------- 1 | # Input/Output Separators 2 | 3 | Num enables you to use custom separators for data. 4 | 5 | 6 | ## FS & RS 7 | 8 | You can use a custom input field separator (FS) and input record separator (RS): 9 | 10 | $ echo "1,2,3;4,5,6;7,8,9" | num min max FS=',' RS=';' 11 | 1 3 12 | 4 5 13 | 7 9 14 | 15 | 16 | ## OFS & ORS 17 | 18 | You can use a custom output field separator (OFS) and output record separator (ORS). 19 | 20 | $ echo "1 2 3\n4 5 6\n7 8 9" | num min max records OFS=',' ORS=';' 21 | 1,3;4,6;7,9 22 | 23 | 24 | ## CSV & TSV & USV 25 | 26 | Num has nicknames for separators for several data file exchange formats: 27 | 28 | * comma-separated values (CSV) is a nickname for FS="," RS="\n" OFS="," ORS="\n" 29 | * tab-separated values (TSV) is a nickname forFS="\t" RS="\n" OFS="\t" ORS="\n" 30 | * unit-separated values (USV) is a nickname for FS="␟" RS="␞" OFS="␟" ORS="␞" 31 | 32 | Examples: 33 | 34 | $ echo "1,2,3\n4,5,6\n7,8,9" | num min max csv 35 | 1,3 36 | 4,6 37 | 7,9 38 | 39 | $ echo "1\t2\t3\n4\t5\t6\n7\t8\t9" | num min max tsv 40 | 1\t3 41 | 4\t6 42 | 7\t9 43 | 44 | $ echo "1␟2␟3␞4␟5␟6␞7␟8␟9" | num min max usv 45 | 1␟3␞4␟6␞7␟9 46 | 47 | You can use CSV/TSV/USV on just the input or output: 48 | 49 | $ echo "1,2,3\n4,5,6\n7,8,9" | num min max input-csv output-tsv 50 | 1\t3 51 | 4\t6 52 | 7\t9 53 | 54 | Note: the nicknames set the separators, and do not do anything more sophisticated such as parsing escaped characters. For example, the CSV nickname sets the separators, and does not do any parsing that allows items to contain escaped commas, embedded newlines, and the like. If you need CSV parsing, consider using a prebuilt tools such as a perl module, ruby gem, or command line tool such as [`csvkit`](http://csvkit.readthedocs.org/), [`csvtool`](http://basepath.com/csv/), [`crush`](https://github.com/google/crush-tools), etc. 55 | -------------------------------------------------------------------------------- /doc/kickstarter-results.md: -------------------------------------------------------------------------------- 1 | # Can open source succeed on Kickstarter? (Top ten things I learned) 2 | 3 | Summary: Yes, thanks to coworkers, friends, and family. 4 | 5 | Thirty days ago I launched an open source software project called Num and website [http://www.numcommand.com](http://www.numcommand.com). 6 | 7 | I tried an experiment using Kickstarter to fundraise, and today the Kickstarter campaign is successful. 8 | 9 | * To discuss on [Hacker News click here](https://news.ycombinator.com/item?id=10600333) or on [Reddit click here](https://www.reddit.com/r/kickstarter/comments/3tjsh7/can_open_source_succeed_on_kickstarter_top_ten/) 10 | 11 | Num is a command line tool for mathematics and statistics, so I pitched the project on [Hacker News](https://news.ycombinator.com/item?id=10581299), relevant [Reddit](https://www.reddit.com/r/statistics/comments/3poigh/new_simple_statistics_tool_num_command_for_unix/) subgroups, relevant blogs/newsgroups/lists, and broadly on my [Facebook](https://www.facebook.com/joelparkerhenderson), [Twitter](https://www.twitter.com/joel_henderson), [LinkedIn](https://www.linkedin.com/in/joelparkerhenderson), etc. Also, I personally emailed dozens of experts in similar areas to ask for specific help. There's a page of [thanks](thanks.md). 12 | 13 | Top Ten Things I Learned: 14 | 15 | 1. Allies are everything. 80% of the funds are thanks to my existing group of coworkers, friends, and family. 20% of the funds are thanks to the kindness of strangers. Special thanks to teams at [Blue Otter](http://www.blueotter.net) and [Adspace Networks](http://www.adspacenetworks.com). 16 | 17 | 2. Documentation is more fundable than source code. In the beginning of the campaign, people had many questions and comments; to help with this I wrote lots of docs, and spent much more time on the docs than the source code. Based on what I saw on Kickstarter, technical books seem to successfully reach funding goals, but open source software projects do not. 18 | 19 | 3. Approximately 20% of experts replied. Replies ranged from negative/dismissive ("don't do this", "it isn't worthy of funding") to positive/constructive ("great idea", "here's an optimization to speed up the inner loop"). Two people provided the bulk of the expert help for which I'm very grateful. Thank you Janis and John! 20 | 21 | 4. The experts who maintain related projects did not reply, except for one, and notably his software is among the fastest I've ever seen. Props to Thomas and the mawk software. If you want an example of excellent pragmatic code, try mawk. 22 | 23 | 5. Outreach to the general public on social networks had minimal impact. Specifically, my technical postings on Facebook, Twitter, LinkedIn, etc. didn't result in many likes, retweets, shares, or dollars. Next time I'll try more-promotable postings with photos and videos. 24 | 25 | 6. Reddit subgroups are worthwhile. The discussion is thoughtful and relevant, the encouragement is helpful and high quality, and the followup is excellent. There are many people coding and sharing good advice and good projects. 26 | 27 | 7. Google indexing is fast. Within one week the Kickstarter project reached page one of Google results for the most important phrase ("num command"). Within three weeks the project website (www.numcommand.com) reached page one. This is all organic. 28 | 29 | 8. Kudos to the teams at [GitHub](https://www.github.com) and [FastMail](https://www.fastmail.com). It's a pleasure to interact with people who are smart, helpful, and especially nice. Much appreciated. These teams helped me launch Num quickly using a custom domain, repo organization, and email address. 30 | 31 | 9. When a Kickstarter project reaches the end of its funding period, the Kickstarter site locks the project page. This means there's no way to do any further funding nor expansion of features. Seems to me this is a lost opportunity for Kickstarter to provide ongoing services to project creators and backers. 32 | 33 | 10. Because Kickstarter doesn't provide ongoing services, I'm setting up ongoing crowdfunding possibilities. I'm evaluating Patreon, Gratipay, Flattr, CentUp, Amazon Dev Pay, PayPal donation buttons, and Bitcoin. All of these look promising in various ways. I welcome comments and advice. The Patreon link is [https://www.patreon.com/num](https://www.patreon.com/num) and the first goal is raising $1000 for functional upgrades. 34 | -------------------------------------------------------------------------------- /doc/known-issues.md: -------------------------------------------------------------------------------- 1 | # Known issues 2 | 3 | 4 | Known issues: 5 | 6 | * The script uses the phrase "coefficient of variance" whereas Wikipedia uses the phrase "coefficient of variation"; figure out which is better. 7 | 8 | * Change the function manager init to be a consistent name. 9 | 10 | * Mode and its friends aren't implemented yet; they return TODO. 11 | 12 | * Tested only on a Mac with Gawk and GNU tools; more testing is TODO. 13 | 14 | 15 |


19 | -------------------------------------------------------------------------------- /doc/porting.md: -------------------------------------------------------------------------------- 1 | # Porting num to more languages 2 | 3 | We are starting the project with an implementation in `awk` because we believe this gives the widest reach for the most systems. 4 | 5 | If you're a programmer and you would like to inplement Num functionality in another language, then we welcome your help. 6 | 7 | We have created placeholder git repos that you're welcome to use. 8 | 9 | -------------------------------------------------------------------------------- /doc/programmer-guide.md: -------------------------------------------------------------------------------- 1 | # Programmer guide 2 | 3 | This file is for programmers who are reading the Num source code or writing Num functions. 4 | 5 | 6 | ### Introduction 7 | 8 | A typical `awk` function looks like this: 9 | 10 | function sum(num, i, x) { 11 | for (i in num) x += num[i] 12 | return x 13 | } 14 | 15 | A `num` function is similar, plus has parameters for numbers metadata, and arbitrary options, and a function name: 16 | 17 | function sum(num, num_ options, f, i, x) { # The num_ underscore array is metadata 18 | f = "sum" # The function name is the metadata key 19 | if (!(f in num_)) { # Check the metadata cache 20 | for (i in num) x += num[i] # Calculate as usual 21 | num_[f] = x # Set the metadata cache 22 | } 23 | return num_[f] 24 | } 25 | 26 | Each `num` function also has a corresponding metadata function, that defines help, also-known-as synonyms, and possibly more in the future. 27 | 28 | function sum_( f) 29 | f = "sum" 30 | function_[f,"help"] = "Sum all the values." 31 | function_[f,"aka"] = "total" 32 | } 33 | 34 | A metadata varaible always ends with an underscore. This helps us keep track. 35 | 36 | 37 | ### Metadata Optimization 38 | 39 | A `num` function may be able to use metadata optimization. 40 | 41 | For example, if the `min` function knows that the numbers are sorted ascending or sorted desending, then the `min` function can immediately return the first or last item, rather than doing a full array scan. 42 | 43 | Example: 44 | 45 | function min(num, num_, options, f, x, i) { 46 | f = "min" 47 | if (!(f in num_)) { 48 | if (num_["ascending"]) 49 | num_[f] = first(num) 50 | else if (num_["descending"]) 51 | num_[f] = last(num) 52 | else { 53 | for (i in num) if (x == "" || num[i] < x) x = num[i] 54 | num_[f] = x 55 | } 56 | } 57 | return num_[f] 58 | } 59 | 60 | 61 | Some of the metadata keys that we're aiming to use: 62 | 63 | * `nums_["linear"]` boolean 0|1 if nums is linear numbers. 64 | * `nums_["unique"]` boolean 0|1 if nums is all unique numbers. 65 | * `nums_["integer"]` boolean 0|1 if nums is all integer numbers. 66 | * `nums_["ascending"]` boolean 0|1 if nums is sorted ascending. 67 | * `nums_["descending"]` boolean 0|1 if nums is sorted descending. 68 | 69 | 70 | ### Function conventions 71 | 72 | The project uses these function conventions: 73 | 74 | * Use the `awk` convention of two spaces to separate the function 75 | declaration list of input variables from internal variables. 76 | 77 | * Check if the array has a key that is the same name as the function. 78 | If the key exists, then skip any calculation; simply return the value. 79 | the value is a previously-calculated result a.k.a. a memo, a.k.a. a cache. 80 | 81 | * Explicitly set all internal variables because this helps 82 | catch bugs and prevent them, and tends to be easier to read. 83 | 84 | * After the loop, we cache the result in the array by using the 85 | opts array and a key that is the same name as the function. 86 | 87 | * We return the cache value, rather than the temp loop variable, 88 | because in our experience this helps prevent bugs. 89 | 90 | ### Coding conventions 91 | 92 | The project uses these coding conventions: 93 | 94 | * Prefer term `num` for a numbers array vs. `arr` for a generic array. 95 | 96 | * Prefer operatior whitespace vs. none. Example: use `a = b` not `a=b`. 97 | This is atypical for awk, but typical for Go, Python, Ruby, etc. 98 | 99 | * Prefer POSIX vs. gawk for coding. Example: use `x ^ 2` not `x ** 2`. 100 | For significant divergences between POSIX and gawk, ideally this code 101 | has a POSIX function and gawk function, and chooses the right one. 102 | 103 | * Prefer clarity vs. small optimizations. Example: intermediate vars. 104 | Big optimizations are always welcome and can be added here quickly. 105 | If a user has a need for speed or size, it is wiser to use R, Go, etc. 106 | 107 | * Local vars may be prefixed with "\_". Example: "\_foo" is local. 108 | This is most useful when a var has the same name as a function. 109 | 110 | * Metadata vars may be suffixed with "\_". Example: "foo\_" is metadata. 111 | 112 | Rule out for now: 113 | 114 | * Launch using `#/usr/bin/env awk` because we need the `-f` arg. 115 | * Launch using `#/usr/bin/env -S ..` because we want POSIX. 116 | 117 | 118 | ### Testing 119 | 120 | To run Num for testing and debugging, you can try using gawk with flags: 121 | 122 | AWK="gawk --posix --lint" num ... 123 | 124 | 125 | ### Building 126 | 127 | Num is provided two ways: 128 | 129 | * as a single script file, which runs using `mawk` or `nawk` 130 | * this script is `./implementations/num-awk/num` 131 | 132 | * as multiple include files, which only runs using `gawk` developers 133 | * this scripts starts with `./implementations/num-awk/src/num.sh` 134 | 135 | There is a one-step build process that makes the multiple files into one file: 136 | 137 | cd implementations/num-awk 138 | awk '/^## /{next}; /^#@include/{next}; /^@include ".*"/{ gsub(/"/,""); path = "src/" $2; print ""; while ((getline line < path) > 0) { print line } close(path); next}{print}' src/num.sh > num 139 | 140 | The build command does this: 141 | 142 | * Deletes any unnneeded comment lines 143 | * Any line that says `@include "foo.awk"` replaces the line with the file `src/foo.awk`. 144 | 145 |


149 | -------------------------------------------------------------------------------- /doc/programmer-library-of-awk-functions.md: -------------------------------------------------------------------------------- 1 | # Programmer library of awk functions 2 | 3 | The relevant files are in the directory `implemenations/num-awk`. 4 | 5 | The `num` script is the main script that users download. 6 | 7 | * The script has the command line parsing, help, etc. 8 | 9 | * The script includes all the code from the `num-*.awk` library files. 10 | 11 | The `num-*.awk` files are library files. 12 | 13 | * The code of all these files is already included in the `num` script. 14 | 15 | * You can use these library files in your own projects if you like. 16 | 17 | The `num-test` script is a shell script that runs tests. 18 | 19 | * When a test runs, the script prints details, then SUCCESS or FAILURE. 20 | 21 | * The test file is a work in progress; pull requests are welcome. 22 | 23 | 24 |


28 | -------------------------------------------------------------------------------- /doc/test.md: -------------------------------------------------------------------------------- 1 | # Test 2 | 3 | To run all test files: 4 | 5 | cd test 6 | ./num-test.sh 7 | 8 | To run one test file, such as for variance: 9 | 10 | cd test 11 | ./num-variance-test.sh 12 | -------------------------------------------------------------------------------- /doc/thanks.md: -------------------------------------------------------------------------------- 1 | # Thanks: our coders, advisors, sponsors, donors, and helpers 2 | 3 | ### Coders 4 | 5 | * [Joel Parker Henderson](http://github.com/joelparkerhenderson) 6 | * [John Allwine](https://github.com/jallwine) 7 | 8 | ### Advisors 9 | 10 | * Andy Bas at [AndyBas.com](http://andybas.com) 11 | * Bill Lazar at [Sumo Logic](http://sumologic.com) 12 | * Lawrence Yu at [Blue Otter](http://blueotter.net) 13 | * Michael Pope at [Living Social](http://livingsocial.com) 14 | * Even Howard at [House in the Moon](http://houseinthemoon.com) 15 | * Nate Amarose [Blue Otter](http://blueotter.net) 16 | 17 | ### Major Company Sponsors 18 | 19 | * [Adspace Networks](http://adspacenetworks.com) 20 | * [Blue Otter](http://blueotter.net) 21 | * [SixArm Software](http://sixarm.com) 22 | 23 | ### Donors 24 | 25 | * Kickstarter donors: 26 | [Alex Brem](https://www.kickstarter.com/profile/freqvibez), 27 | [Alexandru Nedel](https://www.kickstarter.com/profile/916349882), 28 | [Andrew McDonald](https://www.kickstarter.com/profile/682137419), 29 | [Andy Bas](https://www.kickstarter.com/profile/477841983), 30 | [Aritomo Shinozaki](https://www.kickstarter.com/profile/aritomo), 31 | [Barb Henderson](https://www.kickstarter.com/profile/1478635103), 32 | [Becca](https://www.kickstarter.com/profile/460981148), 33 | [Bill Lazar](https://www.kickstarter.com/profile/1640716206), 34 | [Caner](https://www.kickstarter.com/profile/caner), 35 | [Dain White](https://www.kickstarter.com/profile/dainwhite), 36 | [David Krogsrud](https://www.kickstarter.com/profile/716706080), 37 | [Doan Truong](https://www.kickstarter.com/profile/1108887657), 38 | [Duncan Beech](https://www.kickstarter.com/profile/1569217582), 39 | [Eric Cahoon](https://www.kickstarter.com/profile/ecahoon), 40 | [Eric Lamothe](https://www.kickstarter.com/profile/ericl), 41 | [Erica Austin](https://www.kickstarter.com/profile/320141210), 42 | [Espen Torseth](https://www.kickstarter.com/profile/510104035), 43 | [Even Howard](https://www.kickstarter.com/profile/1154983135), 44 | [Ido Rosen](https://www.kickstarter.com/profile/ido), 45 | [Ivica Kičić](https://www.kickstarter.com/profile/1305140661), 46 | [Jacqualynn Jones](https://www.kickstarter.com/profile/1041603700), 47 | [Jeremy Bagai](https://www.kickstarter.com/profile/2139473242), 48 | [Joe Ratti](https://www.kickstarter.com/profile/1494740047), 49 | [John Allwine](https://www.kickstarter.com/profile/941797232), 50 | [Jonathan](https://www.kickstarter.com/profile/899550954), 51 | [Jonathan McCoy](https://www.kickstarter.com/profile/2082177678), 52 | [Jonny Hamilton](https://www.kickstarter.com/profile/1671213970), 53 | [Jonny LeRoy](https://www.kickstarter.com/profile/1187618867), 54 | [Katy Atchison](https://www.kickstarter.com/profile/1805469635), 55 | [Lance](https://www.kickstarter.com/profile/682935683), 56 | [Laurie Dewan](https://www.kickstarter.com/profile/1866687453), 57 | [Lawrence Yu](https://www.kickstarter.com/profile/251870332), 58 | [Luis Velez](https://www.kickstarter.com/profile/1604244352), 59 | [Mario Bogantes](https://www.kickstarter.com/profile/1628770794), 60 | [Matt Cohen](https://www.kickstarter.com/profile/254996529), 61 | [Michael Pope](https://www.kickstarter.com/profile/124039772), 62 | [Michelle Novak](https://www.kickstarter.com/profile/765081405), 63 | [Michelle Vaquilar](https://www.kickstarter.com/profile/263233109), 64 | [Nate Amarose](https://www.kickstarter.com/profile/1753074515), 65 | [Odin Dutton](https://www.kickstarter.com/profile/504607824), 66 | [Ondrej Nekola](https://www.kickstarter.com/profile/1074590221), 67 | [Pascal B. Kalou](https://www.kickstarter.com/profile/1735222166), 68 | [Patty](https://www.kickstarter.com/profile/259531021), 69 | [Paul Henderson](https://www.kickstarter.com/profile/1353952072), 70 | [Sajjad Bashar](https://www.kickstarter.com/profile/115363782), 71 | [Sammy Lao](https://www.kickstarter.com/profile/1616583675), 72 | [Stephanie Rose](https://www.kickstarter.com/profile/1256990289), 73 | [Steve Alandt](https://www.kickstarter.com/profile/239251021), 74 | [Steve Howard](https://www.kickstarter.com/profile/132523397), 75 | [Steven Roberts](https://www.kickstarter.com/profile/1000405747), 76 | [Vance Morosi](https://www.kickstarter.com/profile/1771833525), 77 | [Vineet Mehta](https://www.kickstarter.com/profile/1423958530), 78 | and Anonymous. 79 | 80 | ### Helpers 81 | 82 | * Project readers: Andy Bas, Chris Clark, Marcus Breese, Matt Cohen. 83 | * Project feedback: Janis Papanagnou, Andrew Schorr. 84 | * Tony Fischetti, author of the qstats tool, and its contributors. 85 | * Assaf Gordon, maintainer of the datamash tool, and its contributors. 86 | * Thomas Dickey, maintainer of the mawk tool, and its contributors. 87 | * Arnold Robbins, maintainer of the gawk tool on Unix and POSIX systems, and its maintainers and contributors on all systems. 88 | * Ross Ihaka and Robert Gentleman, creators of the R language, and its contributors. 89 | * Jeff Bezanson, Stefan Karpinski, Viral B. Shah, and Alan Edelman, creators of the Julia language, and its contributors. 90 | * Linus Torvalds, creator of Linux and git, and their contributors. 91 | * All the myriad open source authors, advocates, and users. 92 | 93 | 94 |


98 | -------------------------------------------------------------------------------- /doc/todo.md: -------------------------------------------------------------------------------- 1 | # To do: plans and ideas for our future versions 2 | 3 | ## Roadmap 4 | 5 | Num top ten roadmap goals: 6 | 7 | 1. Create more functionality -- Such as for rankings, histograms, outliers, modes, jarque, dpo, and larger numbers. 8 | 9 | 2. Create input-scrubbing capabilities -- Such as extracting numbers from mixed-type data, parsing numbers that look like currencies or percentages, handling missing data or malformed data, alerting if non-numbers are disrupting results, etc. 10 | 11 | 3. Upgrade options for input and output -- Add columns and rows, headers and labels, output formats for HTML/JSON/JSONB, Unicode symbols, etc. 12 | 13 | 4. Improve help -- Continue enhanching the Num website, tutorials, examples. Add an IRC channel for people who want help and want to contribute. Collect documentation as a PDF book focused on command-line statistics, because our experience shows this is an important way to drive adoption in larger organizations including academia and enterprises. 14 | 15 | 5. Optimize speed -- Use caching, memoization, heuristics, and input hinting. 16 | 17 | 6. Implement on more systems -- Build pure Mac OS X compatibility, and pure POSIX compatibility, and Cygwin compatibility for Windows. Package Num by using various package managers, including apt, brew, yum, etc. 18 | 19 | 7. Create appendable statistics, such as taking an input of an existing count and mean, and appending new numbers to the statistic\ s. We believe this is a killer feature for combining batch-oriented processing with stream-oriented processing. Our testing so far shows that additive statistics can give speed increases of 2x-5x for the real-world data we're\ using in real-world projects. 20 | 21 | 8. Implement in a fast compiled language -- We expect this make Num run 2x-5x faster, and also open up long term possibilities for advanced data structures. We hope to be able to jumpstart this by working with existing open source statistics programmers and code bases, such as datamash and qsort. 22 | 23 | 9. Encourage use of Num -- Such as working with teaching groups (e.g. edX, Khan Academy, Coursera, Udacity), coding groups (e.g. RedHat, Canonical, Apple, Google), and publishing groups (e.g. Amazon, O'Reilly, Pragmatic). These organizations can help us achieve the best success for the project, and can also help the most people. 24 | 25 | 10. Long term we want to advocate for Num to become a Unix command that is automatically installed on all Unix systems, much like common command line tools such as grep and sed. We want this to include working with system vendors and also with programmers who can make the code faster and better for cross-platform uses. 26 | 27 | 28 | ### Ideas for version 2.x and future 29 | 30 | Number sizes: 31 | 32 | * Big numbers 33 | * Research adding GNU MPFR and GNU MP (GMP). 34 | * Research adding GNU Awk `--bignum` flag. 35 | 36 | Number functions: 37 | 38 | * `transpose` 39 | 40 | * `reverse` 41 | 42 | * `count-*`, `select-*`, `reject-*` for positive, negative, zero, even, odd, unique. 43 | 44 | * `frequency` 45 | 46 | * possibly filters based on quantile, such as `=q1`, `>q1`, `
125 | -------------------------------------------------------------------------------- /doc/troubleshooting.md: -------------------------------------------------------------------------------- 1 | # Troubleshooting 2 | 3 | Num works on any operating system that is relatively current, and that has the GNU Awk software that is relatively current. 4 | 5 | If Num is not working for you, please try these steps and let us know what works and what doesn't. 6 | 7 | ## Can you run GNU Awk, and is it current? 8 | 9 | Run this: 10 | 11 | gawk --version 12 | 13 | * The version should be 4.1.3 or higher. 14 | * If the version is lower, please upgrade to a current version, such as by running `brew upgrade gawk`, `apt-get upgrade gawk`, etc. 15 | * If you don't have GNU awk, please install it; see the [Install page](install.md). 16 | 17 | ## Can you run Num, and is it current? 18 | 19 | Run this: 20 | 21 | num --version 22 | 23 | * The version should be 1.2.0 or higher. 24 | * If the version is lower, please install the current version; see the [Install page](install.md). 25 | 26 | ## Can you open an issue? 27 | 28 | Go to https://github.com/numcommand/num/issues 29 | 30 | * Click the "New issue" button. 31 | * Explain your issue: tell us what's happening, what error messages you're getting, and any other information. 32 | -------------------------------------------------------------------------------- /screenshots/num-screenshot-01-basics.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/numcommand/num/933415f824126b7bf2a3d3940c2cb119cd91e71d/screenshots/num-screenshot-01-basics.png -------------------------------------------------------------------------------- /screenshots/num-screenshot-02-systems.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/numcommand/num/933415f824126b7bf2a3d3940c2cb119cd91e71d/screenshots/num-screenshot-02-systems.png -------------------------------------------------------------------------------- /screenshots/num-screenshot-03-curls.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/numcommand/num/933415f824126b7bf2a3d3940c2cb119cd91e71d/screenshots/num-screenshot-03-curls.png -------------------------------------------------------------------------------- /src/num-absolute-value.awk: -------------------------------------------------------------------------------- 1 | ############################################################################ 2 | # 3 | # num-absolute-value.awk 4 | # 5 | ## 6 | 7 | ## 8 | # 9 | # Absolute value. 10 | # 11 | # Examples: 12 | # 13 | # abs(1) => 1 14 | # abs(-1) => 1 15 | # 16 | ## 17 | 18 | function num_absolute_value(x) { 19 | return (x >= 0) ? x : -x 20 | } 21 | 22 | # Alias 23 | function num_abs(x) { return num_absolute_value(x) } 24 | -------------------------------------------------------------------------------- /src/num-argv.awk: -------------------------------------------------------------------------------- 1 | ############################################################################ 2 | # 3 | # num-argv.awk 4 | # 5 | ## 6 | 7 | ## 8 | # 9 | # Parse words by iterating: when a word is recognized, 10 | # move it from the inputs array to the outputs array. 11 | # 12 | # Example to parse ARGV: 13 | # 14 | # parse_words(ARGV, global_word_argv, synonyms) 15 | # 16 | ## 17 | 18 | function num_argv_parse(inputs, outputs, synonyms, i, imax, item, outputs_n) { 19 | split("", outputs) 20 | outputs_n = 0 21 | imax = length(inputs) 22 | for (i=1; i<=imax; i++) { 23 | item = tolower(inputs[i]) 24 | gsub(/[-_]/, "", item) 25 | if (item in synonyms) { 26 | item = synonyms[item] 27 | delete inputs[i] 28 | outputs[++outputs_n] = item 29 | } 30 | } 31 | } 32 | 33 | ## 34 | # 35 | # Parse the command line ARGV inputs to recognized word outputs. 36 | # 37 | ## 38 | 39 | function num_argv() { 40 | num_argv_parse(ARGV, global_word_argv, num_synonyms) 41 | } 42 | -------------------------------------------------------------------------------- /src/num-arr.awk: -------------------------------------------------------------------------------- 1 | ############################################################################ 2 | # 3 | # num-arr.awk 4 | # 5 | ## 6 | 7 | ## 8 | # 9 | # Dump an array, suitable for debugging. 10 | # 11 | # Example: 12 | # 13 | # num_arr_dump(arr) 14 | # 1 a 15 | # 2 b 16 | # 3 d 17 | # 18 | function num_arr_dump(arr) { 19 | for (k in arr) print k, arr[k] 20 | } 21 | 22 | ## 23 | # 24 | # Is an array empty? 25 | # 26 | # Example: 27 | # 28 | # split("", arr) 29 | # num_arr_empty(arr) => TRUE 30 | # 31 | # This is POSIX compatible. 32 | # 33 | function num_arr_empty(arr, i) { 34 | for (i in arr) return FALSE 35 | return TRUE 36 | } 37 | 38 | ## 39 | # 40 | # Length of an array. 41 | # 42 | # Example: 43 | # 44 | # num_arr_length(1 2 4) => 3 45 | # 46 | # TODO: benchmark this POSIX implementation with 47 | # any Gawk implementation, and if Gawk is much faster, 48 | # then research a way to use the Gawk implementation. 49 | # 50 | function num_arr_length(arr, i, len) { 51 | for (i in arr) len++ 52 | return len 53 | } 54 | 55 | ## 56 | # 57 | # Swap array items. 58 | # 59 | # Example: 60 | # 61 | # arr = 4 5 6 62 | # num_arr_swap(arr, 1, 3) => 6 5 4 63 | # 64 | ## 65 | 66 | function num_arr_swap(A, i, j, t) { 67 | t = A[i]; A[i] = A[j]; A[j] = t 68 | } 69 | 70 | ## 71 | # 72 | # Get the closest value to a target value in an array. 73 | # 74 | # Example: 75 | # 76 | # arr = 1 2 4 77 | # target = 2.5 78 | # num_arr_closest_value(arr, target) => 2 79 | # 80 | # If multiple values are equidistant to the target, 81 | # then return the earliest index. 82 | # 83 | # TODO optimize when the array is already sorted, 84 | # by using quicksort or similar divide-and-conquer. 85 | # 86 | function num_arr_closest_value(arr, target, _closest_value, _closest_delta, _delta, x, i) { 87 | for (i in arr) { 88 | _delta = num_abs(arr[i] - target) 89 | if (_closest_delta == "" || _delta < _closest_delta) { 90 | _closest_value = arr[i] 91 | _closest_delta = _delta 92 | } 93 | } 94 | return _closest_value 95 | } 96 | 97 | ## 98 | # 99 | # Join an array to a string, with a separator string. 100 | # 101 | # Examples: 102 | # 103 | # num_arr_join(1 2 3, OFS) => "1 2 3" 104 | # num_arr_join(1 2 3, ",") => "1,2,3" 105 | # 106 | ## 107 | 108 | function num_arr_join(arr, sep, s, i) { 109 | s = "" 110 | for (i = 1; i <= num_arr_length(arr); i++) s = s arr[i] sep 111 | return substr(s, 1, length(s) - length(sep)) 112 | } 113 | 114 | ## 115 | # 116 | # Join an array to a string, with a separator string, prefix, and suffix. 117 | # 118 | # Examples: 119 | # 120 | # num_arr_join(1 2 3, OFS, "<", ">") => "<1> <2> <4>" 121 | # num_arr_join(1 2 3, ",", "(", ")") => "(1),(2),(4)" 122 | # 123 | ## 124 | 125 | function num_arr_join_with_prefix_suffix(arr, sep, prefix, suffix, s, i) { 126 | s = "" 127 | for (i = 1; i <= num_arr_length(arr); i++) s = prefix arr[i] suffix sep 128 | return substr(s, 1, length(s) - length(sep)) 129 | } 130 | -------------------------------------------------------------------------------- /src/num-coefficient-of-variance.awk: -------------------------------------------------------------------------------- 1 | ############################################################################ 2 | # 3 | # num-coefficient-of-variance.awk 4 | # 5 | ## 6 | 7 | # Alias 8 | function num_coefficient_of_variance(arr) { num_sample_coefficient_of_variance(arr) } 9 | function num_coefficient_of_variance_(num, num_, opts) { num_sample_coefficient_of_variance_(num, num_, opts) } 10 | 11 | # Alias 12 | function num_covar(arr) { num_sample_coefficient_of_variance(arr) } 13 | function num_covar_(num, num_, opts) { num_sample_coefficient_of_variance_(num, num_, opts) } 14 | 15 | ## 16 | # 17 | # Sample Coefficient of Variance. 18 | # 19 | # Example: 20 | # 21 | # num_sample_coefficient_of_variance(1 2 4) => 0.654654 22 | # 23 | ## 24 | 25 | function num_sample_coefficient_of_variance(arr) { 26 | return num_sample_standard_deviation(arr) / num_mean(arr) 27 | } 28 | 29 | function num_sample_coefficient_of_variance_(num, num_, opts, f) { 30 | f = "num_sample_coefficient_of_variance" 31 | if (!(f in num_)) num_[f] = num_sample_standard_deviation_(num, num_, opts) / num_mean_(num, num_, opts) 32 | return num_[f] 33 | } 34 | 35 | function num_sample_coefficient_of_variance_init() { 36 | num_function_init(\ 37 | "num_sample_coefficient_of_variance sample_coefficient_of_variance s_co_var s_c_v coefficient_of_variance co_var c_v sample_relative_standard_deviation s_r_s_d relative_standard_deviation r_s_d", 0, 38 | "Get the sample coefficient of variance", 39 | "https://en.wikipedia.org/wiki/Coefficient_of_variation") 40 | } 41 | 42 | # Alias 43 | function num_scovar(arr) { num_sample_coefficient_of_variance(arr) } 44 | function num_scovar_(num, num_, opts) { num_sample_coefficient_of_variance_(num, num_, opts) } 45 | 46 | ## 47 | # 48 | # Population Coefficient of Variance. 49 | # 50 | # Example: 51 | # 52 | # 1 2 4 => 0.534522 53 | # 54 | ## 55 | 56 | function num_population_coefficient_of_variance(arr) { 57 | return num_population_standard_deviation(arr) / num_mean(arr) 58 | } 59 | 60 | function num_population_coefficient_of_variance_(num, num_, opts, f) { 61 | f = "num_population_coefficient_of_variance" 62 | if (!(f in num_)) num_[f] = num_population_standard_deviation_(num, num_, opts) / num_mean_(num, num_, opts) 63 | return num_[f] 64 | } 65 | 66 | function num_population_coefficient_of_variance_init() { 67 | num_function_init(\ 68 | "num_population_coefficient_of_variance population_coefficient_of_variance p_co_var p_c_v population_relative_standard_deviation p_r_s_d", 0, 69 | "Get the population coefficient of variance.", 70 | "https://en.wikipedia.org/wiki/Coefficient_of_variation") 71 | } 72 | 73 | # Alias 74 | function num_pcovar(arr) { num_population_coefficient_of_variance(arr) } 75 | function num_pcovar_(num, num_, opts) { num_population_coefficient_of_variance_(num, num_, opts) } 76 | -------------------------------------------------------------------------------- /src/num-conf.awk: -------------------------------------------------------------------------------- 1 | ############################################################################ 2 | # 3 | # num-conf.awk 4 | # 5 | ## 6 | 7 | ## 8 | # 9 | # Configure 10 | # 11 | ## 12 | 13 | function num_conf() { 14 | num_conf_words(global_word_argv) 15 | num_conf_ofmt() 16 | num_conf_scope() 17 | } 18 | 19 | ## 20 | # 21 | # num_configure the output format string, a.k.a. AWK OFMT. 22 | # This controls number formatting as float, decimal, int, etc. 23 | # 24 | ## 25 | 26 | function num_conf_ofmt() { 27 | OFMT = (ENVIRON["OFMT"]) ? ENVIRON["OFMT"] : "%.6g" 28 | } 29 | 30 | ## 31 | # num_configure scope flags to optimize for tight inner loops. 32 | # 33 | # This enables us to speed up code by doing this: 34 | # 35 | # - if (global_conf["scope"] == NUM_CONF_SCOPE_ALL) 36 | # + if (NUM_CONF_SCOPE_ALL_FLAG) 37 | ## 38 | 39 | function num_conf_scope() { 40 | NUM_CONF_SCOPE_ALL_FLAG = (global_conf["scope"] == NUM_CONF_SCOPE_ALL) 41 | NUM_CONF_SCOPE_RECORD_FLAG = (global_conf["scope"] == NUM_CONF_SCOPE_RECORD) 42 | NUM_CONF_SCOPE_FIELD_FLAG = (global_conf["scope"] == NUM_CONF_SCOPE_FIELD) 43 | } 44 | 45 | ## 46 | # 47 | # Given a word, set a num_configuration setting. 48 | # 49 | # Call this function for each option word a.k.a. flag, 50 | # before any calculation happens and any work happens. 51 | # 52 | # This function must only set `conf` keys and values. 53 | # This function must NOT do any calculations, work, etc. 54 | # 55 | # TODO: consider changing these to functions. 56 | # 57 | ## 58 | 59 | function num_conf_word(word) { 60 | if (word == "") 61 | return 62 | else if (word == "help") 63 | num_help() 64 | else if (word == "num_conf_scope_all") 65 | global_conf["scope"] = NUM_CONF_SCOPE_ALL 66 | else if (word == "num_conf_scope_record") 67 | global_conf["scope"] = NUM_CONF_SCOPE_RECORD 68 | else if (word == "num_conf_scope_field") 69 | global_conf["scope"] = NUM_CONF_SCOPE_FIELD 70 | else if (word == "num_io_comma_separated_values") { FS = ","; RS = "\n"; OFS = ","; ORS = "\n" } 71 | else if (word == "num_input_comma_separated_values") { FS = ","; RS = "\n" } 72 | else if (word == "num_output_comma_separated_values") { OFS = ","; ORS = "\n" } 73 | else if (word == "num_io_tab_separated_values" ) { FS = "\t"; RS = "\n"; OFS = "\t"; ORS = "\n"} 74 | else if (word == "num_input_tab_separated_values" ) { FS = "\t"; RS = "\n" } 75 | else if (word == "num_output_tab_separated_values") { OFS = "\t"; ORS = "\n" } 76 | else if (word == "num_io_unit_separated_values") { FS = "␟"; RS = "␞"; OFS = "␟"; ORS = "␞" } 77 | else if (word == "num_input_unit_separated_values") { FS = "␟"; RS = "␞" } 78 | else if (word == "num_output_unit_separated_values") { OFS = "␟"; ORS = "␞" } 79 | else 80 | return "" 81 | } 82 | 83 | ## 84 | # 85 | # Given a list of words, set all num_configuration settings. 86 | # 87 | ## 88 | 89 | function num_conf_words(words, imax) { 90 | for (i=1; i <= num_arr_length(words); i++) num_conf_word(words[i]) 91 | } 92 | -------------------------------------------------------------------------------- /src/num-frequency.awk: -------------------------------------------------------------------------------- 1 | ############################################################################ 2 | # 3 | # num-frequency.awk 4 | # 5 | ## 6 | 7 | ## 8 | # 9 | # Frequency minimum. 10 | # 11 | # Example: 12 | # 13 | # num_frequency_min(10 10 11 11 11) => 2 14 | # 15 | ## 16 | 17 | function num_frequency_min(arr, min, i, seen) { 18 | for (i in arr) ++seen[arr[i]] 19 | return num_min(seen) 20 | } 21 | 22 | function num_frequency_min_(num, num_, opts, f) { 23 | f = "num_frequency_min" 24 | if (!(f in num_)) num_[f] = num_frequency_min(num) # TODO optimize when min and min are both wanted 25 | return num_[f] 26 | } 27 | 28 | function num_frequency_min_init() { 29 | num_function_init(\ 30 | "num_frequency_min frequency_minimum freq_min", 0, 31 | "Get the frequency minimum: count the occurances of each value, and get the minimum count.", 32 | "https://en.wikipedia.org/wiki/Frequency_Minimum_(statistics)") 33 | } 34 | 35 | ## 36 | # 37 | # Frequency maximum. 38 | # 39 | # Example: 40 | # 41 | # num_frequency_max(10 10 11 11 11) => 3 42 | # 43 | ## 44 | 45 | function num_frequency_max(arr, max, i, seen) { 46 | for (i in arr) ++seen[arr[i]] 47 | return num_max(seen) 48 | } 49 | 50 | function num_frequency_max_(num, num_, opts, f) { 51 | f = "num_frequency_max" 52 | if (!(f in num_)) num_[f] = num_frequency_max(num) # TODO optimize when min and max are both wanted 53 | return num_[f] 54 | } 55 | 56 | function num_frequency_max_init() { 57 | num_function_init(\ 58 | "num_frequency_max frequency_maximum freq_max", 0, 59 | "Get the frequency maximum: count the occurances of each value, and get the maximum count.", 60 | "https://en.wikipedia.org/wiki/Frequency_Maximum_(statistics)") 61 | } 62 | -------------------------------------------------------------------------------- /src/num-function.awk: -------------------------------------------------------------------------------- 1 | ############################################################################ 2 | # 3 | # num-function.awk 4 | # 5 | ## 6 | 7 | ## 8 | # 9 | # Initialize function metadata for a given function. 10 | # 11 | # Example: 12 | # 13 | # function hello() { 14 | # print "hello world" 15 | # } 16 | # 17 | # function hello_init() { 18 | # num_function_init("hello hi hola", "Print a greeting", "http://example.com/hello.html") 19 | # } 20 | # 21 | # The example creates these: 22 | # 23 | # num_functions["hello", "names"] = "hello hi hola" 24 | # num_functions["hello", "help"] = "Print a greeting" 25 | # num_functions["hello", "link"] = "http://example.com/hello.html" 26 | # num_synonyms["hello"] = "hello" 27 | # num_synonyms["hi"] = "hello" 28 | # num_synonyms["hola"] = "hello" 29 | # 30 | ## 31 | 32 | function num_function_init(names, arity, help, link, f, i, name, name_list) { 33 | split(names, names_arr) 34 | f = names_arr[1] 35 | num_functions[f, "names"] = names 36 | num_functions[f, "arity"] = arity 37 | num_functions[f, "help"] = help 38 | num_functions[f, "link"] = link 39 | for (i in names_arr) { 40 | name = names_arr[i] 41 | gsub(/_/,"", name) 42 | num_synonyms[name] = f 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/num-help.awk: -------------------------------------------------------------------------------- 1 | ############################################################################ 2 | # 3 | # num-help.awk 4 | # 5 | ## 6 | 7 | function num_help() { 8 | print "Num version 1.3.0." 9 | print "Update 2015-11-30." 10 | print "" 11 | print "Copyright (C) 2015 Joel Parker Henderson." 12 | print "Please see http://github.com/numcommand" 13 | print "" 14 | print "Num uses this Awk:" 15 | print awk 16 | print "" 17 | cmd = awk "-Wversion 2>/dev/null || awk --version" 18 | print cmd 19 | system(cmd) 20 | exit 21 | } 22 | 23 | function num_help_init() { 24 | num_function_init(\ 25 | "help version usage", 0, 26 | "Print help, version, usage.", 27 | "") 28 | } 29 | -------------------------------------------------------------------------------- /src/num-increment.awk: -------------------------------------------------------------------------------- 1 | ############################################################################ 2 | # 3 | # num-increment.awk 4 | # 5 | ## 6 | 7 | ## 8 | # 9 | # Increment a value, i.e. add 1. 10 | # 11 | # Examples: 12 | # 13 | # increment(1) => 2 14 | # 15 | ## 16 | 17 | function num_increment(x) { 18 | return x + 1 19 | } 20 | -------------------------------------------------------------------------------- /src/num-init.awk: -------------------------------------------------------------------------------- 1 | ############################################################################ 2 | # 3 | # num-init.awk 4 | # 5 | ## 6 | 7 | ## 8 | # 9 | # Initialize everything. 10 | # 11 | # This calls various `init_*` functions. 12 | # 13 | ## 14 | 15 | function num_init() { 16 | #init_lint() 17 | num_init_constants() 18 | num_init_conf() 19 | num_init_word_argv() 20 | num_init_word_list() 21 | num_function_manager_init() 22 | } 23 | 24 | ## 25 | # 26 | # Initialize constants that we use; these are essentially like defines. 27 | # 28 | ## 29 | 30 | function num_init_constants() { 31 | 32 | # Boolean 33 | FALSE = 0 34 | TRUE = 1 35 | 36 | # Math 37 | PI = 3.141592653589797 # also atan2(0,-1) 38 | 39 | # Content 40 | TODO = "TODO" 41 | UNDEF = "UNDEF" 42 | ERROR = "ERROR" 43 | NAN = "NAN" 44 | 45 | # Function kinds: a way to track what a function will do. #TODO 46 | FUN_KIND_CALC = "CALC" 47 | FUN_KIND_SORT = "SORT" 48 | FUN_KIND_MAP = "MAP" 49 | FUN_KIND_FLAG = "FLAG" 50 | FUN_KIND_CONF = "CONF" 51 | 52 | # Feature detection. Currently this script requires Gawk. 53 | AWK_HAS_ASORT = TRUE 54 | AWK_HAS_LENGTH = TRUE 55 | 56 | # The scope controls whether the user wants input and output 57 | # to be all items (the default), or per record, or per field. 58 | # The scope per field is a special case, because it lets the 59 | # the awk loop read one record, calculate, then flush data. 60 | NUM_CONF_SCOPE_ALL = "NUM_CONF_SCOPE_ALL" # Default 61 | NUM_CONF_SCOPE_RECORD = "NUM_CONF_SCOPE_RECORD" # Triggers optimized loop 62 | NUM_CONF_SCOPE_FIELD = "NUM_CONF_SCOPE_FIELD" # TODO implement 63 | } 64 | 65 | ## 66 | # 67 | # Initialize the configuration dictionary. 68 | # 69 | ## 70 | 71 | function num_init_conf() { 72 | split("", global_conf) 73 | global_conf["scope"] = NUM_CONF_SCOPE_ALL 74 | } 75 | 76 | ## 77 | # 78 | # Initialize the word argv list. 79 | # 80 | # The word argv list holds the argv items that this script cares about. 81 | # We keep them in order, so we can output results in order. 82 | # 83 | ## 84 | 85 | function num_init_word_argv() { 86 | split("", global_word_argv) 87 | } 88 | 89 | ## 90 | # 91 | # Initialize the global word list lookup array. 92 | # 93 | # This is to recognize words that a user types on the command line. 94 | # 95 | # TODO: research if there is a better way to initialize a dictionary. 96 | # 97 | ## 98 | 99 | function num_init_word_list() { 100 | 101 | num_synonyms["overall"] = \ 102 | "num_conf_scope_all" 103 | 104 | num_synonyms["records"] = \ 105 | num_synonyms["rows"] = \ 106 | "num_conf_scope_record" 107 | 108 | num_synonyms["fields"] = \ 109 | num_synonyms["colums"] = \ 110 | "num_conf_scope_field" 111 | 112 | # Convention: default is sample, not population. 113 | num_synonyms["secondmomentaboutthemean"] = \ 114 | num_synonyms["secondmoment"] = \ 115 | "num_sample_variance" 116 | 117 | # Convention: default is sample, not population. 118 | num_synonyms["thirdmomentaboutthemean"] = \ 119 | num_synonyms["thirdmoment"] = \ 120 | "num_sample_skewness" 121 | 122 | # Convention: default is sample, not population. 123 | num_synonyms["fourthmomentaboutthemean"] = \ 124 | num_synonyms["fourthmoment"] = \ 125 | "num_sample_kurtosis" 126 | 127 | ## Booleans 128 | 129 | num_synonyms["isunique"] = \ 130 | "num_is_unique" 131 | 132 | num_synonyms["isascending"] = \ 133 | num_synonyms["isasc"] = \ 134 | num_synonyms["isnondescending"] = \ 135 | num_synonyms["isnondesc"] = \ 136 | "num_is_ascending" 137 | 138 | num_synonyms["isstrictlyascending"] = \ 139 | num_synonyms["isstrictasc"] = \ 140 | "num_is_strictly_ascending" 141 | 142 | num_synonyms["isdescending"] = \ 143 | num_synonyms["isdesc"] = \ 144 | num_synonyms["isnonascending"] = \ 145 | num_synonyms["isnonasc"] = \ 146 | "num_is_descending" 147 | 148 | num_synonyms["isstrictlydescending"] = \ 149 | num_synonyms["isstrictdesc"] = \ 150 | "num_is_strictly_descending" 151 | 152 | ### Configurations 153 | 154 | num_synonyms["commaseparatedvalues"] = \ 155 | num_synonyms["csv"] = \ 156 | "num_io_comma_separated_values" 157 | 158 | num_synonyms["inputcommaseparatedvalues"] = \ 159 | num_synonyms["inputcsv"] = \ 160 | num_synonyms["incsv"] = \ 161 | "num_input_comma_separated_values" 162 | 163 | num_synonyms["outputcommaseparatedvalues"] = \ 164 | num_synonyms["outputcsv"] = \ 165 | num_synonyms["outcsv"] = \ 166 | "num_output_comma_separated_values" 167 | 168 | num_synonyms["tabseparatedvalues"] = \ 169 | num_synonyms["tsv"] = \ 170 | "num_io_tab_separated_values" 171 | 172 | num_synonyms["inputtabseparatedvalues"] = \ 173 | num_synonyms["inputtsv"] = \ 174 | num_synonyms["intsv"] = \ 175 | "num_input_tab_separated_values" 176 | 177 | num_synonyms["outputtabseparatedvalues"] = \ 178 | num_synonyms["outputtsv"] = \ 179 | num_synonyms["outtsv"] = \ 180 | "num_output_tab_separated_values" 181 | 182 | num_synonyms["unitseparatedvalues"] = \ 183 | num_synonyms["usv"] = \ 184 | "num_io_unit_separated_values" 185 | 186 | num_synonyms["inputunitseparatedvalues"] = \ 187 | num_synonyms["inputusv"] = \ 188 | num_synonyms["inusv"] = \ 189 | "num_input_unit_separated_values" 190 | 191 | num_synonyms["outputunitseparatedvalues"] = \ 192 | num_synonyms["outputusv"] = \ 193 | num_synonyms["outusv"] = \ 194 | "num_output_unit_separated_values" 195 | 196 | } 197 | -------------------------------------------------------------------------------- /src/num-insertion-sort.awk: -------------------------------------------------------------------------------- 1 | ############################################################################ 2 | # 3 | # num-insertion-sort.awk 4 | # 5 | ## 6 | 7 | ## 8 | # 9 | # Insertion sort. 10 | # 11 | # This implementation is a slightly faster version that moves A[i] 12 | # to its position in one go and only performs one assignment in the 13 | # inner loop body. 14 | # 15 | # Thanks to https://en.wikipedia.org/wiki/Insertion_sort 16 | # 17 | ## 18 | 19 | function num_insertion_sort(A) { 20 | num_insertion_sort_slice(A, 1, num_arr_length(A)) 21 | } 22 | 23 | function num_insertion_sort_slice(A, lo, hi, i, j, x) { 24 | if (lo >= hi) return 25 | for (i = lo + 1; i <= hi; i++) { 26 | x = A[i] 27 | j = i 28 | while (j > lo && A[j-1] > x) { 29 | A[j] = A[j-1] 30 | j-- 31 | } 32 | A[j] = x 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/num-kurtosis.awk: -------------------------------------------------------------------------------- 1 | ############################################################################ 2 | # 3 | # num-kurtosis.awk 4 | # 5 | # A.k.a. fourth moment about the mean. 6 | # 7 | # Calculation: 8 | # 9 | # * Sum each value’s deviation from the mean quaded. 10 | # * Divide by the number of items. 11 | # 12 | # The kurtosis formula measures the degree of peak. 13 | # 14 | # Kurtosis measures the heaviness of tails, 15 | # relative to a normal distribution. 16 | # 17 | # * Positive kurtosis (peakness) is termed leptokurtic. 18 | # * Negative kurtosis (flatness) is termed platykurtic. 19 | # * In-between is termed mesokurtic. 20 | # 21 | # Kurtosis equals 3 for a normal distribution. 22 | # 23 | # Kurtosis is a nondimensional quantity. 24 | # 25 | ## 26 | 27 | # Alias 28 | function num_kurtosis(arr) { num_sample_kurtosis(arr) } 29 | function num_kurtosis_(num, num_, opts) { num_sample_kurtosis_(num, num_, opts) } 30 | 31 | # Alias 32 | function num_kurt(arr) { num_sample_kurtosis(arr) } 33 | function num_kurt_(num, num_, opts) { num_sample_kurtosis_(num, num_, opts) } 34 | 35 | ## 36 | # 37 | # Sample kurtosis 38 | # 39 | # Example: 40 | # 41 | # num_sample_kurtosis(1 2 4) => 5.44444 42 | # 43 | ## 44 | 45 | function num_sample_kurtosis(arr) { 46 | return num_sum_of_mean_deviation_exp(arr, num_mean(arr), 4) / (num_n(arr) - 1) 47 | } 48 | 49 | function num_sample_kurtosis_(num, num_, opts, f) { 50 | f = "num_sample_kurtosis" 51 | if (!(f in num_)) num_[f] = num_sum_of_mean_deviation_exp_(num, num_, opts, num_mean_(num, num_, opts), 4) / (num_n_(num, num_, opts) - 1) 52 | return num_[f] 53 | } 54 | 55 | function num_sample_kurtosis_init() { 56 | num_function_init(\ 57 | "num_sample_kurtosis sample_kurtosis s_kurt kurtosis kurt sample_fourth_moment_about_the_mean s_fourth_moment_about_the_mean s_4_m_a_t_m fourth_moment_about_the_mean 4_m_a_t_m", 0, 58 | "Get the kurtosis, a.k.a. sample fourth moment about the mean.", 59 | "https://en.wikipedia.org/wiki/Kurtosis") 60 | } 61 | 62 | # Alias 63 | function num_skurt(arr) { num_sample_kurtosis(arr) } 64 | function num_skurt_(num, num_, opts) { num_sample_kurtosis_(num, num_, opts) } 65 | 66 | ## 67 | # 68 | # Population kurtosis 69 | # 70 | # Example: 71 | # 72 | # num_population_kurtosis(1 2 4) => 3.62963 73 | # 74 | ## 75 | 76 | function num_population_kurtosis(arr) { 77 | return num_sum_of_mean_deviation_exp(arr, num_mean(arr), 4) / num_n(arr) 78 | } 79 | 80 | function num_population_kurtosis_(num, num_, opts, f) { 81 | f = "num_population_kurtosis" 82 | if (!(f in num_)) num_[f] = num_sum_of_mean_deviation_exp_(num, num_, opts, num_mean_(num, num_, opts), 4) / num_n_(num, num_, opts) 83 | return num_[f] 84 | } 85 | 86 | function num_population_kurtosis_init() { 87 | num_function_init(\ 88 | "num_population_kurtosis population_kurtosis p_kurt population_fourth_moment_about_the_mean p_fourth_moment_about_the_mean p_4_m_a_t_m", 0, 89 | "Get the kurtosis, a.k.a. population fourth moment about the mean.", 90 | "https://en.wikipedia.org/wiki/Kurtosis") 91 | } 92 | 93 | # Alias 94 | function num_pkurt(arr) { num_population_kurtosis(arr) } 95 | function num_pkurt_(num, num_, opts) { num_population_kurtosis_(num, num_, opts) } 96 | -------------------------------------------------------------------------------- /src/num-lint.awk: -------------------------------------------------------------------------------- 1 | ############################################################################ 2 | # 3 | # num-lint.awk 4 | # 5 | ## 6 | 7 | ## 8 | # 9 | # Initialize lint protection. 10 | # 11 | ## 12 | 13 | function init_lint() { 14 | split("", a) 15 | 16 | ## Basics 17 | num_all(a) 18 | 19 | ## Quartiles 20 | num_quartile_0(a) 21 | num_quartile_1(a) 22 | num_quartile_2(a) 23 | num_quartile_3(a) 24 | num_quartile_4(a) 25 | num_interquartile_range(a) 26 | 27 | ## Round 28 | num_nint(0) 29 | num_truncate(0) 30 | num_ceiling(0) 31 | num_floor(0) 32 | 33 | ## Print 34 | num_out("") 35 | num_err("") 36 | 37 | ## List 38 | num_unshift(a,0) 39 | num_shift(a) 40 | 41 | ## Stack 42 | num_push(a,0) 43 | num_pop(a) 44 | 45 | ## Queue 46 | num_enqueue(a0) 47 | num_dequeue(a) 48 | 49 | ## Statitics 50 | num_mean_absolute_deviation(a) 51 | mum_trimmed_mean(a) 52 | num_median_low(a) 53 | num_median_high(a) 54 | num_sum_of_mean_deviation_exp(a) 55 | 56 | ## Math 57 | num_min(a) 58 | num_max(a) 59 | num_range(a) 60 | num_abs(a) 61 | 62 | ## TODO: these may need fixing 63 | num_arr_empty(a) 64 | num_arr_dump() 65 | #num_word_to_conf() 66 | #num_init_global_fun_kind 67 | } 68 | -------------------------------------------------------------------------------- /src/num-main.awk: -------------------------------------------------------------------------------- 1 | ############################################################################ 2 | # 3 | # num-main.awk 4 | # 5 | ## 6 | 7 | BEGIN{ 8 | num_init() 9 | num_argv() 10 | num_conf() 11 | if (NUM_CONF_SCOPE_ALL_FLAG) scope_start() 12 | } 13 | 14 | { 15 | if (NUM_CONF_SCOPE_ALL_FLAG) { 16 | for(i=1;i<=NF;i++) global_num[++global_num_n] = $i # Inline 17 | } else if (NUM_CONF_SCOPE_RECORD_FLAG) { 18 | scope_start() 19 | for(i=1;i<=NF;i++) global_num[++global_num_n] = $i # Inline 20 | scope_stop() 21 | } else { 22 | num_err("Num configuration scope is not recognized") 23 | } 24 | } 25 | 26 | END{ 27 | if (NUM_CONF_SCOPE_ALL_FLAG) scope_stop() 28 | printf "\n" # TODO add conf flag equivalent to echo -n 29 | } 30 | -------------------------------------------------------------------------------- /src/num-map-absolute-value.awk: -------------------------------------------------------------------------------- 1 | ############################################################################ 2 | # 3 | # num-map-absolute-value.awk 4 | # 5 | ## 6 | 7 | ## 8 | # 9 | # Map absolute value. 10 | # 11 | # Example: 12 | # 13 | # num_map_absolute_value_(1 -2 3) => 1 2 3 14 | # 15 | ## 16 | 17 | function num_map_absolute_value(arr) { 18 | for (i in arr) arr[i] = num_absolute_value(arr[i]) 19 | } 20 | 21 | function num_map_absolute_value_(num, num_, opts, f, i, memo) { 22 | f = "num_map_absolute_value" 23 | if (num_[f] != TRUE) { 24 | map_before_(num, num_, opts, memo) 25 | num_map_absolute_value(num) 26 | map_after_(num, num_, opts, memo) 27 | num_[f] = TRUE 28 | } 29 | return "" 30 | } 31 | 32 | function num_map_absolute_value_init() { 33 | num_function_init(\ 34 | "num_map_absolute_value absolute_value abs magnitude", 0, 35 | "Map using absolute value.", 36 | "https://en.wikipedia.org/wiki/Absolute_value_(algebra)") 37 | } 38 | 39 | # Alias 40 | function num_map_abs(arr) { return num_map_absolute_value(arr) } 41 | function num_map_abs_(num, num_, opts) { return num_map_absolute_value_(num, num_, opts) } 42 | -------------------------------------------------------------------------------- /src/num-map-increment.awk: -------------------------------------------------------------------------------- 1 | ############################################################################ 2 | # 3 | # num-map-increment.awk 4 | # 5 | ## 6 | 7 | ## 8 | # 9 | # Map increment. 10 | # 11 | # Example: 12 | # 13 | # num_map_increment_(1 2 3) => -2 3 4 14 | # 15 | ## 16 | 17 | function num_map_increment(arr) { 18 | for (i in arr) arr[i] = num_increment(arr[i]) 19 | } 20 | 21 | function num_map_increment_(num, num_, opts, f, i, memo) { 22 | f = "num_map_increment" 23 | if (num_[f] != TRUE) { 24 | map_before_(num, num_, opts, memo) 25 | num_map_increment(num) 26 | map_after_(num, num_, opts, memo) 27 | num_[f] = TRUE 28 | } 29 | return "" 30 | } 31 | 32 | function num_map_increment_init() { 33 | num_function_init(\ 34 | "num_map_increment increment", 0, 35 | "Map using increment.", 36 | "https://en.wikipedia.org/wiki/Increment_and_decrement_operators") 37 | } 38 | -------------------------------------------------------------------------------- /src/num-map-normalize.awk: -------------------------------------------------------------------------------- 1 | ############################################################################ 2 | # 3 | # num-map-normalize.awk 4 | # 5 | ## 6 | 7 | ## 8 | # 9 | # Map: normalize each value to be 0 to 1. 10 | # 11 | # Example: 12 | # 13 | # num_map_normalize(1 2 4) => 0 0.33333 1 14 | # 15 | ## 16 | 17 | function num_map_normalize_with_min_max(arr, min_old, max_old, min_new, max_new) { 18 | multiply = (max_new - min_new) / (max_old - min_old) 19 | add = min_new - (multiply * min_old) 20 | for (i in arr) arr[i] = arr[i] * multiply + add 21 | } 22 | 23 | function num_map_normalize(arr) { 24 | return num_map_normalize_with_min_max(arr, num_min(arr), num_max(arr), 0, 1) 25 | } 26 | 27 | function num_map_normalize_(num, num_, opts, f, min_old, max_old, min_new, max_new, multiply, add) { 28 | f = "num_map_normalize" 29 | if (num_[f] != TRUE) { 30 | map_before_(num, num_, opts, memo) 31 | x = num_map_normalize_with_min_max(num, num_min_(num, num_, opts), num_max_(num, num_, opts), 0, 1) 32 | map_after_(num, num_, opts, memo) 33 | num_[f] = TRUE 34 | } 35 | return "" 36 | } 37 | 38 | function num_map_normalize_init() { 39 | num_function_init(\ 40 | "num_map_normalize normalize norm", 0, 41 | "Map using normalize.", 42 | "https://wikipedia.org/wiki/Normalization_(statistics)") 43 | } 44 | -------------------------------------------------------------------------------- /src/num-map-round.awk: -------------------------------------------------------------------------------- 1 | ############################################################################ 2 | # 3 | # num-map-round.awk 4 | # 5 | ## 6 | 7 | ## 8 | # 9 | # Initialize. 10 | # 11 | ## 12 | 13 | function num_map_round_awk_init() { 14 | #TODO refactor 15 | NUM_MAP_ROUND_MEMO["n"] = \ 16 | NUM_MAP_ROUND_MEMO["sorted"] = \ 17 | NUM_MAP_ROUND_MEMO["ascending"] = \ 18 | NUM_MAP_ROUND_MEMO["descending"] = \ 19 | TRUE 20 | } 21 | 22 | ## 23 | # 24 | # Map round, a.k.a. round towards nearest integer, nint. 25 | # 26 | # Example: 27 | # 28 | # num_map_round(-1.9 1.9) => -2 2 29 | # 30 | ## 31 | 32 | function num_map_round(arr) { 33 | for (i in arr) arr[i] = num_round(arr[i]) 34 | } 35 | 36 | function num_map_round_(num, num_, opts, f, memo, i) { 37 | if (num_["integer"] == TRUE) return 38 | f = "num_map_round" 39 | if (num_[f] != TRUE) { 40 | num_map_round_before_(num, num_, opts, f, memo) 41 | num_map_round(num) 42 | num_map_round_after_(num, num_, opts, f, memo) 43 | } 44 | return "" 45 | } 46 | 47 | function num_map_round_init() { 48 | num_function_init(\ 49 | "num_map_round round round_towards_nearest nearest_integer n_int", 0, 50 | "Map using round, a.k.a. round towards nearest, nint.", 51 | "https://en.wikipedia.org/wiki/Rounding") 52 | } 53 | 54 | # Alias 55 | function num_map_nint(arr) { 56 | return num_map_round(arr) 57 | } 58 | 59 | # Alias 60 | function num_map_nint_(num, num_, opts) { 61 | return num_map_round_(num, num_, opts) 62 | } 63 | 64 | ## 65 | # 66 | # Map: round off, a.k.a. round towards zero, truncate. 67 | # 68 | # Example: 69 | # 70 | # num_map_round_off(-1.9 1.9) => -1 1 71 | # 72 | ## 73 | 74 | function num_map_round_off(arr) { 75 | for (i in arr) arr[i] = num_round_off(arr[i]) 76 | } 77 | 78 | function num_map_round_off_(num, num_, opts, f, memo) { 79 | if (num_["integer"] == TRUE) return "" 80 | f = "num_map_round_off" 81 | if (num_[f] != TRUE) { 82 | num_map_round_before_(num, num_, opts, f, memo) 83 | num_map_round_off(num) 84 | num_map_round_after_(num, num_, opts, f, memo) 85 | } 86 | return "" 87 | } 88 | 89 | function num_map_round_off_init() { 90 | num_function_init(\ 91 | "num_map_round_off round_off round_towards_zero truncate", 0, 92 | "Map using round off, a.k.a. round towards zero, truncate.", 93 | "https://en.wikipedia.org/wiki/Rounding") 94 | } 95 | 96 | # Alias 97 | function num_map_truncate(arr) { 98 | return num_map_round_off(arr) 99 | } 100 | 101 | # Alias 102 | function num_map_truncate_(num, num_, opts) { 103 | return num_map_round_off_(num, num_, opts) 104 | } 105 | 106 | ## 107 | # 108 | # Map: round up, a.k.a. round towards positive infinity, ceiling. 109 | # 110 | # Example: 111 | # 112 | # num_map_round_up(-1.9 1.9) => -1 2 113 | # 114 | ## 115 | 116 | function num_map_round_up(arr) { 117 | for (i in arr) arr[i] = num_round_up(arr[i]) 118 | } 119 | 120 | function num_map_round_up_(num, num_, opts, f, memo) { 121 | if (num_["integer"] == TRUE) return "" 122 | f = "num_map_round_up" 123 | if (num_[f] != TRUE) { 124 | num_map_round_before_(num, num_, opts, f, memo) 125 | num_map_round_up(num) 126 | num_map_round_after_(num, num_, opts, f, memo) 127 | } 128 | return "" 129 | } 130 | 131 | function num_map_round_up_init() { 132 | num_function_init(\ 133 | "num_map_round_up round_up ceiling", 0, 134 | "Map using round up, a.k.a. round towards positive infinity, ceiling.", 135 | "https://en.wikipedia.org/wiki/Rounding") 136 | } 137 | 138 | # Alias 139 | function num_map_ceiling(arr) { 140 | return num_map_round_up(arr) 141 | } 142 | 143 | # Alias 144 | function num_map_ceiling_(num, num_, opts) { 145 | return num_map_round_up_(num, num_, opts) 146 | } 147 | 148 | ## 149 | # 150 | # Map: round down, a.k.a. round towards negative infinity, floor. 151 | # 152 | # Example: 153 | # 154 | # num_map_round_down(-1.9 1.9) => -2 1 155 | # 156 | ## 157 | 158 | function num_map_round_down(arr) { 159 | for (i in arr) arr[i] = num_round_down(arr[i]) 160 | } 161 | 162 | function num_map_round_down_(num, num_, opts, f, memo) { 163 | if (num_["integer"] == TRUE) return "" 164 | f = "num_map_round_down" 165 | if (num_[f] != TRUE) { 166 | num_map_round_before_(num, num_, opts, f, memo) 167 | num_map_round_down(num) 168 | num_map_round_after_(num, num_, opts, f, memo) 169 | } 170 | return "" 171 | } 172 | 173 | function num_map_round_down_init() { 174 | num_function_init(\ 175 | "num_map_round_down round_down floor", 0, 176 | "Map using round down, a.k.a. round towards negative infinity, floor.", 177 | "https://en.wikipedia.org/wiki/Rounding") 178 | } 179 | 180 | # Alias 181 | function num_map_floor(arr) { 182 | return num_map_round_down(arr) 183 | } 184 | 185 | # Alias 186 | function num_map_floor_(num, num_, opts) { 187 | return num_map_round_down_(num, num_, opts) 188 | } 189 | 190 | ## 191 | # 192 | # Map round after helper: call this function only from within 193 | # each of the rounding functions, before the work begins. 194 | # This function saves as much metadata as possible. 195 | # 196 | ## 197 | 198 | function num_map_round_before_(num, num_, opts, f, memo) { 199 | for (k in NUM_MAP_ROUND_MEMO) if (k in num_) memo[k] = num_[k] 200 | } 201 | 202 | ## 203 | # 204 | # Map round after helper: call this function only from within 205 | # each of the rounding functions, after the work ends. 206 | # This function restores as much metadata as possible. 207 | # 208 | ## 209 | 210 | function num_map_round_after_(num, num_, opts, f, memo) { 211 | split("",num_) 212 | for (k in NUM_MAP_ROUND_MEMO) if (k in memo) num_[k] = memo[k] 213 | num_[f] = TRUE 214 | num_["integer"] = TRUE 215 | } 216 | -------------------------------------------------------------------------------- /src/num-map-sign.awk: -------------------------------------------------------------------------------- 1 | ############################################################################ 2 | # 3 | # num-map-sign.awk 4 | # 5 | ## 6 | 7 | ## 8 | # 9 | # Map sign. 10 | # 11 | # Example: 12 | # 13 | # num_map_sign_(-8 0 8) => -1 0 1 14 | # 15 | ## 16 | 17 | function num_map_sign(arr) { 18 | for (i in arr) arr[i] = num_sign(arr[i]) 19 | } 20 | 21 | function num_map_sign_(num, num_, opts, f, i, memo) { 22 | f = "num_map_sign" 23 | if (num_[f] != TRUE) { 24 | map_before_(num, num_, opts, memo) 25 | num_map_sign(num) 26 | map_after_(num, num_, opts, memo) 27 | num_[f] = TRUE 28 | } 29 | return "" 30 | } 31 | 32 | function num_map_sign_init() { 33 | num_function_init(\ 34 | "num_map_sign sign sgn signum", 0, 35 | "Map using sign.", 36 | "https://en.wikipedia.org/wiki/Sign_function") 37 | } 38 | -------------------------------------------------------------------------------- /src/num-map.awk: -------------------------------------------------------------------------------- 1 | ############################################################################ 2 | # 3 | # num-map.awk 4 | # 5 | # Map helpers. 6 | # 7 | # Call `map_before` to remember the number of items. 8 | # 9 | # Call `map_after` to invalidate all metadata then 10 | # restore the number of items. 11 | # 12 | ## 13 | 14 | function map_before_(num, num_, opts, memo) { 15 | memo["n"] = num_["n"] 16 | } 17 | 18 | function map_after_(num, num_, opts, memo) { 19 | split("",num_) 20 | num_["n"] = memo["n"] 21 | } 22 | -------------------------------------------------------------------------------- /src/num-mean-absolute-deviation.awk: -------------------------------------------------------------------------------- 1 | ############################################################################ 2 | # 3 | # num-mean-absolute-deviation.awk 4 | # 5 | ## 6 | 7 | ## 8 | # 9 | # Mean absolute deviation. 10 | # 11 | # The average distance between each value and the mean. 12 | # 13 | # Example: 14 | # 15 | # num_mean_absolute_deviation(1 2 4) => 1.11111 16 | ## 17 | 18 | function num_mean_absolute_deviation(arr, _mean, _n, x) { 19 | _mean = num_mean(arr) 20 | _n = num_n(arr) 21 | for (i in arr) x += num_absolute_value(arr[i] - _mean) 22 | return x / _n 23 | } 24 | 25 | function num_mean_absolute_deviation_(num, num_, opts, f, _n, _mean, i, x) { 26 | f = "num_mean_absolute_deviation" 27 | if (!(f in num_)) { 28 | _n = num_n_(num, num_, opts) 29 | _mean = num_mean_(num, num_, opts) 30 | for (i in num) x += num_absolute_value(num[i] - _mean) 31 | num_[f] = x / _n 32 | } 33 | return num_[f] 34 | } 35 | 36 | function num_mean_absolute_deviation_init() { 37 | num_function_init(\ 38 | "num_mean_absolute_deviation mean_absolute_deviation mad", 0, 39 | "Get the average distance between each value and the mean.", 40 | "https://en.wikipedia.org/wiki/Average_absolute_deviation") 41 | } 42 | 43 | # Alias 44 | function num_mad(arr) { return num_mean_absolute_deviation(arr) } 45 | function num_mad_(num, num_, opts) { return num_mean_absolute_deviation_(num, num_, opts) } 46 | -------------------------------------------------------------------------------- /src/num-mean.awk: -------------------------------------------------------------------------------- 1 | ############################################################################ 2 | # 3 | # num-mean.awk 4 | # 5 | ## 6 | 7 | ## 8 | # 9 | # Mean, a.k.a. arithmetic mean, average. 10 | # 11 | # Example: 12 | # 13 | # num_mean(1 2 4) => 2.33333 14 | # 15 | ## 16 | 17 | function num_mean(arr) { 18 | return num_sum(arr) / num_n(arr) 19 | } 20 | 21 | function num_mean_(num, num_, opts, f, _n, _min, _max, _sum) { 22 | f = "num_mean" 23 | if (!(f in num_)) { 24 | if (num_["linear"]) { 25 | _n = num_n_(num, num_, opts) 26 | _min = num_min_(num, num_, opts) 27 | _max = num_max_(num, num_, opts) 28 | num_[f] = _min + (_max - _min) / _n 29 | } else { 30 | _n = num_n_(num, num_, opts) 31 | _sum = num_sum_(num, num_, opts) 32 | num_[f] = _sum / _n 33 | } 34 | } 35 | return num_[f] 36 | } 37 | 38 | function num_mean_init() { 39 | num_function_init(\ 40 | "num_mean mean average avg", 0, 41 | "Get the mean, a.k.a artihmetic mean, average.", 42 | "https://en.wikipedia.org/wiki/Mean") 43 | } 44 | -------------------------------------------------------------------------------- /src/num-meanest.awk: -------------------------------------------------------------------------------- 1 | ############################################################################ 2 | # 3 | # num-meanest.awk 4 | # 5 | ## 6 | 7 | ## 8 | # 9 | # Meanest, i.e. the value closest to the mean. 10 | # 11 | # Example: 12 | # 13 | # num_meanest(1 2 4) => 2 14 | # 15 | ## 16 | 17 | function num_meanest(arr) { 18 | return num_arr_closest_value(arr, num_mean(arr)) 19 | } 20 | 21 | function num_meanest_(num, num_, opts, f, _n, _mean, i, x) { 22 | f = "num_meanest" 23 | if (!(f in num_)) num_[f] = num_arr_closest_value(num, num_mean_(num, num_, opts)) 24 | return num_[f] 25 | } 26 | 27 | function num_meanest_init() { 28 | num_function_init(\ 29 | "num_meanest meanest", 0, 30 | "Get the value that is closest to the mean.", 31 | "https://en.wikipedia.org/wiki/Mean") 32 | } 33 | -------------------------------------------------------------------------------- /src/num-median.awk: -------------------------------------------------------------------------------- 1 | ############################################################################ 2 | # 3 | # num-median.awk 4 | # 5 | ## 6 | 7 | ## 8 | # 9 | # Median of an array slice. 10 | # 11 | # Example: 12 | # 13 | # num_median_slice((1 2 4), 1, 3) => 2 14 | # num_median_slice((1 2 4 9 9), 1, 3) => 3 15 | # 16 | ## 17 | 18 | function num_median_of_slice(arr, start, stop, _n, i) { 19 | _n = 1 + stop - start 20 | if (_n % 2) { 21 | i = (start - 1) + ((_n + 1) / 2) 22 | return arr[i] 23 | } else { 24 | i = (start - 1) + (_n / 2) 25 | return (arr[i] + arr[i+1]) / 2 26 | } 27 | } 28 | 29 | ## 30 | # 31 | # Median. 32 | # 33 | # Example: 34 | # 35 | # num_median(1 2 4) => 2 36 | # num_median(1 2 4 99) => 3 37 | # 38 | # Requirement: the array is sorted. 39 | # 40 | ## 41 | 42 | function num_median(arr, _n, i) { 43 | _n = num_n(arr) 44 | if (_n % 2) { 45 | i = (_n + 1) / 2 46 | return arr[i] 47 | } else { 48 | i = _n / 2 49 | return (arr[i] + arr[i+1]) / 2.0 50 | } 51 | } 52 | 53 | function num_median_(num, num_, opts, f, i, _n) { 54 | f = "num_median" 55 | if (!(f in num_)) { 56 | _n = num_n_(num, num_, opts) 57 | num_sort_ascending_(num, num_, opts) 58 | if (_n % 2) { 59 | i = (_n + 1) / 2 60 | num_[f] = num_["num_median_low"] = num_["num_median_high"] = num[i] 61 | } else { 62 | i = _n / 2 63 | num_["num_median_low"] = num[i] 64 | num_["num_median_high"] = num[i+1] 65 | num_[f] = (num[i] + num[i+1]) / 2.0 66 | } 67 | } 68 | return num_[f] 69 | } 70 | 71 | function num_median_init() { 72 | num_function_init(\ 73 | "num_median median med", 0, 74 | "Get the median.", 75 | "https://en.wikipedia.org/wiki/Median") 76 | } 77 | 78 | ## 79 | # 80 | # Median low: get the lesser median. 81 | # 82 | # Example: 83 | # 84 | # num_median_low(1 2 4) => 2 85 | # num_median_low(1 2 4 99) => 2 86 | # 87 | function num_median_low(arr, _n, i) { 88 | _n = num_n(arr) 89 | if (_n % 2) { 90 | i = (_n + 1) / 2 91 | } else { 92 | i = _n / 2 93 | } 94 | return arr[i] 95 | } 96 | 97 | function num_median_low_(num, num_, opts, f, _n) { 98 | f = "num_median_low" 99 | if (!(f in num_)) { 100 | num_median_(num, num_, opts) # n.b. median sets median_low 101 | } 102 | return num_[f] 103 | } 104 | 105 | function num_median_low_init() { 106 | num_function_init(\ 107 | "num_median_low median_low med_low", 0, 108 | "Get the median that is lower a.k.a. lesser.", 109 | "https://en.wikipedia.org/wiki/Median") 110 | } 111 | 112 | ## 113 | # 114 | # Median high: get the greater median. 115 | # 116 | # Example: 117 | # 118 | # num_median_high(1 2 4) => 2 119 | # num_median_high(1 2 4 99) => 4 120 | # 121 | ## 122 | 123 | function num_median_high(arr, _n, i) { 124 | _n = num_n(arr) 125 | if (_n % 2) { 126 | i = (_n + 1) / 2 127 | } else { 128 | i = (_n / 2) + 1 129 | } 130 | return arr[i] 131 | } 132 | 133 | function num_median_high_(num, num_, opts, f, _n) { 134 | f = "num_median_high" 135 | if (!(f in num_)) { 136 | num_median_(num, num_, opts) # n.b. median sets median_high 137 | } 138 | return num_[f] 139 | } 140 | 141 | function num_median_high_init() { 142 | num_function_init(\ 143 | "num_median_high median_high med_high", 0, 144 | "Get the median that is higher a.k.a. greater.", 145 | "https://en.wikipedia.org/wiki/Median") 146 | } 147 | -------------------------------------------------------------------------------- /src/num-mode.awk: -------------------------------------------------------------------------------- 1 | ############################################################################ 2 | # 3 | # num-mode.awk 4 | # 5 | ## 6 | 7 | ## 8 | # 9 | # Modes: get the modes, which may be a number, or list, or UNDEF. 10 | # 11 | # The modes are: 12 | # 13 | # * The value that appears most often in a set of data. 14 | # * If mutlipe values appear as often, there are multiple modes. 15 | # * If each value occurs only once, then there are no modes. 16 | # 17 | # Examples: 18 | # 19 | # 1 2 2 3 => 2 20 | # 1 1 2 3 3 => 1 3 21 | # 1 2 3 => UNDEF 22 | # 23 | # Output the `modes` array. 24 | # Return the `modes` array length. 25 | # 26 | # Examples: 27 | # 28 | # num_modes(1 2 3, modes) => 0, modes == [] 29 | # num_modes(1 2 2 3, modes) => 1, modes == [2] 30 | # num_modes(1 1 2 3 3, modes) => 2, modes == [1, 3] 31 | # 32 | ## 33 | 34 | function num_modes(arr, modes, modes_i, i, n, seen, max) { 35 | for (i in arr) { 36 | # Optimization: use one scan 37 | n = ++seen[arr[i]] 38 | if (max == "" || max < n) max = n 39 | } 40 | split("", out); out_i = 0 41 | if (max > 1) { 42 | for (i in seen) { 43 | if (seen[i] == max) { 44 | out[++out_i] = i 45 | } 46 | } 47 | } 48 | return out_i 49 | } 50 | 51 | function num_modes_(num, num_, opts, modes, f, modes_i) { 52 | f = "num_modes" 53 | if (!(f in num_)) { 54 | if (num_["unique"]) { 55 | num_[f] = num_["num_mode_min"] = num_["num_mode_max"] = UNDEF 56 | } else { 57 | # TODO: optimize if we know the array is sorted 58 | num_[f] = modes_i = num_modes(num, modes) 59 | if (modes_i == 0) { 60 | num_["unique"] = TRUE 61 | num_["num_mode_min"] = num_["num_mode_max"] = UNDEF 62 | } else { 63 | num_["unique"] = FALSE 64 | } 65 | num_[f] = modes_i 66 | } 67 | } 68 | return num_[f] 69 | } 70 | 71 | function num_modes_init() { 72 | num_function_init(\ 73 | "num_modes modes", 0, 74 | "Get the modes, which is a list.", 75 | "https://en.wikipedia.org/wiki/Mode_(statistics)") 76 | } 77 | 78 | ## 79 | # 80 | # Mode min: get the minimum mode, if any, or UNDEF. 81 | # 82 | # TODO: IMPLEMENT 83 | # 84 | # Examples: 85 | # 86 | # num_mode_min(1 2 3) => UNDEF 87 | # num_mode_min(1 2 2 3) => 2 88 | # num_mode_min(1 1 2 4 4) => 1 89 | # 90 | ## 91 | 92 | function num_mode_min(arr) { 93 | return TODO 94 | } 95 | 96 | function num_mode_min_(num, num_, opts, f) { 97 | f = "num_mode_min" 98 | if (!(f in num_)) { 99 | num_[f] = TODO 100 | } 101 | return num_[f] 102 | } 103 | 104 | function num_mode_min_init() { 105 | num_function_init(\ 106 | "num_mode_min mode_min", 0, 107 | "Get the minimum mode, if any, or UNDEF.", 108 | "https://en.wikipedia.org/wiki/Mode_(statistics)") 109 | } 110 | 111 | ## 112 | # 113 | # Mode max: get the maximum mode, if any, or UNDEF. 114 | # 115 | # TODO: IMPLEMENT 116 | # 117 | # Examples: 118 | # 119 | # num_mode_max(1 2 3) => UNDEF 120 | # num_mode_max(1 2 2 3) => 2 121 | # num_mode_max(1 1 2 4 4) => 4 122 | # 123 | ## 124 | 125 | function num_mode_max(arr) { 126 | return TODO 127 | } 128 | 129 | function num_mode_max_(num, num_, opts, f) { 130 | f = "num_mode_max" 131 | if (!(f in num_)) { 132 | num_[f] = TODO 133 | } 134 | return num_[f] 135 | } 136 | 137 | function num_mode_max_init() { 138 | num_function_init(\ 139 | "num_mode_max mode_max", 0, 140 | "Get the maximum mode, if any, or UNDEF.", 141 | "https://en.wikipedia.org/wiki/Mode_(statistics)") 142 | } 143 | -------------------------------------------------------------------------------- /src/num-normalize.awk: -------------------------------------------------------------------------------- 1 | ############################################################################ 2 | # 3 | # num-map-normalize.awk 4 | # 5 | ## 6 | 7 | ## 8 | # 9 | # Map: normalize each value to be 0 to 1. 10 | # 11 | # Example: 12 | # 13 | # num_map_normalize(1 2 4) => 0 0.33333 1 14 | # 15 | ## 16 | 17 | function num_map_normalize_with_min_max(arr, min_old, max_old, min_new, max_new) { 18 | multiply = (max_new - min_new) / (max_old - min_old) 19 | add = min_new - (multiply * min_old) 20 | for (i in arr) arr[i] = arr[i] * multiply + add 21 | } 22 | 23 | function num_map_normalize(arr) { 24 | return num_map_normalize_with_min_max(arr, num_min(arr), num_max(arr), 0, 1) 25 | } 26 | 27 | function num_map_normalize_(num, num_, opts, f, min_old, max_old, min_new, max_new, multiply, add) { 28 | f = "num_map_normalize" 29 | if (num_[f] != TRUE) { 30 | map_before_(num, num_, opts, memo) 31 | x = num_map_normalize_with_min_max(num, num_min_(num, num_, opts), num_max_(num, num_, opts), 0, 1) 32 | map_after_(num, num_, opts, memo) 33 | num_[f] = TRUE 34 | } 35 | return "" 36 | } 37 | 38 | function num_map_normalize_init() { 39 | num_function_init(\ 40 | "num_map_normalize normalize norm", 0, 41 | "Map using normalize.", 42 | "https://wikipedia.org/wiki/Normalization_(statistics)") 43 | } 44 | -------------------------------------------------------------------------------- /src/num-out-err.awk: -------------------------------------------------------------------------------- 1 | ############################################################################ 2 | # 3 | # num-out-err.awk 4 | # 5 | ## 6 | 7 | ## 8 | # 9 | # Print a message to stdout. 10 | # 11 | # Example: 12 | # 13 | # num_out("hello") 14 | # => Print "hello" to STDOUT 15 | # 16 | function num_out(msg) { 17 | print msg 18 | } 19 | 20 | ## 21 | # 22 | # Print a message to stderr. 23 | # 24 | # Example: 25 | # 26 | # num_err("hello") 27 | # => Print "hello" to STDERR 28 | # 29 | # This is purposefully POSIX compatible. 30 | # 31 | function num_err(msg) { 32 | print msg | "cat 1>&2" 33 | } 34 | -------------------------------------------------------------------------------- /src/num-print.awk: -------------------------------------------------------------------------------- 1 | ############################################################################ 2 | # 3 | # num-print.awk 4 | # 5 | ## 6 | 7 | ## 8 | # 9 | # Print a record. 10 | # 11 | # This is the core output function, and the only one that should ever 12 | # print any normal result to the screen, during any normal operation. 13 | # 14 | ## 15 | 16 | function num_print_record(num, num_, opts, s, i) { 17 | s = num_function_names_to_s(global_num, global_num_, global_opts, global_word_argv) 18 | if (global_num_scope_n > 1) printf ORS 19 | if (s != "") { 20 | printf s 21 | } else { 22 | num_print_record_fields(global_num, global_num_, global_opts) 23 | } 24 | } 25 | 26 | ## 27 | # 28 | # Print all record fields. 29 | # 30 | # This is the fallback if the record has no output so far. 31 | # A typical example would be for a sort, or filter, or map. 32 | # 33 | ## 34 | 35 | function num_print_record_fields(num, num_, opts) { 36 | for (i = 1; i <= num_["n"]; i++) { 37 | if (i > 1) printf OFS 38 | printf(OFMT, num[i]) 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/num-product.awk: -------------------------------------------------------------------------------- 1 | ############################################################################ 2 | # 3 | # num-product.awk 4 | # 5 | ## 6 | 7 | ## 8 | # 9 | # Product. 10 | # 11 | # Example: 12 | # 13 | # num_product(1 2 4) => 8 14 | # 15 | ## 16 | 17 | function num_product(arr, x) { 18 | x = 1 19 | for (i in arr) x *= arr[i] 20 | return x 21 | } 22 | 23 | function num_product_(num, num_, opts, f) { 24 | f = "num_product" 25 | if (!(f in num_)) num_[f] = num_product(num) 26 | return num_[f] 27 | } 28 | 29 | function num_product_init() { 30 | num_function_init(\ 31 | "num_product product", 0, 32 | "Get the product.", 33 | "https://wikipedia.org/wiki/Product_(mathematics)") 34 | } 35 | -------------------------------------------------------------------------------- /src/num-queue.awk: -------------------------------------------------------------------------------- 1 | ############################################################################ 2 | # 3 | # num-queue.awk 4 | # 5 | ## 6 | 7 | ## 8 | # 9 | # Enqueue one item to an array queue. 10 | # 11 | # Example: 12 | # 13 | # arr = 1 2 14 | # num_enqueue(arr, 4) 15 | # => 4 16 | # => arr == 1 2 4 17 | # 18 | # Return the item for chainability. 19 | # 20 | function num_enqueue(arr, item, i) { 21 | i = num_arr_length(arr) + 1 22 | arr[i] = item 23 | return item 24 | } 25 | 26 | ## 27 | # 28 | # Dequeue one item from an array queue. 29 | # 30 | # Example: 31 | # 32 | # arr = 1 2 4 33 | # num_dequeue(arr) 34 | # => 1 35 | # => arr == 2 4 36 | # 37 | # Return the item. 38 | # 39 | function num_dequeue(arr, item, i) { 40 | return num_shift(arr) 41 | } 42 | -------------------------------------------------------------------------------- /src/num-quicksort.awk: -------------------------------------------------------------------------------- 1 | ############################################################################ 2 | # 3 | # num-quicksort.awk 4 | # 5 | ## 6 | 7 | function num_quicksort_awk_init() { 8 | NUM_QUICKSORT_INSERTION_SORT_THRESHOLD = 12 9 | } 10 | 11 | ## 12 | # 13 | # Quicksort. 14 | # 15 | # Quicksort selects a pivot and divides the data into values above and 16 | # below the pivot. Sorting then recurses on these sub-lists. 17 | # 18 | # From http://awk.info/?doc/quicksort.html 19 | # 20 | # TODO: research implementing the pivot by using Tukeys ninther, 21 | # which is a median of medians, and may be a faster heuristic. 22 | # See http://www.johndcook.com/blog/2009/06/23/tukey-median-ninther/ 23 | # 24 | # TODO: research implementing the small size sort using Shell sort, 25 | # which is similar to insertion sort yet better for typical data. 26 | # See https://en.wikipedia.org/wiki/Shellsort 27 | # 28 | # TODO: research upgrading from single pivot to dual pivot. 29 | # http://stackoverflow.com/questions/20917617/whats-the-difference-of-dual-pivot-quick-sort-and-quick-sort 30 | # http://stackoverflow.com/questions/32001841/how-to-implement-dual-pivot-quicksort-in-python 31 | # http://hg.openjdk.java.net/jdk8/jdk8/jdk/file/tip/src/share/classes/java/util/DualPivotQuicksort.java 32 | # 33 | ## 34 | 35 | function num_quicksort(A) { 36 | num_quicksort_slice(A, 1, num_arr_length(A)) 37 | } 38 | 39 | function num_quicksort_slice(A, lo, hi, pivot_index) { 40 | if (lo >= hi) return 41 | if (hi - lo < NUM_QUICKSORT_INSERTION_SORT_THRESHOLD) { 42 | num_insertion_sort_slice(A, lo, hi) 43 | return 44 | } 45 | pivot_index = num_quicksort_pivot_index_via_median_of_three_sort_slice(A, lo, hi) 46 | pivot_index = num_partition_slice(A, lo, hi, pivot_index) 47 | num_quicksort_slice(A, lo, pivot_index - 1) 48 | num_quicksort_slice(A, pivot_index + 1, hi) 49 | } 50 | 51 | ## 52 | # 53 | # Partition an array slice using a given pivot index. 54 | # 55 | # Return the new pivot index. 56 | # 57 | ## 58 | 59 | function num_partition_slice(A, lo, hi, pivot_index, left, right, pivot_value, t) { 60 | if (lo >= hi) return lo 61 | left = lo 62 | right = hi 63 | pivot_value = A[pivot_index] 64 | num_arr_swap(A, left, pivot_index) 65 | while (left < right) { 66 | while (left <= hi && A[left] <= pivot_value) left++ 67 | while (right >= lo && A[right] > pivot_value) right-- 68 | if (left < right) num_arr_swap(A, left, right) 69 | } 70 | pivot_index = right 71 | num_arr_swap(A, lo, pivot_index) 72 | return pivot_index 73 | } 74 | 75 | ## 76 | # 77 | # Choose a quicksort pivot index by using a random number. 78 | # This is a naive implemenation and is here for benchmarking. 79 | # Typically you will never need this function for real-world data. 80 | # 81 | ## 82 | 83 | function num_quicksort_pivot_index_via_rand(A, lo, hi) { 84 | return lo + int((hi - lo + 1) * rand()) 85 | } 86 | 87 | ## 88 | # 89 | # Choose a quicksort pivot index by using the "median of three" heuristic 90 | # with a swap sort of the three items for efficiency on the next pivot. 91 | # 92 | # Compared to picking the pivot randomly, the median of three heuristic: 93 | # 94 | # * Ensures that a common case of fully sorted data remains optimal. 95 | # * Is more difficult for an attacker to manipulate into the worst case. 96 | # * Is often faster than a PRNG, which is often relatively slow. 97 | # 98 | # The median of three looks at the first, middle and last elements of 99 | # the array, and choose the median of those as the pivot. 100 | # 101 | # To get the "full effect" of the median of three, it is also important 102 | # to sort those three items, not just use the median as the pivot -- 103 | # this does not affect what is chosen as the pivot in the current 104 | # iteration, but can/will affect what is used as the pivot in the next 105 | # recursive call, which helps to limit the bad behavior for a few 106 | # initial orderings (one that turns out to be particularly bad in many 107 | # cases is an array that is sorted, except for having the smallest 108 | # element at the high end of the array (or largest element at the low 109 | # end)). 110 | # 111 | # Thanks to http://stackoverflow.com/questions/7559608/median-of-three-values-strategy 112 | # 113 | # To calculate the midpoint, we prefer (lo+(hi−lo)/2) instead of the naive 114 | # simpler ((hi+lo)/2) because the former does not risk integer overflow. 115 | # 116 | # Return the pivot index. 117 | # 118 | ## 119 | 120 | function num_quicksort_pivot_index_via_median_of_three_sort(A) { 121 | num_quicksort_pivot_index_via_median_of_three_sort_slice(A, 1, num_arr_length(A)) 122 | } 123 | 124 | function num_quicksort_pivot_index_via_median_of_three_sort_slice(A, lo, hi, mid) { 125 | if (lo == hi) return lo 126 | mid = lo + int((hi - lo) / 2) 127 | if (A[hi] < A[lo]) num_arr_swap(A, lo, hi) 128 | if (A[mid] < A[lo]) num_arr_swap(A, mid, lo) 129 | if (A[hi] < A[mid]) num_arr_swap(A, hi, mid) 130 | return mid 131 | } 132 | -------------------------------------------------------------------------------- /src/num-round.awk: -------------------------------------------------------------------------------- 1 | ############################################################################ 2 | # 3 | # num-round.awk 4 | # 5 | # We provide four kinds of rounding: 6 | # 7 | # * round a.k.a. nint. 8 | # * round off a.k.a. truncate. 9 | # * round up a.k.a. ceiling. 10 | # * round down a.k.a. floor. 11 | # 12 | ## 13 | 14 | ## 15 | # 16 | # Round to the nearest integer, a.k.a. nint(). 17 | # 18 | # Examples: 19 | # 20 | # num_round(1.9) => 2 21 | # num_round(-1.9) => -2 22 | # 23 | # num_nint(1.9) => 2 24 | # num_nint(-1.9) => -2 25 | # 26 | ## 27 | 28 | function num_round(x) { 29 | return (x >= 0) ? int(x + 0.5) : int(x - 0.5) 30 | } 31 | 32 | # Alias 33 | function num_nint(x) { 34 | return num_round(x) 35 | } 36 | 37 | ## 38 | # 39 | # Round off the fractional part, a.k.a. truncate(). 40 | # 41 | # Examples: 42 | # 43 | # num_round_off(1.9) => 1 44 | # num_round_off(-1.9) => -1 45 | # 46 | # num_truncate(1.9) => 1 47 | # num_truncate(-1.9) => -1 48 | # 49 | ## 50 | 51 | function num_round_off(x) { 52 | return int(x) 53 | } 54 | 55 | # Alias 56 | function num_truncate(x) { 57 | return num_round_off(x) 58 | } 59 | 60 | ## 61 | # 62 | # Round up, a.k.a. ceiling(). 63 | # 64 | # Examples: 65 | # 66 | # num_round_up(1.9) => 2 67 | # num_round_up(-1.9) => -1 68 | # 69 | # num_ceiling(1.9) => 2 70 | # num_ceiling(-1.9) => -1 71 | # 72 | function num_round_up(x, y) { 73 | y = int(x) 74 | return (x == y) ? x : (x >= 0) ? y + 1 : y 75 | } 76 | 77 | # Alias 78 | function num_ceiling(x) { 79 | return num_round_up(x) 80 | } 81 | 82 | ## 83 | # 84 | # Round down, a.k.a. floor(). 85 | # 86 | # Examples: 87 | # 88 | # num_round_down(1.9) => 1 89 | # num_round_down(-1.9) => -2 90 | # 91 | # num_floor(1.9) => 1 92 | # num_floor(-1.9) => -2 93 | # 94 | ## 95 | 96 | function num_round_down(x, y) { 97 | y = int(x) 98 | return (x == y) ? x : (x >= 0) ? y : y - 1 99 | } 100 | 101 | # Alias 102 | function num_floor(x) { 103 | num_round_down(x) 104 | } 105 | -------------------------------------------------------------------------------- /src/num-scope.awk: -------------------------------------------------------------------------------- 1 | ############################################################################ 2 | # 3 | # num-scope.awk 4 | # 5 | ## 6 | 7 | ## 8 | # 9 | # Start receiving input. 10 | # 11 | # Ready the global number array for new input and new metadata. 12 | # 13 | ## 14 | 15 | function scope_start() { 16 | global_num_scope_n++ 17 | global_num_scope_output_n = 0 18 | global_num_n = 0 19 | split("", global_num) 20 | split("", global_num_) 21 | } 22 | 23 | ## 24 | # 25 | # Stop receiving input. 26 | # 27 | # Set any work in progress here. 28 | # 29 | ## 30 | 31 | function scope_stop() { 32 | global_num_["n"] = global_num_n 33 | num_print_record(global_num, global_num_, global_opts) 34 | } 35 | -------------------------------------------------------------------------------- /src/num-shift.awk: -------------------------------------------------------------------------------- 1 | ############################################################################ 2 | # 3 | # num-shift.awk 4 | # 5 | ## 6 | 7 | ## 8 | # 9 | # Shift one item from the head of a list. 10 | # 11 | # Example: 12 | # 13 | # arr = 1 2 4 14 | # num_shift(arr, 4) 15 | # => 4 16 | # => arr == 1 2 17 | # 18 | # Return the item. 19 | # 20 | function num_shift(arr, item, i) { 21 | len = num_arr_length(arr) 22 | item = arr[1] 23 | for (i = 1; i < len; i++) arr[i] = arr[i+1] 24 | delete arr[len] 25 | return item 26 | } 27 | 28 | ## 29 | # 30 | # Unshift one item onto the head of a list. 31 | # 32 | # Example: 33 | # 34 | # arr = 1 2 35 | # num_unshift(arr, 4) 36 | # => 4 37 | # => arr == 4 1 2 38 | # 39 | # Return the item for chainability. 40 | # 41 | function num_unshift(arr, item, i) { 42 | len = num_arr_length(arr) 43 | for (i = 1; i < len; i++) arr[i+1] = arr[i] 44 | arr[1] = item 45 | return item 46 | } 47 | -------------------------------------------------------------------------------- /src/num-sign.awk: -------------------------------------------------------------------------------- 1 | ############################################################################ 2 | # 3 | # num-sign.awk 4 | # 5 | ## 6 | 7 | ## 8 | # 9 | # Return the sign of the value, either 1, -1, or 0. 10 | # 11 | # Examples: 12 | # 13 | # sign(8) => 1 14 | # sign(-8) => -1 15 | # sign(0) => 0 16 | # 17 | ## 18 | 19 | function num_sign(x) { 20 | return (x > 0) - (x < 0) 21 | } 22 | -------------------------------------------------------------------------------- /src/num-skewness.awk: -------------------------------------------------------------------------------- 1 | ############################################################################ 2 | # 3 | # num-skewness.awk 4 | # 5 | ## 6 | 7 | # Alias 8 | function num_skewness(arr) { num_sample_skewness(arr) } 9 | function num_skewness_(num, num_, opts) { num_sample_skewness_(num, num_, opts) } 10 | 11 | # Alias 12 | function num_skew(arr) { num_sample_skewness(arr) } 13 | function num_skew_(num, num_, opts) { num_sample_skewness_(num, num_, opts) } 14 | 15 | ## 16 | # 17 | # Sample skewness 18 | # 19 | # Example: 20 | # 21 | # num_sample_skewness(1 2 4) => 1.11111 22 | # 23 | # A.k.a. population third moment about the mean. 24 | # 25 | # Calculation: 26 | # 27 | # * Sum each value deviation from the mean cubed. 28 | # * Divide by the number of items - 1. 29 | # 30 | ## 31 | 32 | function num_sample_skewness(arr) { 33 | return num_sum_of_mean_deviation_exp(arr, num_mean(arr), 3) / (num_n(arr) - 1) 34 | } 35 | 36 | function num_sample_skewness_(num, num_, opts, f) { 37 | f = "num_sample_skewness" 38 | if (!(f in num_)) num_[f] = num_sum_of_mean_deviation_exp_(num, num_, opts, num_mean_(num, num_, opts), 3) / (num_n_(num, num_, opts) - 1) 39 | return num_[f] 40 | } 41 | 42 | function num_sample_skewness_init() { 43 | num_function_init(\ 44 | "num_sample_skewness sample_skewness s_skew skewness skew sample_third_moment_about_the_mean s_third_moment_about_the_mean s_3_m_a_t_m third_moment_about_the_mean 3_m_a_t_m", 0, 45 | "Get the sample skewness, a.k.a. sample third moment about the mean.", 46 | "https://en.wikipedia.org/wiki/Skewness") 47 | } 48 | 49 | # Alias 50 | function num_sskew(arr) { num_sample_skewness(arr) } 51 | function num_sskew_(num, num_, opts) { num_sample_skewness_(num, num_, opts) } 52 | 53 | ## 54 | # 55 | # Population skewness 56 | # 57 | # Example: 58 | # 59 | # num_population_skewness(1 2 4) => 0.740741 60 | # 61 | # A.k.a. population third moment about the mean. 62 | # 63 | # If skewness is greater than zero, the distribution is positively skewed. 64 | # If it is less than zero, it is negatively skewed. 65 | # Zero means it is symmetric. 66 | # 67 | # Calculation: 68 | # 69 | # * Sum each value deviation from the mean cubed. 70 | # * Divide by the number of items. 71 | # 72 | ## 73 | 74 | function num_population_skewness(arr) { 75 | return num_sum_of_mean_deviation_exp(arr, num_mean(arr), 3) / num_n(arr) 76 | } 77 | 78 | function num_population_skewness_(num, num_, opts, f) { 79 | f = "num_population_skewness" 80 | if (!(f in num_)) num_[f] = num_sum_of_mean_deviation_exp_(num, num_, opts, num_mean_(num, num_, opt), 3) / num_n_(num, num_, opts) 81 | return num_[f] 82 | } 83 | 84 | function num_population_skewness_init() { 85 | num_function_init(\ 86 | "num_population_skewness population_skewness p_skew population_third_moment_about_the_mean p_third_moment_about_the_mean p_3_m_a_t_m", 0, 87 | "Get the population skewness, a.k.a. population third moment about the mean.", 88 | "https://en.wikipedia.org/wiki/Skewness") 89 | } 90 | 91 | # Alias 92 | function num_pskew(arr) { num_population_skewness(arr) } 93 | function num_pskew_(num, num_, opts) { num_population_skewness_(num, num_, opts) } 94 | -------------------------------------------------------------------------------- /src/num-stack.awk: -------------------------------------------------------------------------------- 1 | ############################################################################ 2 | # 3 | # num-stack.awk 4 | # 5 | ## 6 | 7 | ## 8 | # 9 | # Push one item on an array list. 10 | # 11 | # Example: 12 | # 13 | # arr = 1 2 14 | # num_push(arr, 4) 15 | # => 4 16 | # => arr == 1 2 4 17 | # 18 | # Return the item for chainability. 19 | # 20 | function num_push(arr, item, i) { 21 | i = num_arr_length(arr) + 1 22 | arr[i] = item 23 | return item 24 | } 25 | 26 | ## 27 | # 28 | # Pop one item from an array list. 29 | # 30 | # Example: 31 | # 32 | # arr = 1 2 4 33 | # num_pop(arr) 34 | # => 4 35 | # => arr == 1 2 36 | # 37 | # Return the item. 38 | # 39 | function num_pop(arr, item, i) { 40 | i = num_arr_length(arr) 41 | item = arr[i] 42 | delete arr[i] 43 | return item 44 | } 45 | -------------------------------------------------------------------------------- /src/num-standard-deviation.awk: -------------------------------------------------------------------------------- 1 | ############################################################################ 2 | # 3 | # num-standard-deviation.awk 4 | # 5 | ## 6 | 7 | # Alias 8 | function num_standard_deviation(arr) { num_sample_standard_deviation(arr) } 9 | function num_standard_deviation_(num, num_, opts) { num_sample_standard_deviation_(num, num_, opts) } 10 | 11 | # Alias 12 | function num_stddev(arr) { num_sample_standard_deviation(arr) } 13 | function num_stddev_(num, num_, opts) { num_sample_standard_deviation_(num, num_, opts) } 14 | 15 | ## 16 | # 17 | # Sample Standard Deviation. 18 | # 19 | # Example: 20 | # 21 | # num_sample_standard_deviation(1 2 4) => 1.52753 22 | # 23 | ## 24 | 25 | function num_sample_standard_deviation(arr) { 26 | return sqrt(num_sample_variance(arr)) 27 | } 28 | 29 | function num_sample_standard_deviation_(num, num_, opts, f) { 30 | f = "num_sample_standard_deviation" 31 | if (!(f in num_)) num_[f] = sqrt(num_sample_variance_(num, num_, opts)) 32 | return num_[f] 33 | } 34 | 35 | function num_sample_standard_deviation_init() { 36 | num_function_init(\ 37 | "num_sample_standard_deviation sample_standard_deviation s_st_dev s_s_d standard_deviation std_dev sd", 0, 38 | "Get the sample standard deviation", 39 | "https://wikipedia.org/wiki/Standard_deviation") 40 | } 41 | 42 | # Alias 43 | function num_sstddev(arr) { num_sample_standard_deviation(arr) } 44 | function num_sstddev_(num, num_, opts) { num_sample_standard_deviation_(num, num_, opts) } 45 | 46 | ## 47 | # 48 | # Population Standard Deviation. 49 | # 50 | # Example: 51 | # 52 | # num_population_standard_deviation(1 2 4) => 1.24722 53 | # 54 | ## 55 | 56 | function num_population_standard_deviation(arr) { 57 | return sqrt(num_population_variance(arr)) 58 | } 59 | 60 | function num_population_standard_deviation_(num, num_, opts, f) { 61 | f = "num_population_standard_deviation" 62 | if (!(f in num_)) num_[f] = sqrt(num_population_variance_(num, num_, opts)) 63 | return num_[f] 64 | } 65 | 66 | function num_population_standard_deviation_init() { 67 | num_function_init(\ 68 | "num_population_standard_deviation population_standard_deviation p_st_dev p_s_d", 0, 69 | "Get the population standard deviation.", 70 | "https://wikipedia.org/wiki/Standard_deviation") 71 | } 72 | 73 | # Alias 74 | function num_pstddev(arr) { num_population_standard_deviation(arr) } 75 | function num_pstddev_(num, num_, opts) { num_population_standard_deviation_(num, num_, opts) } 76 | -------------------------------------------------------------------------------- /src/num-sum-of-mean-deviation.awk: -------------------------------------------------------------------------------- 1 | ############################################################################ 2 | # 3 | # num-sum-of-mean-deviation.awk 4 | # 5 | ## 6 | 7 | ## 8 | # 9 | # Sum of mean deviation exp. 10 | # 11 | # Example: 12 | # 13 | # arr = 1 2 4 14 | # exponent = 3 15 | # sum_of_mean_deviation_exp(arr, exponent) => sum of cubes 16 | # 17 | # Typically useful to calculate variance, skewness, kurtosis. 18 | # 19 | ## 20 | 21 | function num_sum_of_mean_deviation_exp(arr, mean, exponent, i, x) { 22 | for (i in arr) x += (arr[i] - mean) ^ exponent 23 | return x 24 | } 25 | 26 | function num_sum_of_mean_deviation_exp_(num, num_, opts, mean, exponent) { 27 | f = "num_sum_of_mean_deviation_exp" ":mean:" mean ":exponent:" exponent 28 | if (!(f in num_)) num_[f] = num_sum_of_mean_deviation_exp(num, mean, exponent) 29 | return num_[f] 30 | } 31 | 32 | function num_sum_of_mean_deviation_exp_init() { 33 | num_function_init(\ 34 | "num_sum_of_mean_deviation_exp", 0, 35 | "Get the sum of mean deviation for a given exponent.", 36 | "https://en.wikipedia.org/wiki/Deviation_(statistics)") 37 | } 38 | 39 | ## 40 | # 41 | # Sum of Squares, a.k.a. the sum of each deviation to the power of 2, a.k.a. SS. 42 | # 43 | # Example: 44 | # 45 | # num_sum_of_squares(1 2 4) => 4.66667 46 | # 47 | ## 48 | 49 | function num_sum_of_squares(arr) { 50 | return num_sum_of_mean_deviation_exp(arr, num_mean(arr), 2) 51 | } 52 | 53 | function num_sum_of_squares_(num, num_, opts) { 54 | f = "num_sum_of_squares" 55 | if (!(f in num_)) num_[f] = num_sum_of_mean_deviation_exp_(num, num_, opts, num_mean_(num, num_, opts), 2) 56 | return num_[f] 57 | } 58 | 59 | function num_sum_of_squares_init() { 60 | num_function_init(\ 61 | "num_sum_of_squares sum_of_squares sum_squares ss mean_squared_error mse", 0, 62 | "Get the sum of squares, a.k.a. sum of each mean deviation to the power of 2, a.k.a. SS", 63 | "https://en.wikipedia.org/wiki/Deviation_(statistics)") 64 | } 65 | 66 | ## 67 | # 68 | # Sum of Cubes, a.k.a. sum of each mean deviation to the power of 3. 69 | # 70 | # Example: 71 | # 72 | # 1 2 4 => 2.22222 73 | # 74 | ## 75 | 76 | function num_sum_of_cubes(arr) { 77 | return num_sum_of_mean_deviation_exp(arr, num_mean(arr), 3) 78 | } 79 | 80 | function num_sum_of_cubes_(num, num_, opts) { 81 | f = "num_sum_of_cubes" 82 | if (!(f in num_)) num_[f] = num_sum_of_mean_deviation_exp_(num, num_, opts, num_mean_(num, num_, opts), 3) 83 | return num_[f] 84 | } 85 | 86 | function num_sum_of_cubes_init() { 87 | num_function_init(\ 88 | "num_sum_of_cubes sum_of_cubes sum_cubes", 0, 89 | "Get the sum of cubes, a.k.a. sum of each mean deviation to the power of 3.", 90 | "https://en.wikipedia.org/wiki/Mean_squared_error") 91 | } 92 | 93 | ## 94 | # 95 | # Sum of Quads, a.k.a. sum of each mean deviation to the power of 4. 96 | # 97 | # Example: 98 | # 99 | # 1 2 4 => TODO 100 | # 101 | ## 102 | 103 | function num_sum_of_quads(arr) { 104 | return num_sum_of_mean_deviation_exp(arr, num_mean(arr), 4) 105 | } 106 | 107 | function num_sum_of_quads_(num, num_, opts) { 108 | f = "num_sum_of_quads" 109 | if (!(f in num_)) num_[f] = num_sum_of_mean_deviation_exp_(num, num_, opts, num_mean_(num, num_, opts), 4) 110 | return num_[f] 111 | } 112 | 113 | function num_sum_of_quads_init() { 114 | num_function_init(\ 115 | "num_sum_of_quads sum_of_quads sum_quads", 0, 116 | "Get the sum of quads, a.k.a. sum of each mean deviation to the power of 4.", 117 | "https://en.wikipedia.org/wiki/Mean_squared_error") 118 | } 119 | -------------------------------------------------------------------------------- /src/num-sum.awk: -------------------------------------------------------------------------------- 1 | ############################################################################ 2 | # 3 | # num-sum.awk 4 | # 5 | ## 6 | 7 | ## 8 | # 9 | # Sum, a.k.a. total. 10 | # 11 | # Example: 12 | # 13 | # num_sum(1 2 4) => 7 14 | ## 15 | 16 | function num_sum(arr, i, x) { 17 | for (i in arr) x += arr[i] 18 | return x 19 | } 20 | 21 | function num_sum_(num, num_, opts, f) { 22 | f = "num_sum" 23 | if (!(f in num_)) num_[f] = num_sum(num) # TODO optimize linear 24 | return num_[f] 25 | } 26 | 27 | function num_sum_init() { 28 | num_function_init(\ 29 | "num_sum sum total", 0, 30 | "Get the sum, a.k.a. total.", 31 | "https://en.wikipedia.org/wiki/Summation") 32 | } 33 | -------------------------------------------------------------------------------- /src/num-trimean.awk: -------------------------------------------------------------------------------- 1 | ############################################################################ 2 | # 3 | # num-trimean.awk 4 | # 5 | ## 6 | 7 | ## 8 | # 9 | # Trimean. 10 | # 11 | # Example: 12 | # 13 | # num_trimean(1 1.75 3 27.75 99) => 8.875 14 | # 15 | # Requirement: the array is sorted. 16 | # 17 | ## 18 | 19 | function num_trimean(arr, _q1, _q2, _q3) { 20 | _q1 = num_quartile_1(arr) 21 | _q2 = num_quartile_2(arr) 22 | _q3 = num_quartile_3(arr) 23 | return (_q1 + _q2 + _q2 + _q3) / 4 24 | } 25 | 26 | function num_trimean_(num, num_, opts, f, _q1, _q2, _q3) { 27 | f = "num_trimean" 28 | if (!(f in num_)) { 29 | _q1 = num_quartile_1_(num, num_, opts) 30 | _q2 = num_quartile_2_(num, num_, opts) 31 | _q3 = num_quartile_3_(num, num_, opts) 32 | num_[f] = (_q1 + _q2 + _q2 + _q3) / 4 33 | } 34 | return num_[f] 35 | } 36 | 37 | function num_trimean_init() { 38 | num_function_init(\ 39 | "num_trimean trimean", 0, 40 | "Calculate the trimean.", 41 | "https://wikipedia.org/wiki/Trimean") 42 | } 43 | -------------------------------------------------------------------------------- /src/num-trimmed-mean.awk: -------------------------------------------------------------------------------- 1 | ############################################################################ 2 | # 3 | # num-trimmed-mean.awk 4 | # 5 | ## 6 | 7 | ## 8 | # 9 | # Trimmed mean. This implemention deletes values that are not in the IQR. 10 | # 11 | # Example: 12 | # 13 | # num_trimmed_mean(1 2 3 4 5) => 2 3 4 14 | # 15 | # TODO: implement, and also upgrade to enable custom ranges. 16 | # 17 | ## 18 | 19 | function num_trimmed_mean_min_max(arr, q1, q3) { 20 | q1 = num_quartile_1(arr) 21 | q3 = num_quartile_3(arr) 22 | return TODO 23 | } 24 | 25 | function num_trimmed_mean_(num, num_, opts, f) { 26 | f = "num_trimmed_mean" 27 | if (!(f in num_)) { 28 | q1 = num_quartile_1_(num, num_, opts) 29 | q3 = num_quartile_3_(num, num_, opts) 30 | num_[f] = TODO 31 | } 32 | return num_[f] 33 | } 34 | 35 | function num_trimmed_mean_init() { 36 | num_function_init(\ 37 | "num_trimmed_mean trimmed_mean truncated_mean", 0, 38 | "Calculate the trimmed mean", 39 | "https://en.wikipedia.org/wiki/Truncated_Mean") 40 | } 41 | -------------------------------------------------------------------------------- /src/num-unique.awk: -------------------------------------------------------------------------------- 1 | ############################################################################ 2 | # 3 | # num-unique.awk 4 | # 5 | ## 6 | 7 | ## 8 | # 9 | # Is the list all unique items? 10 | # 11 | # Examples: 12 | # 13 | # 1 2 3 => TRUE 14 | # 1 2 2 => FALSE 15 | # 16 | function num_is_unique_(num, num_, opts, f, i, seen, flag) { 17 | f = "num_unique" 18 | if (!(f in num_)) { 19 | flag = TRUE 20 | split("", seen) 21 | for (i in num) { 22 | if (num[i] in seen) { 23 | flag = FALSE 24 | break 25 | } else { 26 | seen[num[i]] = TRUE 27 | } 28 | } 29 | num_[f] = flag 30 | } 31 | return num_[f] 32 | } 33 | 34 | function num_is_unique_init() { 35 | num_function_init(\ 36 | "num_is_unique is_unique is_uniq", 0, 37 | "Is the list all unique items?", 38 | "https://en.wikipedia.org/wiki/Uniqueness_quantification") 39 | } 40 | -------------------------------------------------------------------------------- /src/num-variance.awk: -------------------------------------------------------------------------------- 1 | ############################################################################ 2 | # 3 | # num-variance.awk 4 | # 5 | ## 6 | 7 | # Alias 8 | function num_variance(arr) { num_sample_variance(arr) } 9 | function num_variance_(num, num_, opts) { num_sample_variance_(num, num_, opts) } 10 | 11 | # Alias 12 | function num_var(arr) { num_sample_variance(arr) } 13 | function num_var_(num, num_, opts) { num_sample_variance_(num, num_, opts) } 14 | 15 | ## 16 | # 17 | # Sample Variance. 18 | # 19 | # Example: 20 | # 21 | # num_population_variance(1 2 4) => 2.33333 22 | # 23 | ## 24 | 25 | function num_sample_variance(arr) { 26 | return TODO 27 | } 28 | 29 | function num_sample_variance_(num, num_, opts, f) { 30 | f = "num_sample_variance" 31 | if (!(f in num_)) num_[f] = num_sum_of_mean_deviation_exp_(num, num_, _opts, num_mean_(num, num_, opts), 2) / (num_n_(num, num_, opts) - 1) 32 | return num_[f] 33 | } 34 | 35 | function num_sample_variance_init() { 36 | num_function_init(\ 37 | "num_sample_variance sample_variance s_var variance var sample_second_moment_about_the_mean s_second_moment_about_the_mean s_2_m_a_t_m second_moment_about_the_mean 2_m_a_t_m", 0, 38 | "Get the sample variance, a.k.a. sample second moment about the mean.", 39 | "https://wikipedia.org/wiki/Variance") 40 | } 41 | 42 | # Alias 43 | function num_svar(arr) { return num_sample_variance(arr) } 44 | function num_svar_(num, num_, opt) { return num_sample_variance_(num, num_, opt) } 45 | 46 | ## 47 | # 48 | # Population Variance. 49 | # 50 | # Example: 51 | # 52 | # num_population_variance(1 2 4) => 1.55556 53 | # 54 | ## 55 | 56 | function num_population_variance(arr) { 57 | return TODO 58 | } 59 | 60 | function num_population_variance_(num, num_, opts) { 61 | f = "num_population_variance" 62 | if (!(f in num_)) num_[f] = num_sum_of_mean_deviation_exp_(num, num_, opts, num_mean_(num, num_, opts), 2) / num_n_(num, num_, opts) 63 | return num_[f] 64 | } 65 | 66 | function num_population_variance_init() { 67 | num_function_init(\ 68 | "num_population_variance population_variance p_var population_second_moment_about_the_mean p_second_moment_about_the_mean p_2_m_a_t_m", 0, 69 | "Get the population variance, a.k.a. sample second moment about the mean.", 70 | "https://wikipedia.org/wiki/Variance") 71 | } 72 | 73 | # Alias 74 | function num_pvar(arr) { return num_population_variance(arr) } 75 | function num_pvar_(num, num_, opt) { return num_population_variance_(num, num_, opt) } 76 | -------------------------------------------------------------------------------- /src/num.awk: -------------------------------------------------------------------------------- 1 | ## Basics 2 | #@include "num-lint.awk" 3 | @include "num-help.awk" 4 | @include "num-out-err.awk" 5 | @include "num-function.awk" 6 | ## Mathematics 7 | @include "num-absolute-value.awk" 8 | @include "num-sign.awk" 9 | @include "num-increment.awk" 10 | @include "num-round.awk" 11 | @include "num-sum.awk" 12 | @include "num-product.awk" 13 | ## Lists 14 | @include "num-arr.awk" 15 | @include "num-list.awk" 16 | @include "num-frequency.awk" 17 | @include "num-shift.awk" 18 | @include "num-stack.awk" 19 | @include "num-queue.awk" 20 | @include "num-sort.awk" 21 | @include "num-insertion-sort.awk" 22 | @include "num-quicksort.awk" 23 | @include "num-map.awk" 24 | ## Filters 25 | @include "num-unique.awk" 26 | ## Maps 27 | @include "num-map-increment.awk" 28 | @include "num-map-absolute-value.awk" 29 | @include "num-map-sign.awk" 30 | @include "num-map-round.awk" 31 | ## Statistics 32 | @include "num-map-normalize.awk" 33 | @include "num-mean.awk" 34 | @include "num-mean-absolute-deviation.awk" 35 | @include "num-sum-of-mean-deviation.awk" 36 | @include "num-meanest.awk" 37 | @include "num-trimean.awk" 38 | @include "num-trimmed_mean.awk" 39 | @include "num-median.awk" 40 | @include "num-mode.awk" 41 | @include "num-variance.awk" 42 | @include "num-skewness.awk" 43 | @include "num-kurtosis.awk" 44 | @include "num-standard-deviation.awk" 45 | @include "num-coefficient-of-variance.awk" 46 | @include "num-quartiles.awk" 47 | ## Controllers 48 | @include "num-init.awk" 49 | @include "num-conf.awk" 50 | @include "num-scope.awk" 51 | @include "num-function-manager.awk" 52 | @include "num-print.awk" 53 | @include "num-argv.awk" 54 | @include "num-main.awk" 55 | -------------------------------------------------------------------------------- /src/num.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | set -euf 3 | 4 | ## 5 | # 6 | # Num: number utilties for mathematics and statistics. 7 | # 8 | # Syntax: 9 | # 10 | # num [ options ] [ file ... ] 11 | # 12 | # Full documentation and code is available: 13 | # 14 | # * http://www.numcommand.com 15 | # * https://github.com/numcommand/num 16 | # 17 | # ## Tracking 18 | # 19 | # Author: Joel Parker Henderson (joel@joelparkerhenderson.com) 20 | # License: GPL, BSD, MIT 21 | # Created: 2015-03-28 22 | # Updated: 2015-11-29 23 | # Version: 1.2.2 24 | # 25 | ## 26 | 27 | AWK=${AWK:-$(command -v gawk || command -v awk || echo "awk")} 28 | "$AWK" -v awk="$AWK" ' 29 | 30 | ## Basics 31 | #@include "num-lint.awk" 32 | @include "num-help.awk" 33 | @include "num-out-err.awk" 34 | @include "num-function.awk" 35 | ## Mathematics 36 | @include "num-absolute-value.awk" 37 | @include "num-sign.awk" 38 | @include "num-increment.awk" 39 | @include "num-round.awk" 40 | @include "num-sum.awk" 41 | @include "num-product.awk" 42 | ## Lists 43 | @include "num-arr.awk" 44 | @include "num-list.awk" 45 | @include "num-frequency.awk" 46 | @include "num-shift.awk" 47 | @include "num-stack.awk" 48 | @include "num-queue.awk" 49 | @include "num-sort.awk" 50 | @include "num-insertion-sort.awk" 51 | @include "num-quicksort.awk" 52 | @include "num-map.awk" 53 | ## Filters 54 | @include "num-unique.awk" 55 | ## Maps 56 | @include "num-map-increment.awk" 57 | @include "num-map-absolute-value.awk" 58 | @include "num-map-sign.awk" 59 | @include "num-map-round.awk" 60 | ## Statistics 61 | @include "num-map-normalize.awk" 62 | @include "num-mean.awk" 63 | @include "num-mean-absolute-deviation.awk" 64 | @include "num-sum-of-mean-deviation.awk" 65 | @include "num-meanest.awk" 66 | @include "num-trimean.awk" 67 | @include "num-trimmed-mean.awk" 68 | @include "num-median.awk" 69 | @include "num-mode.awk" 70 | @include "num-variance.awk" 71 | @include "num-skewness.awk" 72 | @include "num-kurtosis.awk" 73 | @include "num-standard-deviation.awk" 74 | @include "num-coefficient-of-variance.awk" 75 | @include "num-quartiles.awk" 76 | ## Controllers 77 | @include "num-init.awk" 78 | @include "num-conf.awk" 79 | @include "num-scope.awk" 80 | @include "num-function-manager.awk" 81 | @include "num-print.awk" 82 | @include "num-argv.awk" 83 | @include "num-main.awk" 84 | 85 | ' "$@" 86 | -------------------------------------------------------------------------------- /test/minitest.awk: -------------------------------------------------------------------------------- 1 | ## 2 | # This file has a some simple test functions. 3 | # 4 | # We include these test functions in our actual tests. 5 | # If there's a better way to create a library of functions, 6 | # please let us know and we'll update these test files. 7 | # 8 | # If you're translating num into any other language, 9 | # feel free to use your own language's testing framework, 10 | # testing patterns, and the like. 11 | ## 12 | 13 | function assert_init() { 14 | TRUE=1 15 | FALSE=0 16 | TODO="TODO" 17 | split(TODO, ARR_TODO) 18 | } 19 | 20 | function gsub_newline_to_rs( s) { 21 | gsub(/\n/, "␞", s) 22 | return s 23 | } 24 | 25 | function assert_eq(expect, actual, msg) { 26 | if (expect == actual) { 27 | print "assert_eq", msg, "expect:" gsub_newline_to_rs(expect), "actual:" gsub_newline_to_rs(actual), "SUCCESS" 28 | } else { 29 | print "assert_eq", msg, "expect:" gsub_newline_to_rs(expect), "actual:" gsub_newline_to_rs(actual), "FAILURE" 30 | } 31 | } 32 | 33 | function assert_eq_todo(expect, actual, msg) { 34 | assert_eq(TODO, actual, msg) 35 | } 36 | 37 | function assert_arr_eq(expect, actual, msg, k) { 38 | x = TRUE 39 | for (k in expect) { 40 | if (!(k in actual)) { 41 | x = FALSE 42 | err[++err_i] = "expect has key " k 43 | break 44 | } 45 | } 46 | for (k in actual) { 47 | if (!(k in expect)) { 48 | x = FALSE 49 | err[++err_i] = "actual has key " k 50 | break 51 | } 52 | } 53 | for (k in expect) { 54 | if (expect[k] != actual[k]) { 55 | x = FALSE 56 | err[++err_i] = "key:" k " expect:" expect[k] " actual:" actual[k] 57 | break 58 | } 59 | } 60 | if (x) { 61 | print "assert_arr_eq", msg, "SUCCESS" 62 | } else { 63 | print "assert_arr_eq", msg, "FAILURE" 64 | for (i = 1; i <= err_i; i++) { 65 | print " " err[i] 66 | } 67 | print "expect..." 68 | num_arr_dump(expect) 69 | print "actual..." 70 | num_arr_dump(actual) 71 | } 72 | } 73 | 74 | function assert_arr_eq_todo(expect, actual, msg) { 75 | assert_eq(ARR_TODO, actual, msg) 76 | } 77 | -------------------------------------------------------------------------------- /test/minitest.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # 3 | # This file runs some simple tests. 4 | # 5 | # If there's a better way to create a library of tests, 6 | # please let us know and we'll update these test files. 7 | # 8 | # If you're translating num into any other language, 9 | # feel free to use your own language's testing framework, 10 | # testing patterns, and the like. 11 | ## 12 | 13 | ## Constants 14 | TRUE=1 15 | FALSE=0 16 | TODO="TODO" 17 | UNDEF="UNDEF" 18 | 19 | minitest_start() { 20 | success_count=0 21 | failure_count=0 22 | } 23 | 24 | minitest_stop() { 25 | echo "SUCCESS count: $success_count" 26 | echo "FAILURE count: $failure_count" 27 | } 28 | 29 | assert_eq() { 30 | expect="$1"; actual="$2"; msg="$3" 31 | expect_pretty=${expect//$'\n'/␞}; expect_pretty=${expect_pretty//$'\t'/␟}; 32 | actual_pretty=${actual//$'\n'/␞}; actual_pretty=${actual_pretty//$'\t'/␟} 33 | if [ "$expect" == "$actual" ]; then 34 | success_count=$((success_count+1)) 35 | echo "assert_eq $msg expect:$expect_pretty actual:$actual_pretty: SUCCESS" 36 | else 37 | failure_count=$((failure_count+1)) 38 | echo "assert_eq $msg expect:$expect_pretty actual:$actual_pretty: FAILURE" 39 | fi 40 | } 41 | 42 | assert_eq_todo() { 43 | assert_eq $TODO "$2" "$3" 44 | } 45 | -------------------------------------------------------------------------------- /test/num-absolute-value-test.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | ############################################################################ 3 | # 4 | # num-absolute-value.awk 5 | # 6 | ## 7 | 8 | . minitest.sh 9 | num=${NUM:-num} 10 | 11 | ## 12 | # 13 | # absolute value 14 | # 15 | ## 16 | 17 | ## 18 | # 19 | # ...with all positive... 20 | # 21 | # row: 22 | # 23 | # $ echo "1 2 3" | num absolute-value 24 | # 1 2 3 25 | # 26 | # column: 27 | # 28 | # $ echo "1\n2\n3" | num absolute-value 29 | # 1 2 3 30 | # 31 | # records of positives: 32 | # 33 | # $ echo "1 2 3\n5 6 9" | num absolute-value records 34 | # 1 2 3 35 | # 5 6 9 36 | ## 37 | 38 | x=$(echo "1 2 3" | "$num" absolute-value) && 39 | assert_eq "1 2 3" "$x" "absolute-value with row with all positive" 40 | 41 | x=$(echo "1\n2\n3" | "$num" absolute-value) && 42 | assert_eq "1 2 3" "$x" "absolute-value with col with all positive" 43 | 44 | x=$(echo "1 2 3\n5 6 9" | "$num" absolute-value records) && 45 | assert_eq "1 2 3"$'\n'"5 6 9" "$x" "absolute-value with records with all positive" 46 | 47 | ## 48 | # 49 | # ...with all negative... 50 | # 51 | # row: 52 | # 53 | # $ echo "-1 -2 -3" | num absolute-value 54 | # 1 2 3 55 | # 56 | # column: 57 | # 58 | # $ echo "-1\n-2\n-3" | num absolute-value 59 | # 1 2 3 60 | # 61 | # records: 62 | # 63 | # $ echo "-1 -2 -3\n-5 -6 -9" | num absolute-value records 64 | # 1 2 3 65 | # 5 6 9 66 | ## 67 | 68 | x=$(echo "-1 -2 -3" | "$num" absolute-value) && 69 | assert_eq "1 2 3" "$x" "absolute-value with row with all negative" 70 | 71 | x=$(echo "-1\n-2\n-3" | "$num" $f) && 72 | assert_eq "1 2 3" "$x" "absolute-value with col with all negative" 73 | 74 | x=$(echo "-1 -2 -3\n-5 -6 -9" | "$num" $f records) && 75 | assert_eq "1 2 3"$'\n'"5 6 9" "$x" "absolute-value with records with all negative" 76 | -------------------------------------------------------------------------------- /test/num-coefficient-of-variance-test.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | ############################################################################ 3 | # 4 | # num-coefficient-of-variance-test.sh 5 | # 6 | ## 7 | 8 | . minitest.sh 9 | num=${NUM:-num} 10 | 11 | ## 12 | # 13 | # coefficient of variance 14 | # 15 | # row: 16 | # 17 | # $ echo "1 2 4" | num coefficient-of-variance 18 | # 0.654654 19 | # 20 | # column: 21 | # 22 | # $ echo "1\n2\n4" | num coefficient-of-variance 23 | # 0.654654 24 | # 25 | # records: 26 | # 27 | # $ echo "1 2 4\n5 6 9" | num coefficient-of-variance records 28 | # 0.654654 29 | # 0.31225 30 | ## 31 | 32 | x=$(echo "1 2 4" | "$num" coefficient-of-variance) && 33 | assert_eq 0.654654 "$x" "coefficient-of-variance with row" 34 | 35 | x=$(echo "1\n2\n4" | "$num" $f) && 36 | assert_eq 0.654654 "$x" "coefficient-of-variance with col" 37 | 38 | x=$(echo "1 2 4\n5 6 9" | "$num" coefficient-of-variance records) && 39 | assert_eq "0.654654"$'\n'"0.31225" "$x" "coefficient-of-variance with records" 40 | 41 | ## 42 | # 43 | # sample coefficient of variance 44 | # 45 | # row: 46 | # 47 | # $ echo "1 2 4" | num sample-coefficient-of-variance 48 | # 0.654654 49 | # 50 | # column: 51 | # 52 | # $ echo "1\n2\n4" | num sample-coefficient-of-variance 53 | # 0.654654 54 | # 55 | # records: 56 | # 57 | # $ echo "1 2 4\n5 6 9" | num sample-coefficient-of-variance records 58 | # 0.654654 59 | # 0.31225 60 | ## 61 | 62 | x=$(echo "1 2 4" | "$num" sample-coefficient-of-variance) && 63 | assert_eq 0.654654 "$x" "sample-coefficient-of-variance with row" 64 | 65 | x=$(echo "1\n2\n4" | "$num" sample-coefficient-of-variance) && 66 | assert_eq 0.654654 "$x" "sample-coefficient-of-variance with col" 67 | 68 | x=$(echo "1 2 4\n5 6 9" | "$num" sample-coefficient-of-variance records) && 69 | assert_eq "0.654654"$'\n'"0.31225" "$x" "sample-coefficient-of-variance with records" 70 | 71 | ## 72 | # 73 | # population coefficient of variance 74 | # 75 | # row: 76 | # 77 | # $ echo "1 2 4" | num population-coefficient-of-variance 78 | # 0.534522 79 | # 80 | # column: 81 | # 82 | # $ echo "1\n2\n4" | num population-coefficient-of-variance 83 | # 0.534522 84 | # 85 | # records: 86 | # 87 | # $ echo "1 2 4\n5 6 9" | num population-coefficient-of-variance records 88 | # 0.534522 89 | # 0.254951 90 | ## 91 | 92 | x=$(echo "1 2 4" | "$num" population-coefficient-of-variance) && 93 | assert_eq 0.534522 "$x" "population-coefficient-of-variance with row" 94 | 95 | x=$(echo "1\n2\n4" | "$num" population-coefficient-of-variance) && 96 | assert_eq 0.534522 "$x" "population-coefficient-of-variance with col" 97 | 98 | x=$(echo "1 2 4\n5 6 9" | "$num" population-coefficient-of-variance records) && 99 | assert_eq "0.534522"$'\n'"0.254951" "$x" "population-coefficient-of-varianc with records" 100 | -------------------------------------------------------------------------------- /test/num-frequency-test.sh: -------------------------------------------------------------------------------- 1 | ############################################################################ 2 | # 3 | # num-frequency-test.sh 4 | # 5 | ## 6 | 7 | . minitest.sh 8 | num=${NUM:-num} 9 | 10 | ## 11 | # 12 | # frequency minimum 13 | # 14 | ## 15 | 16 | f="frequency-minimum" 17 | 18 | x=$(echo "10 10 20 20 20 40" | "$num" $f) && 19 | assert_eq 1 "$x" "$f with row" 20 | 21 | x=$(echo "10\n10\n20\n20\n20\n40" | "$num" $f) && 22 | assert_eq 1 "$x" "$f with col" 23 | 24 | x=$(echo "10 20 20 20 40\n50 50 60 60 60 60 90 90" | "$num" $f records) && 25 | assert_eq "1"$'\n'"2" "$x" "$f with records" 26 | 27 | ## 28 | # 29 | # frequency maximum 30 | # 31 | ## 32 | 33 | f="frequency-maximum" 34 | 35 | x=$(echo "10 10 20 20 20 40" | "$num" $f) && 36 | assert_eq 3 "$x" "$f with row" 37 | 38 | x=$(echo "10\n10\n20\n20\n20\n40" | "$num" $f) && 39 | assert_eq 3 "$x" "$f with col" 40 | 41 | x=$(echo "10 10 20 20 20 40\n50 50 60 60 60 60 90 90" | "$num" $f records) && 42 | assert_eq "3"$'\n'"4" "$x" "$f with records" 43 | -------------------------------------------------------------------------------- /test/num-help-test.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | ############################################################################ 3 | # 4 | # num-help-test.sh 5 | # 6 | ## 7 | 8 | . minitest.sh 9 | num=${NUM:-num} 10 | 11 | ## 12 | # 13 | # help, version, usage 14 | # 15 | ## 16 | 17 | x=$("$num" help | head -1 | grep -o 'Num version') && 18 | assert_eq "Num version" "$x" "help" 19 | 20 | x=$("$num" --help | head -1 | grep -o 'Num version') && 21 | assert_eq "Num version" "$x" "--help" 22 | 23 | x=$("$num" version | head -1 | grep -o 'Num version') && 24 | assert_eq "Num version" "$x" "version" 25 | 26 | x=$("$num" --version | head -1 | grep -o 'Num version') && 27 | assert_eq "Num version" "$x" "--version" 28 | 29 | x=$("$num" usage | head -1 | grep -o 'Num version') && 30 | assert_eq "Num version" "$x" "usage" 31 | 32 | x=$("$num" --usage | head -1 | grep -o 'Num version') && 33 | assert_eq "Num version" "$x" "--usage" 34 | -------------------------------------------------------------------------------- /test/num-increment-test.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | ############################################################################ 3 | # 4 | # num-increment-test.awk 5 | # 6 | ## 7 | 8 | . minitest.sh 9 | num=${NUM:-num} 10 | 11 | ## 12 | # 13 | # increment 14 | # 15 | ## 16 | 17 | x=$(echo "-8 0 8" | "$num" increment) && 18 | assert_eq "-7 1 9" "$x" "increment, with row" 19 | 20 | x=$(echo "-8\n0\n8" | "$num" increment) && 21 | assert_eq "-7 1 9" "$x" "increment, with col" 22 | 23 | x=$(echo "-8 0 8\n-9 0 9" | "$num" increment records) && 24 | assert_eq "-7 1 9"$'\n'"-8 1 10" "$x" "increment, with records" 25 | -------------------------------------------------------------------------------- /test/num-kurtosis-test.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | ############################################################################ 3 | # 4 | # num-kurtosis-test.sh 5 | # 6 | ## 7 | 8 | . minitest.sh 9 | num=${NUM:-num} 10 | 11 | ## 12 | # 13 | # kurtosis 14 | # 15 | ## 16 | 17 | f="kurtosis" 18 | 19 | x=$(echo "1 2 4" | "$num" $f) && 20 | assert_eq 5.44444 "$x" "$f with row" 21 | 22 | x=$(echo "1\n2\n4" | "$num" $f) && 23 | assert_eq 5.44444 "$x" "$f with col" 24 | 25 | x=$(echo "1 2 4\n5 6 9" | "$num" $f records) && 26 | assert_eq "5.44444"$'\n'"18.7778" "$x" "$f with records" 27 | 28 | ## 29 | # 30 | # sample kurtosis 31 | # 32 | ## 33 | 34 | f="sample-kurtosis" 35 | 36 | x=$(echo "1 2 4" | "$num" $f) && 37 | assert_eq 5.44444 "$x" "$f with row" 38 | 39 | x=$(echo "1\n2\n4" | "$num" $f) && 40 | assert_eq 5.44444 "$x" "$f with col" 41 | 42 | x=$(echo "1 2 4\n5 6 9" | "$num" $f records) && 43 | assert_eq "5.44444"$'\n'"18.7778" "$x" "$f with records" 44 | 45 | ## 46 | # 47 | # population kurtosis 48 | # 49 | ## 50 | 51 | f="population-kurtosis" 52 | 53 | x=$(echo "1 2 4" | "$num" $f) && 54 | assert_eq 3.62963 "$x" "$f with row" 55 | 56 | x=$(echo "1\n2\n4" | "$num" $f) && 57 | assert_eq 3.62963 "$x" "$f with col" 58 | 59 | x=$(echo "1 2 4\n5 6 9" | "$num" $f records) && 60 | assert_eq "3.62963"$'\n'"12.5185" "$x" "$f with records" 61 | -------------------------------------------------------------------------------- /test/num-list-test.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | ############################################################################ 3 | # 4 | # num-list-test.sh 5 | # 6 | ## 7 | 8 | . minitest.sh 9 | num=${NUM:-num} 10 | 11 | ## 12 | # 13 | # n 14 | # 15 | ## 16 | 17 | f="n" 18 | 19 | x=$(echo "1 2 4" | "$num" $f) && 20 | assert_eq 3 "$x" "$f with row" 21 | 22 | x=$(echo "1\n2\n4" | "$num" n) && 23 | assert_eq 3 "$x" "$f with col" 24 | 25 | x=$(echo "1 2 4\n5 6 9 9" | "$num" n records) && 26 | assert_eq "3"$'\n'"4" "$x" "$f with records" 27 | 28 | ## 29 | # 30 | # first 31 | # 32 | ## 33 | 34 | f="first" 35 | 36 | x=$(echo "1 2 4" | "$num" $f) && 37 | assert_eq 1 "$x" "$f, with row" 38 | 39 | x=$(echo "1\n2\n4" | "$num" $f) && 40 | assert_eq 1 "$x" "$f, with col" 41 | 42 | x=$(echo "1 2 4\n5 6 9" | "$num" $f records) && 43 | assert_eq "1"$'\n'"5" "$x" "$f, with records" 44 | 45 | ## 46 | # 47 | # last 48 | # 49 | ## 50 | 51 | f="last" 52 | 53 | x=$(echo "1 2 4" | "$num" $f) && 54 | assert_eq 4 "$x" "$f, with row" 55 | 56 | x=$(echo "1\n2\n4" | "$num" $f) && 57 | assert_eq 4 "$x" "$f, with col" 58 | 59 | x=$(echo "1 2 4\n5 6 9" | "$num" $f records) && 60 | assert_eq "4"$'\n'"9" "$x" "$f, with records" 61 | 62 | ## 63 | # 64 | # all 65 | # 66 | ## 67 | 68 | f="all" 69 | 70 | x=$(echo "1 2 4" | "$num") && 71 | assert_eq "1 2 4" "$x" "$f with row" 72 | 73 | x=$(echo "1\n2\n4" | "$num") && 74 | assert_eq "1 2 4" "$x" "$f with col" 75 | 76 | x=$(echo "1 2 4\n5 6 9" | "$num" records) && 77 | assert_eq "1 2 4"$'\n'"5 6 9" "$x" "$f with records" 78 | 79 | ## 80 | # 81 | # minimum 82 | # 83 | ## 84 | 85 | f="minimum" 86 | 87 | x=$(echo "1 2 4" | "$num" $f) && 88 | assert_eq 1 "$x" "$f, with row" 89 | 90 | x=$(echo "1\n2\n4" | "$num" $f) && 91 | assert_eq 1 "$x" "$f, with col" 92 | 93 | x=$(echo "1 2 4\n5 6 9" | "$num" $f records) && 94 | assert_eq "1"$'\n'"5" "$x" "$f, with records" 95 | 96 | ## 97 | # 98 | # maximum 99 | # 100 | ## 101 | 102 | f="maximum" 103 | 104 | x=$(echo "1 2 4" | "$num" $f) && 105 | assert_eq 4 "$x" "$f, wih row" 106 | 107 | x=$(echo "1\n2\n4" | "$num" $f) && 108 | assert_eq 4 "$x" "$f, wih col" 109 | 110 | x=$(echo "1 2 4\n5 6 9" | "$num" $f records) && 111 | assert_eq "4"$'\n'"9" "$x" "$f, wih records" 112 | 113 | ## 114 | # 115 | # range 116 | # 117 | ## 118 | 119 | f="range" 120 | 121 | x=$(echo "1 2 4" | "$num" $f) && 122 | assert_eq 3 "$x" "$f, with row" 123 | 124 | x=$(echo "1 2 4" | "$num" $f) && 125 | assert_eq 3 "$x" "$f, with col" 126 | 127 | x=$(echo "1 2 4\n5 6 9" | "$num" $f records) && 128 | assert_eq "3"$'\n'"4" "$x" "$f, with records" 129 | 130 | ## 131 | # 132 | # comparisons 133 | # 134 | ## 135 | 136 | #TODO 137 | 138 | # x=$(echo "1 2 3 4 5" | "$num" select-eq 3) && 139 | # assert_eq "3" "$x" "select eq" 140 | 141 | # x=$(echo "1 2 3 4 5" | "$num" select-ne 3) && 142 | # assert_eq "1 2 4 5" "$x" "select ne" 143 | 144 | # x=$(echo "1 2 3 4 5" | "$num" select-lt 3) && 145 | # assert_eq "1 2" "$x" "select lt" 146 | 147 | # x=$(echo "1 2 3 4 5" | "$num" select-le 3) && 148 | # assert_eq "1 2 3" "$x" "select le" 149 | 150 | # x=$(echo "1 2 3 4 5" | "$num" select-gt 3) && 151 | # assert_eq "4 5" "$x" "select gt" 152 | 153 | # x=$(echo "1 2 3 4 5" | "$num" select-ge 3) && 154 | # assert_eq "3 4 5" "$x" "select ge" 155 | 156 | # x=$(echo "1 2 3 4 5" | "$num" select-in 2 4) && 157 | # assert_eq "3 4 5" "$x" "select in" 158 | 159 | # x=$(echo "1 2 3 4 5" | "$num" select-ex 2 4) && 160 | # assert_eq "1 5" "$x" "select ex" 161 | 162 | # x=$(echo "1 2 3 4 5" | "$num" reject-eq 3) && 163 | # assert_eq "1 2 4 5" "$x" "reject eq" 164 | 165 | # x=$(echo "1 2 3 4 5" | "$num" reject-ne 3) && 166 | # assert_eq "3" "$x" "reject ne" 167 | 168 | # x=$(echo "1 2 3 4 5" | "$num" reject-lt 3) && 169 | # assert_eq "3 4 5" "$x" "reject lt" 170 | 171 | # x=$(echo "1 2 3 4 5" | "$num" reject-le 3) && 172 | # assert_eq "4 5" "$x" "reject le" 173 | 174 | # x=$(echo "1 2 3 4 5" | "$num" reject-gt 3) && 175 | # assert_eq "1 2 3" "$x" "reject gt" 176 | 177 | # x=$(echo "1 2 3 4 5" | "$num" reject-ge 3) && 178 | # assert_eq "1 2" "$x" "reject ge" 179 | 180 | # x=$(echo "1 2 3 4 5" | "$num" reject-in 2 4) && 181 | # assert_eq "1 5" "$x" "reject in" 182 | 183 | # x=$(echo "1 2 3 4 5" | "$num" reject-ex 2 4) && 184 | # assert_eq "2 3 4" "$x" "reject ex" 185 | -------------------------------------------------------------------------------- /test/num-mean-absolute-deviation-test.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | ############################################################################ 3 | # 4 | # num-mean-absolute-deviation-test.sh 5 | # 6 | ## 7 | 8 | . minitest.sh 9 | num=${NUM:-num} 10 | 11 | ## 12 | # 13 | # mean absolute deviation 14 | # 15 | ## 16 | 17 | f="mean-absolute-deviation" 18 | 19 | x=$(echo "1 2 4" | "$num" $f) && 20 | assert_eq 1.11111 "$x" "$f with row" 21 | 22 | x=$(echo "1\n2\n4" | "$num" $f) && 23 | assert_eq 1.11111 "$x" "$f with col" 24 | 25 | x=$(echo "1 2 4\n5 6 9" | "$num" $f records) && 26 | assert_eq "1.11111"$'\n'"1.55556" "$x" "$f with records" 27 | -------------------------------------------------------------------------------- /test/num-mean-test.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | ############################################################################ 3 | # 4 | # num-mean-test.sh 5 | # 6 | ## 7 | 8 | . minitest.sh 9 | num=${NUM:-num} 10 | 11 | ## 12 | # 13 | # mean 14 | # 15 | ## 16 | 17 | f="mean" 18 | 19 | x=$(echo "1 2 4" | "$num" $f) && 20 | assert_eq 2.33333 "$x" "$f with row" 21 | 22 | x=$(echo "1\n2\n4" | "$num" $f) && 23 | assert_eq 2.33333 "$x" "$f with col" 24 | 25 | x=$(echo "1 2 4\n5 6 9" | "$num" $f records) && 26 | assert_eq "2.33333"$'\n'"6.66667" "$x" "$f with records" 27 | -------------------------------------------------------------------------------- /test/num-meanest-test.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | ############################################################################ 3 | # 4 | # num-meanest-test.sh 5 | # 6 | ## 7 | 8 | . minitest.sh 9 | num=${NUM:-num} 10 | 11 | ## 12 | # 13 | # meanest 14 | # 15 | ## 16 | 17 | f="meanest" 18 | 19 | x=$(echo "1 2 4" | "$num" $f) && 20 | assert_eq 2 "$x" "$f with row" 21 | 22 | x=$(echo "1\n2\n4" | "$num" $f) && 23 | assert_eq 2 "$x" "$f with col" 24 | 25 | x=$(echo "1 2 4\n5 6 9" | "$num" $f records) && 26 | assert_eq "2"$'\n'"6" "$x" "$f with records" 27 | -------------------------------------------------------------------------------- /test/num-median-test.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | ############################################################################ 3 | # 4 | # num-median-test.sh 5 | # 6 | ## 7 | 8 | . minitest.sh 9 | num=${NUM:-num} 10 | 11 | ## 12 | # 13 | # median 14 | # 15 | ## 16 | 17 | f="median" 18 | 19 | ## with exact median 20 | 21 | x=$(echo "1 2 4" | "$num" $f) && 22 | assert_eq 2 "$x" "$f with row, with exact median" 23 | 24 | x=$(echo "1\n2\n4" | "$num" $f) && 25 | assert_eq 2 "$x" "$f with col, with exact median" 26 | 27 | x=$(echo "1 2 4\n5 6 9" | "$num" $f records) && 28 | assert_eq "2"$'\n'"6" "$x" "$f with records, with exact median" 29 | 30 | ## with split median 31 | 32 | x=$(echo "1 2 3 4" | "$num" $f) && 33 | assert_eq 2.5 "$x" "$f with row, with split median" 34 | 35 | x=$(echo "1\n2\n3\n4" | "$num" $f) && 36 | assert_eq 2.5 "$x" "$f with col, with split median" 37 | 38 | x=$(echo "1 2 3 4\n5 6 7 8" | "$num" $f records) && 39 | assert_eq "2.5"$'\n'"6.5" "$x" "$f with records, with split median" 40 | 41 | ## 42 | # 43 | # median-low 44 | # 45 | ## 46 | 47 | f="median-low" 48 | 49 | ## with exact median 50 | 51 | x=$(echo "1 2 4" | "$num" $f) && 52 | assert_eq 2 "$x" "$f with row, with exact median" 53 | 54 | x=$(echo "1\n2\n4" | "$num" $f) && 55 | assert_eq 2 "$x" "$f with col, with exact median" 56 | 57 | x=$(echo "1 2 4\n5 6 9" | "$num" $f records) && 58 | assert_eq "2"$'\n'"6" "$x" "$f with records, with exact median" 59 | 60 | ## with split median 61 | 62 | x=$(echo "1 2 3 4" | "$num" $f) && 63 | assert_eq 2 "$x" "$f with row, with split median" 64 | 65 | x=$(echo "1\n2\n3\n4" | "$num" $f) && 66 | assert_eq 2 "$x" "$f with col, with split median" 67 | 68 | x=$(echo "1 2 3 4\n5 6 7 8" | "$num" $f records) && 69 | assert_eq "2"$'\n'"6" "$x" "$f with records, with split median" 70 | 71 | ## 72 | # 73 | # median-high 74 | # 75 | ## 76 | 77 | f="median-high" 78 | 79 | ## with exact median 80 | 81 | x=$(echo "1 2 4" | "$num" $f) && 82 | assert_eq 2 "$x" "$f with row, with exact median" 83 | 84 | x=$(echo "1\n2\n4" | "$num" $f) && 85 | assert_eq 2 "$x" "$f with col, with exact median" 86 | 87 | x=$(echo "1 2 4\n5 6 9" | "$num" $f records) && 88 | assert_eq "2"$'\n'"6" "$x" "$f with records, with exact median" 89 | 90 | ## with split median 91 | 92 | x=$(echo "1 2 3 4" | "$num" $f) && 93 | assert_eq 3 "$x" "$f with row, with split median" 94 | 95 | x=$(echo "1\n2\n3\n4" | "$num" $f) && 96 | assert_eq 3 "$x" "$f with col, with split median" 97 | 98 | x=$(echo "1 2 3 4\n5 6 7 8" | "$num" $f records) && 99 | assert_eq "3"$'\n'"7" "$x" "$f with records, with split median" 100 | -------------------------------------------------------------------------------- /test/num-mode-test.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | ############################################################################ 3 | # 4 | # num-mode-test.sh 5 | # 6 | ## 7 | 8 | . minitest.sh 9 | num=${NUM:-num} 10 | 11 | ## 12 | # 13 | # mode 14 | # 15 | # 1 2 2 3 => 2 16 | # 1 1 2 3 3 => 1 3 17 | # 1 2 3 => UNDEF 18 | # 19 | # #TODO implement 20 | # 21 | ## 22 | 23 | f="modes" 24 | 25 | ## unimodal 26 | 27 | #x=$(echo "1 2 2 3" | "$num" $f) && 28 | # assert_eq 2 "$x" "$f with row, with unimodal" 29 | 30 | #x=$(echo "1\n2\n2\n3" | "$num" $f) && 31 | # assert_eq 2 "$x" "$f with col, with unimodal" 32 | 33 | #x=$(echo "1 2 2 3\n5 6 6 7" | "$num" $f records) && 34 | # assert_eq "2"$'\n'"6" "$x" "$f with records, with unimodal" 35 | 36 | ## bimodal 37 | 38 | #x=$(echo "1 1 2 3 3" | "$num" $f) && 39 | # assert_eq "1 3" "$x" "$f with row, with bimodal" 40 | 41 | #x=$(echo "1\n1\n2\n3\n3" | "$num" $f) && 42 | # assert_eq "1 3" "$x" "$f with col, with bimodal" 43 | 44 | #x=$(echo "1 1 2 3 3\n5 5 6 7 7" | "$num" $f records) && 45 | # assert_eq "1 3"$'\n'"5 7" "$x" "$f with records, with unimodal" 46 | 47 | ## nonmodal 48 | 49 | #x=$(echo "1 2 3" | "$num" $f) && 50 | # assert_eq $UNDEF "$x" "$f with row, with nonmodal" 51 | 52 | #x=$(echo "1\n2\n3" | "$num" $f) && 53 | # assert_eq $UNDEF "$x" "$f with col, with nonmodal" 54 | 55 | #x=$(echo "1 2 3\n5 6 7" | "$num" $f records) && 56 | # assert_eq $UNDEF$'\n'$UNDEF "$x" "$f with records, with unimodal" 57 | -------------------------------------------------------------------------------- /test/num-normalize-test.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | ############################################################################ 3 | # 4 | # num-normalize-test.sh 5 | # 6 | ## 7 | 8 | . minitest.sh 9 | num=${NUM:-num} 10 | 11 | ## 12 | # 13 | # normalize 14 | # 15 | ## 16 | 17 | f="normalize" 18 | 19 | x=$(echo "1 2 4" | "$num" $f) && 20 | assert_eq "0 0.333333 1" "$x" "$f with row" 21 | 22 | x=$(echo "1\n2\n4" | "$num" $f) && 23 | assert_eq "0 0.333333 1" "$x" "$f with col" 24 | 25 | x=$(echo "1 2 4\n5 6 9" | "$num" $f records) && 26 | assert_eq "0 0.333333 1"$'\n'"0 0.25 1" "$x" "$f with records" 27 | -------------------------------------------------------------------------------- /test/num-ofmt-test.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | ############################################################################ 3 | # 4 | # num-ofmt-test.awk 5 | # 6 | ## 7 | 8 | . minitest.sh 9 | num=${NUM:-num} 10 | 11 | ## 12 | # 13 | # output format 14 | # 15 | ## 16 | 17 | ## with all 18 | 19 | x=$(echo "11.11 22.22 99.99" | OFMT="%.0f" "$num") && 20 | assert_eq "11 22 100" "$x" "ofmt, with row" 21 | 22 | x=$(echo "11.11\n22.22\n99.99" | OFMT="%.0f" "$num") && 23 | assert_eq "11 22 100" "$x" "oftm, with col" 24 | 25 | x=$(echo "11.11 22.22 33.33\n44.44 55.55 66.66\n77.77 88.88 99.99" | OFMT="%.0f" "$num" records) && 26 | assert_eq "11 22 33"$'\n'"44 56 67"$'\n'"78 89 100" "$x" "ofmt, with records" 27 | 28 | ## with any typical function 29 | 30 | x=$(echo "11.11 22.22" | OFMT="%.0f" "$num" sum) && 31 | assert_eq "33" "$x" "ofmt, with row" 32 | 33 | x=$(echo "11.11\n22.22" | OFMT="%.0f" "$num" sum) && 34 | assert_eq "33" "$x" "oftm, with col" 35 | 36 | x=$(echo "11.11 22.22\n33.33 44.44\n55.55 66.66" | OFMT="%.0f" "$num" sum records) && 37 | assert_eq "33"$'\n'"78"$'\n'"122" "$x" "ofmt, with records" 38 | -------------------------------------------------------------------------------- /test/num-product-test.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | ############################################################################ 3 | # 4 | # num-product-test.sh 5 | # 6 | ## 7 | 8 | . minitest.sh 9 | num=${NUM:-num} 10 | 11 | ## 12 | # 13 | # product 14 | # 15 | ## 16 | 17 | f="product" 18 | 19 | x=$(echo "1 2 4" | "$num" $f) && 20 | assert_eq 8 "$x" "$f with row" 21 | 22 | x=$(echo "1\n2\n4" | "$num" $f) && 23 | assert_eq 8 "$x" "$f with col" 24 | 25 | x=$(echo "1 2 4\n5 6 9" | "$num" $f records) && 26 | assert_eq "8"$'\n'"270" "$x" "$f with records" 27 | -------------------------------------------------------------------------------- /test/num-quicksort-test.awk: -------------------------------------------------------------------------------- 1 | function num_quicksort_pivot_index_via_median_of_three_sort_test( expect, actual, min, med, max) { 2 | min = 2 3 | med = 4 4 | max = 6 5 | split(min " " med " " max, expect) 6 | split(max " " min " " med, actual) 7 | num_quicksort_pivot_index_via_median_of_three_sort(actual) 8 | assert_arr_eq(expect, actual, "num_quicksort_pivot_index_via_median_of_three_sort") 9 | } 10 | 11 | function num_quicksort_pivot_index_via_median_of_three_sort_slice_test( expect, actual, min, med, max, chaff) { 12 | min = 2 13 | med = 4 14 | max = 6 15 | chaff = " 1 1 1 " 16 | split(chaff min " " med " " max chaff, expect) 17 | split(chaff max " " min " " med chaff, actual) 18 | num_quicksort_pivot_index_via_median_of_three_sort_slice(actual, 4, 6) 19 | assert_arr_eq(expect, actual, "num_quicksort_pivot_index_via_median_of_three_sort_slice") 20 | } 21 | 22 | function num_partition_slice_test( expect, actual, min, med, max, lo, hi, pivot_index) { 23 | lo = 1 24 | hi = 5 25 | pivot_index = 3 26 | split("1 1 2 3 3", expect) 27 | split("3 3 2 1 1", actual) 28 | num_partition_slice(actual, lo, hi, pivot_index) 29 | assert_arr_eq(expect, actual, "num_partition_slice") 30 | } 31 | 32 | function num_quicksort_test( expect, actual) { 33 | split("1 1 2 3 4 5 6 9", expect) 34 | split("3 1 4 1 5 9 2 6", actual) 35 | num_quicksort(actual) 36 | assert_arr_eq(expect, actual, "num_quicksort") 37 | } 38 | 39 | BEGIN { 40 | assert_init() 41 | num_quicksort_pivot_index_via_median_of_three_sort_test() 42 | num_quicksort_pivot_index_via_median_of_three_sort_slice_test() 43 | num_partition_slice_test() 44 | num_quicksort_test() 45 | exit 46 | } 47 | -------------------------------------------------------------------------------- /test/num-quicksort-test.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | ############################################################################ 3 | # 4 | # num-quicksort-test.sh 5 | # 6 | ## 7 | 8 | . minitest.sh 9 | num=${NUM:-num} 10 | 11 | ## 12 | # 13 | # quicksort 14 | # 15 | ## 16 | 17 | f="sort" 18 | 19 | x=$(echo "3 1 4 1 5 9 2 6" | "$num" $f) && 20 | assert_eq "1 1 2 3 4 5 6 9" "$x" "$f with row" 21 | 22 | x=$(echo "3\n1\n4\n1\n5\n9\n2\n6" | "$num" $f) && 23 | assert_eq "1 1 2 3 4 5 6 9" "$x" "$f with col" 24 | 25 | x=$(echo "3 1 4 1 5 9 2 6\n2 7 1 8 2 8 1 8" | "$num" $f records) && 26 | assert_eq "1 1 2 3 4 5 6 9"$'\n'"1 1 2 2 7 8 8 8" "$x" "$f with records" 27 | -------------------------------------------------------------------------------- /test/num-round-test.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | ############################################################################ 3 | # 4 | # num-round-test.sh 5 | # 6 | ## 7 | 8 | . minitest.sh 9 | num=${NUM:-num} 10 | 11 | ## 12 | # 13 | # round 14 | # 15 | ## 16 | 17 | f="round" 18 | 19 | x=$(echo "-1.9 1.9" | "$num" $f) && 20 | assert_eq "-2 2" "$x" "$f with row" 21 | 22 | x=$(echo "-1.9\n1.9" | "$num" $f) && 23 | assert_eq "-2 2" "$x" "$f with col" 24 | 25 | x=$(echo "-1.9 1.9\n-3.9 3.9" | "$num" $f records) && 26 | assert_eq "-2 2"$'\n'"-4 4" "$x" "$f with records" 27 | 28 | ## 29 | # 30 | # round-off 31 | # 32 | ## 33 | 34 | f="round-off" 35 | 36 | x=$(echo "-1.9 1.9" | "$num" $f) && 37 | assert_eq "-1 1" "$x" "$f with row" 38 | 39 | x=$(echo "-1.9\n1.9" | "$num" $f) && 40 | assert_eq "-1 1" "$x" "$f with col" 41 | 42 | x=$(echo "-1.9 1.9\n-3.9 3.9" | "$num" $f records) && 43 | assert_eq "-1 1"$'\n'"-3 3" "$x" "$f with records" 44 | 45 | ## 46 | # 47 | # round-up 48 | # 49 | ## 50 | 51 | f="round-up" 52 | 53 | x=$(echo "-1.9 1.9" | "$num" $f) && 54 | assert_eq "-1 2" "$x" "$f with row" 55 | 56 | x=$(echo "-1.9\n1.9" | "$num" $f) && 57 | assert_eq "-1 2" "$x" "$f with col" 58 | 59 | x=$(echo "-1.9 1.9\n -3.9 3.9" | "$num" $f records) && 60 | assert_eq "-1 2"$'\n'"-3 4" "$x" "$f with records" 61 | 62 | ## 63 | # 64 | # round-down 65 | # 66 | ## 67 | 68 | f="round-down" 69 | 70 | x=$(echo "-1.9 1.9" | "$num" $f) && 71 | assert_eq "-2 1" "$x" "$f with row" 72 | 73 | x=$(echo "-1.9\n1.9" | "$num" $f) && 74 | assert_eq "-2 1" "$x" "$f with col" 75 | 76 | x=$(echo "-1.9 1.9\n-3.9 3.9" | "$num" $f records) && 77 | assert_eq "-2 1"$'\n'"-4 3" "$x" "$f with records" 78 | -------------------------------------------------------------------------------- /test/num-separators-test.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | ############################################################################ 3 | # 4 | # num-separators-test.sh 5 | # 6 | ## 7 | 8 | . minitest.sh 9 | num=${NUM:-num} 10 | 11 | ## 12 | # 13 | # Separators: 14 | # 15 | # * input field separator (FS) 16 | # * input record separator (RS) 17 | # * output field separator (OFS) 18 | # * output record separator (ORS) 19 | # 20 | ## 21 | 22 | x=$(echo "1;2,3;4,5,6;7,8,9,0" | ../num n n n FS="," RS=";" OFS="-" ORS="~" records) && 23 | assert_eq "1-1-1~2-2-2~3-3-3~4-4-4" "$x" "separators" 24 | 25 | ## 26 | # 27 | # Separators for common needs: 28 | # 29 | # * comma-separated values (CSV) which use a comma and newline. 30 | # * tab-separated values (TSV) which use a tab and newline. 31 | # * unit-separated values (USV) which use Unicode unit separator and record separator. 32 | ## 33 | 34 | ## CSV 35 | 36 | x=$(echo "1\n2,3\n4,5,6\n7,8,9,0" | ../num n n n csv records) && 37 | assert_eq "1,1,1"$'\n'"2,2,2"$'\n'"3,3,3"$'\n'"4,4,4" "$x" "csv" 38 | 39 | x=$(echo "1\n2,3\n4,5,6\n7,8,9,0" | ../num n n n input-csv records) && 40 | assert_eq "1 1 1"$'\n'"2 2 2"$'\n'"3 3 3"$'\n'"4 4 4" "$x" "input-csv" 41 | 42 | x=$(echo "1\n2 3\n4 5 6\n7 8 9 0" | ../num n n n output-csv records) && 43 | assert_eq "1,1,1"$'\n'"2,2,2"$'\n'"3,3,3"$'\n'"4,4,4" "$x" "output-csv" 44 | 45 | ## TSV 46 | 47 | x=$(echo "1\n2\t3\n4\t5\t6\n7\t8\t9\t0" | ../num n n n tsv records) && 48 | assert_eq "1"$'\t'"1"$'\t'"1"$'\n'"2"$'\t'"2"$'\t'"2"$'\n'"3"$'\t'"3"$'\t'"3"$'\n'"4"$'\t'"4"$'\t'"4" "$x" "tsv" 49 | 50 | x=$(echo "1\n2\t3\n4\t5\t6\n7\t8\t9\t0" | ../num n n n input-tsv records) && 51 | assert_eq "1 1 1"$'\n'"2 2 2"$'\n'"3 3 3"$'\n'"4 4 4" "$x" "input-tsv" 52 | 53 | x=$(echo "1\n2 3\n4 5 6\n7 8 9 0" | ../num n n n output-tsv records) && 54 | assert_eq "1"$'\t'"1"$'\t'"1"$'\n'"2"$'\t'"2"$'\t'"2"$'\n'"3"$'\t'"3"$'\t'"3"$'\n'"4"$'\t'"4"$'\t'"4" "$x" "output-tsv" 55 | 56 | ## USV 57 | 58 | x=$(echo "1␞2␟3␞4␟5␟6␞7␟8␟9␟0" | ../num n n n usv records) && 59 | assert_eq "1␟1␟1␞2␟2␟2␞3␟3␟3␞4␟4␟4" "$x" "usv" 60 | 61 | x=$(echo "1␞2␟3␞4␟5␟6␞7␟8␟9␟0" | ../num n n n input-usv records) && 62 | assert_eq "1 1 1"$'\n'"2 2 2"$'\n'"3 3 3"$'\n'"4 4 4" "$x" "input-usv" 63 | 64 | x=$(echo "1\n2 3\n4 5 6\n7 8 9 0" | ../num n n n output-usv records) && 65 | assert_eq "1␟1␟1␞2␟2␟2␞3␟3␟3␞4␟4␟4" "$x" "output-usv" 66 | -------------------------------------------------------------------------------- /test/num-sign-test.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | ############################################################################ 3 | # 4 | # num-sign-test.awk 5 | # 6 | ## 7 | 8 | . minitest.sh 9 | num=${NUM:-num} 10 | 11 | ## 12 | # 13 | # sign 14 | # 15 | ## 16 | 17 | f="sign" 18 | 19 | x=$(echo "-8 0 8" | "$num" $f) && 20 | assert_eq "-1 0 1" "$x" "$f with row" 21 | 22 | x=$(echo "-8\n0\n8" | "$num" $f) && 23 | assert_eq "-1 0 1" "$x" "$f with col" 24 | 25 | x=$(echo "-8 0 8\n-9 0 9" | "$num" $f records) && 26 | assert_eq "-1 0 1"$'\n'"-1 0 1" "$x" "$f with records" 27 | -------------------------------------------------------------------------------- /test/num-skewness-test.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | ############################################################################ 3 | # 4 | # num-skewness-test.awk 5 | # 6 | ## 7 | 8 | . minitest.sh 9 | num=${NUM:-num} 10 | 11 | ## 12 | # 13 | # skewness 14 | # 15 | ## 16 | 17 | f="skewness" 18 | 19 | x=$(echo "1 2 4" | "$num" $f) && 20 | assert_eq 1.11111 "$x" "$f with row" 21 | 22 | x=$(echo "1\n2\n4" | "$num" $f) && 23 | assert_eq 1.11111 "$x" "$f with col" 24 | 25 | x=$(echo "1 2 4\n5 6 9" | "$num" $f records) && 26 | assert_eq "1.11111"$'\n'"3.88889" "$x" "$f with records" 27 | 28 | ## 29 | # 30 | # sample skewness 31 | # 32 | ## 33 | 34 | f="sample-skewness" 35 | 36 | x=$(echo "1 2 4" | "$num" $f) && 37 | assert_eq 1.11111 "$x" "$f with row" 38 | 39 | x=$(echo "1\n2\n4" | "$num" $f) && 40 | assert_eq 1.11111 "$x" "$f with col" 41 | 42 | x=$(echo "1 2 4\n5 6 9" | "$num" $f records) && 43 | assert_eq "1.11111"$'\n'"3.88889" "$x" "$f with records" 44 | 45 | ## 46 | # 47 | # population skewness 48 | # 49 | ## 50 | 51 | f="population-skewness" 52 | 53 | x=$(echo "1 2 4" | "$num" $f) && 54 | assert_eq 0.740741 "$x" "$f with row" 55 | 56 | x=$(echo "1\n2\n4" | "$num" $f) && 57 | assert_eq 0.740741 "$x" "$f with col" 58 | 59 | x=$(echo "1 2 4\n5 6 9" | "$num" $f records) && 60 | assert_eq "0.740741"$'\n'"2.59259" "$x" "$f with records" 61 | -------------------------------------------------------------------------------- /test/num-smoke-test.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | ############################################################################ 3 | # 4 | # num-smoke-test.awk 5 | # 6 | # The goal of this file is to test a bunch of functions in combination, 7 | # as a smoke test for errors if/when functions intefere with each other. 8 | # 9 | ## 10 | 11 | . minitest.sh 12 | num=${NUM:-num} 13 | 14 | x=$(echo "1 2 4" | "$num" n first last min max range sum product mean meanest mad trimean med var skew kurt sd covar) && 15 | assert_eq "3 1 4 1 4 3 7 8 2.33333 2 1.11111 2.1875 2 2.33333 1.11111 5.44444 1.52753 0.654654" "$x" "integration, with row" 16 | 17 | x=$(echo "1\n2\n4" | "$num" n first last min max range sum product mean meanest mad trimean med var skew kurt sd covar) && 18 | assert_eq "3 1 4 1 4 3 7 8 2.33333 2 1.11111 2.1875 2 2.33333 1.11111 5.44444 1.52753 0.654654" "$x" "integration, with col" 19 | -------------------------------------------------------------------------------- /test/num-sort-test.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | ############################################################################ 3 | # 4 | # num-sort-test.awk 5 | # 6 | ## 7 | 8 | . minitest.sh 9 | num=${NUM:-num} 10 | 11 | ## 12 | # 13 | # sort 14 | # 15 | ## 16 | 17 | f="sort" 18 | 19 | x=$(echo "3 1 2" | "$num" $f) && 20 | assert_eq "1 2 3" "$x" "$f with row" 21 | 22 | x=$(echo "3\n1\n2" | "$num" $f) && 23 | assert_eq "1 2 3" "$x" "$f with col" 24 | 25 | x=$(echo "3 1 2\n9 5 6" | "$num" $f records) && 26 | assert_eq "1 2 3"$'\n'"5 6 9" "$x" "$f with records" 27 | 28 | ## 29 | # 30 | # sort-ascending 31 | # 32 | ## 33 | 34 | f="sort-ascending" 35 | 36 | x=$(echo "3 1 2" | "$num" $f) && 37 | assert_eq "1 2 3" "$x" "$f with row" 38 | 39 | x=$(echo "3\n1\n2" | "$num" $f) && 40 | assert_eq "1 2 3" "$x" "$f with col" 41 | 42 | x=$(echo "3 1 2\n9 5 6" | "$num" $f records) && 43 | assert_eq "1 2 3"$'\n'"5 6 9" "$x" "$f with row" 44 | 45 | ## 46 | # 47 | # sort-descending 48 | # 49 | ## 50 | 51 | f="sort-descending" 52 | 53 | x=$(echo "3 1 2" | "$num" $f) && 54 | assert_eq "3 2 1" "$x" "$f with row" 55 | 56 | x=$(echo "3\n1\n2" | "$num" $f) && 57 | assert_eq "3 2 1" "$x" "$f with col" 58 | 59 | x=$(echo "3 1 2\n9 5 6" | "$num" $f records) && 60 | assert_eq "3 2 1"$'\n'"9 6 5" "$x" "$f with records" 61 | 62 | ## 63 | # 64 | # is-ascending 65 | # 66 | ## 67 | 68 | f="is-ascending" 69 | 70 | ## with ascending unique 71 | 72 | x=$(echo "1 2 3" | "$num" $f) && 73 | assert_eq $TRUE "$x" "$f with row, with ascending unique" 74 | 75 | x=$(echo "1\n2\n3" | "$num" $f) && 76 | assert_eq $TRUE "$x" "$f with col, with ascending unique" 77 | 78 | x=$(echo "1 2 3\n5 6 9" | "$num" $f records) && 79 | assert_eq "$TRUE"$'\n'"$TRUE" "$x" "$f with records, with ascending unique" 80 | 81 | ## with dup 82 | 83 | x=$(echo "2 2 2" | "$num" $f) && 84 | assert_eq $TRUE "$x" "$f with row, with dup" 85 | 86 | x=$(echo "2\n2\n2" | "$num" $f) && 87 | assert_eq $TRUE "$x" "$f with col, with dup" 88 | 89 | x=$(echo "2 2 2\n6 6 6" | "$num" $f records) && 90 | assert_eq "$TRUE"$'\n'"$TRUE" "$x" "$f with records, with dup" 91 | 92 | ## with descending 93 | 94 | x=$(echo "3 2 1" | "$num" $f) && 95 | assert_eq $FALSE "$x" "$f with row, with descending" 96 | 97 | x=$(echo "3\n2\n1" | "$num" $f) && 98 | assert_eq $FALSE "$x" "$f with col, with descending" 99 | 100 | x=$(echo "3 2 1\n9 6 5" | "$num" $f records) && 101 | assert_eq "$FALSE"$'\n'"$FALSE" "$x" "$f with records, with descending" 102 | 103 | ## 104 | # 105 | # is-strictly-ascending 106 | # 107 | ## 108 | 109 | f="is-strictly-ascending" 110 | 111 | ## with unique 112 | 113 | x=$(echo "1 2 3" | "$num" $f) && 114 | assert_eq $TRUE "$x" "$f with row, with unique" 115 | 116 | x=$(echo "1\n2\n3" | "$num" $f) && 117 | assert_eq $TRUE "$x" "$f with col, with unique" 118 | 119 | x=$(echo "1 2 3\n5 6 9" | "$num" $f records) && 120 | assert_eq "$TRUE"$'\n'"$TRUE" "$x" "$f with records, with unique" 121 | 122 | ## with dup 123 | 124 | x=$(echo "2 2 2" | "$num" $f) && 125 | assert_eq $FALSE "$x" "$f with row, with dup" 126 | 127 | x=$(echo "2\n2\n2" | "$num" $f) && 128 | assert_eq $FALSE "$x" "$f with col, with dup" 129 | 130 | x=$(echo "2 2 2\n6 6 6" | "$num" $f records) && 131 | assert_eq "$FALSE"$'\n'"$FALSE" "$x" "$f with records, with dup" 132 | 133 | ## with descending 134 | 135 | x=$(echo "3 2 1" | "$num" $f) && 136 | assert_eq $FALSE "$x" "$f with row, with descending" 137 | 138 | x=$(echo "3\n2\n1" | "$num" $f) && 139 | assert_eq $FALSE "$x" "$f with col, with descending" 140 | 141 | x=$(echo "3 2 1\n9 6 5" | "$num" $f records) && 142 | assert_eq "$FALSE"$'\n'"$FALSE" "$x" "$f with records, with descending" 143 | 144 | ## 145 | # 146 | # is-descending 147 | # 148 | ## 149 | 150 | f="is-descending" 151 | 152 | ## with unique 153 | 154 | x=$(echo "3 2 1" | "$num" $f) && 155 | assert_eq $TRUE "$x" "$f with row, with unique" 156 | 157 | x=$(echo "3\n2\n1" | "$num" $f) && 158 | assert_eq $TRUE "$x" "$f with col, with unique" 159 | 160 | x=$(echo "3 2 1\n9 6 5" | "$num" $f records) && 161 | assert_eq "$TRUE"$'\n'"$TRUE" "$x" "$f with records, with unique" 162 | 163 | ## with dup 164 | 165 | x=$(echo "2 2 2" | "$num" $f) && 166 | assert_eq $TRUE "$x" "$f with row, with dup" 167 | 168 | x=$(echo "2\n2\n2" | "$num" $f) && 169 | assert_eq $TRUE "$x" "$f with col, with dup" 170 | 171 | x=$(echo "2 2 2\n6 6 6" | "$num" $f records) && 172 | assert_eq "$TRUE"$'\n'"$TRUE" "$x" "$f with records, with dup" 173 | 174 | ## with ascending 175 | 176 | x=$(echo "1 2 3" | "$num" $f) && 177 | assert_eq $FALSE "$x" "$f with row, with ascending" 178 | 179 | x=$(echo "1\n2\n3" | "$num" $f) && 180 | assert_eq $FALSE "$x" "$f with col, with ascending" 181 | 182 | x=$(echo "1 2 3\n5 6 9" | "$num" $f records) && 183 | assert_eq "$FALSE"$'\n'"$FALSE" "$x" "$f with records, with ascending" 184 | 185 | ## 186 | # 187 | # is-strictly-descending 188 | # 189 | ## 190 | 191 | f="is-strictly-descending" 192 | 193 | ## with unique 194 | 195 | x=$(echo "3 2 1" | "$num" $f) && 196 | assert_eq $TRUE "$x" "$f with row, with unique" 197 | 198 | x=$(echo "3\n2\n1" | "$num" $f) && 199 | assert_eq $TRUE "$x" "$f with col, with unique" 200 | 201 | x=$(echo "3 2 1\n9 6 5" | "$num" $f records) && 202 | assert_eq "$TRUE"$'\n'"$TRUE" "$x" "$f with records, with unique" 203 | 204 | ## with dup 205 | 206 | x=$(echo "2 2 2" | "$num" $f) && 207 | assert_eq $FALSE "$x" "$f with row, with dup" 208 | 209 | x=$(echo "2\n2\n2" | "$num" $f) && 210 | assert_eq $FALSE "$x" "$f with col, with dup" 211 | 212 | x=$(echo "2 2 2\n6 6 6" | "$num" $f records) && 213 | assert_eq "$FALSE"$'\n'"$FALSE" "$x" "$f with records, with dup" 214 | 215 | ## with ascending 216 | 217 | x=$(echo "1 2 3" | "$num" $f) && 218 | assert_eq $FALSE "$x" "$f with row, with ascending" 219 | 220 | x=$(echo "1\n2\n3" | "$num" $f) && 221 | assert_eq $FALSE "$x" "$f with col, with ascending" 222 | 223 | x=$(echo "1 2 3\n5 6 9" | "$num" $f records) && 224 | assert_eq "$FALSE"$'\n'"$FALSE" "$x" "$f with records, with ascending" 225 | -------------------------------------------------------------------------------- /test/num-standard-deviation-test.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | ############################################################################ 3 | # 4 | # num-standard-deviation-test.sh 5 | # 6 | ## 7 | 8 | . minitest.sh 9 | num=${NUM:-num} 10 | 11 | ## 12 | # 13 | # standard deviation 14 | # 15 | ## 16 | 17 | f="standard-deviation" 18 | 19 | x=$(echo "1 2 4" | "$num" $f) && 20 | assert_eq 1.52753 "$x" "$f with row" 21 | 22 | x=$(echo "1\n2\n4" | "$num" $f) && 23 | assert_eq 1.52753 "$x" "$f with col" 24 | 25 | x=$(echo "1 2 4\n5 6 9" | "$num" $f records) && 26 | assert_eq "1.52753"$'\n'"2.08167" "$x" "$f with records" 27 | 28 | ## 29 | # 30 | # sample standard deviation 31 | # 32 | ## 33 | 34 | f="sample-standard-deviation" 35 | 36 | x=$(echo "1 2 4" | "$num" $f) && 37 | assert_eq 1.52753 "$x" "$f with row" 38 | 39 | x=$(echo "1\n2\n4" | "$num" $f) && 40 | assert_eq 1.52753 "$x" "$f with col" 41 | 42 | x=$(echo "1 2 4\n5 6 9" | "$num" standard-deviation records) && 43 | assert_eq "1.52753"$'\n'"2.08167" "$x" "$f with records" 44 | 45 | ## 46 | # 47 | # population standard deviation 48 | # 49 | ## 50 | 51 | f="population-standard-deviation" 52 | 53 | x=$(echo "1 2 4" | "$num" $f) && 54 | assert_eq 1.24722 "$x" "$f with row" 55 | 56 | x=$(echo "1\n2\n4" | "$num" $f) && 57 | assert_eq 1.24722 "$x" "$f with col" 58 | 59 | x=$(echo "1 2 4\n5 6 9" | "$num" $f records) && 60 | assert_eq "1.24722"$'\n'"1.69967" "$x" "$f with records" 61 | -------------------------------------------------------------------------------- /test/num-sum-of-mean-deviation-test.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | ############################################################################ 3 | # 4 | # num-sum-of-mean-deviation-test.sh 5 | # 6 | ## 7 | 8 | . minitest.sh 9 | num=${NUM:-num} 10 | 11 | ## 12 | # 13 | # sum-of-squares 14 | # 15 | ## 16 | 17 | f="sum-of-squares" 18 | 19 | x=$(echo "1 2 4" | "$num" $f) && 20 | assert_eq 4.66667 "$x" "$f with row" 21 | 22 | x=$(echo "1\n2\n4" | "$num" $f) && 23 | assert_eq 4.66667 "$x" "$f with col" 24 | 25 | x=$(echo "1 2 4\n5 6 9" | "$num" $f records) && 26 | assert_eq "4.66667"$'\n'"8.66667" "$x" "$f with records" 27 | 28 | ## 29 | # 30 | # sum-of-cubes 31 | # 32 | ## 33 | 34 | f="sum-of-cubes" 35 | 36 | x=$(echo "1 2 4" | "$num" $f) && 37 | assert_eq 2.22222 "$x" "$f with row" 38 | 39 | x=$(echo "1\n2\n4" | "$num" $f) && 40 | assert_eq 2.22222 "$x" "$f with col" 41 | 42 | x=$(echo "1 2 4\n5 6 9" | "$num" $f records) && 43 | assert_eq "2.22222"$'\n'"7.77778" "$x" "$f with records" 44 | 45 | ## 46 | # 47 | # sum-of-quads 48 | # 49 | ## 50 | 51 | f="sum-of-quads" 52 | 53 | x=$(echo "1 2 4" | "$num" $f) && 54 | assert_eq 10.8889 "$x" "$f with row" 55 | 56 | x=$(echo "1\n2\n4" | "$num" $f) && 57 | assert_eq 10.8889 "$x" "$f with col" 58 | 59 | x=$(echo "1 2 4\n5 6 9" | "$num" $f records) && 60 | assert_eq "10.8889"$'\n'"37.5556" "$x" "$f with records" 61 | -------------------------------------------------------------------------------- /test/num-sum-test.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | ############################################################################ 3 | # 4 | # num-sum-test.sh 5 | # 6 | ## 7 | 8 | . minitest.sh 9 | num=${NUM:-num} 10 | 11 | ## 12 | # 13 | # sum 14 | # 15 | ## 16 | 17 | f="sum" 18 | 19 | x=$(echo "1 2 4" | "$num" $f) && 20 | assert_eq 7 "$x" "$f with row" 21 | 22 | x=$(echo "1\n2\n4" | "$num" $f) && 23 | assert_eq 7 "$x" "$f with col" 24 | 25 | x=$(echo "1 2 4\n5 6 9" | "$num" $f records) && 26 | assert_eq "7"$'\n'"20" "$x" "$f with records" 27 | -------------------------------------------------------------------------------- /test/num-test: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | NUM=${NUM:-$(command -v num || echo "num")} 4 | for f in num-*-test.sh; do NUM="$NUM" ./$f; done 5 | -------------------------------------------------------------------------------- /test/num-trimean-test.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | ############################################################################ 3 | # 4 | # num-trimean-test.sh 5 | # 6 | ## 7 | 8 | . minitest.sh 9 | num=${NUM:-num} 10 | 11 | ## 12 | # 13 | # trimean 14 | # 15 | ## 16 | 17 | f="trimean" 18 | 19 | x=$(echo "1 2 3 4 99" | "$num" $f) && 20 | assert_eq 8.875 "$x" "$f with row" 21 | 22 | x=$(echo "1\n2\n3\n4\n99" | "$num" $f) && 23 | assert_eq 8.875 "$x" "$f with col" 24 | 25 | x=$(echo "1 2 3 4 99\n5 6 7 8 99" | "$num" $f records) && 26 | assert_eq "8.875"$'\n'"12.625" "$x" "$f with records" 27 | -------------------------------------------------------------------------------- /test/num-trimmed-mean-test.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | ############################################################################ 3 | # 4 | # num-trimmed-mean-test.sh 5 | # 6 | ## 7 | 8 | . minitest.sh 9 | num=${NUM:-num} 10 | 11 | ## 12 | # 13 | # trimmed-mean 14 | # 15 | # * q1 == 1.75 16 | # * q3 == 4.25 17 | ## 18 | 19 | f="trimmed-mean" 20 | 21 | x=$(echo "1 2 3 4 5" | "$num" $f) && 22 | assert_eq $TODO "$x" "$f with row" 23 | 24 | x=$(echo "1\n2\n3\n4\n5" | "$num" $f) && 25 | assert_eq $TODO "$x" "$f with col" 26 | 27 | x=$(echo "1 2 3 5\n1 2 3 4 5" | "$num" $f records) && 28 | assert_eq $TODO$'\n'$TODO "$x" "$f with records" 29 | -------------------------------------------------------------------------------- /test/num-unique-test.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | ############################################################################ 3 | # 4 | # num-unique-test.sh 5 | # 6 | ## 7 | 8 | . minitest.sh 9 | num=${NUM:-num} 10 | 11 | ## 12 | # 13 | # is-unique 14 | # 15 | ## 16 | 17 | f="is-unique" 18 | 19 | ## with unique 20 | 21 | x=$(echo "1 2 3" | "$num" $f) && 22 | assert_eq $TRUE "$x" "$f with row, with unique" 23 | 24 | x=$(echo "1\n2\n3" | "$num" $f) && 25 | assert_eq $TRUE "$x" "$f with col, with unique" 26 | 27 | x=$(echo "1 2 3\n5 6 9" | "$num" $f records) && 28 | assert_eq "$TRUE"$'\n'"$TRUE" "$x" "$f with records, with unique" 29 | 30 | ## with dup 31 | 32 | x=$(echo "1 2 2" | "$num" $f) && 33 | assert_eq $FALSE "$x" "$f with row, with dup" 34 | 35 | x=$(echo "1\n2\n2" | "$num" $f) && 36 | assert_eq $FALSE "$x" "$f with col, with dup" 37 | 38 | x=$(echo "1 2 2\n5 6 6" | "$num" $f records) && 39 | assert_eq "$FALSE"$'\n'"$FALSE" "$x" "$f with records, with dup" 40 | -------------------------------------------------------------------------------- /test/num-variance-test.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | ############################################################################ 3 | # 4 | # num-variance-test.sh 5 | # 6 | ## 7 | 8 | . minitest.sh 9 | num=${NUM:-num} 10 | 11 | ## 12 | # 13 | # variance 14 | # 15 | ## 16 | 17 | f="variance" 18 | 19 | x=$(echo "1 2 4" | "$num" $f) && 20 | assert_eq 2.33333 "$x" "$f with row" 21 | 22 | x=$(echo "1\n2\n4" | "$num" $f) && 23 | assert_eq 2.33333 "$x" "$f with col" 24 | 25 | x=$(echo "1 2 4\n5 6 9" | "$num" $f records) && 26 | assert_eq "2.33333"$'\n'"4.33333" "$x" "$f with records" 27 | 28 | ## 29 | # 30 | # sample variance 31 | # 32 | ## 33 | 34 | f="sample-variance" 35 | 36 | x=$(echo "1 2 4" | "$num" $f) && 37 | assert_eq 2.33333 "$x" "$f with row" 38 | 39 | x=$(echo "1\n2\n4" | "$num" $f) && 40 | assert_eq 2.33333 "$x" "$f with col" 41 | 42 | x=$(echo "1 2 4\n5 6 9" | "$num" $f records) && 43 | assert_eq "2.33333"$'\n'"4.33333" "$x" "$f with records" 44 | 45 | ## 46 | # 47 | # population variance 48 | # 49 | ## 50 | 51 | f="population-variance" 52 | 53 | x=$(echo "1 2 4" | "$num" $f) && 54 | assert_eq 1.55556 "$x" "$f with row" 55 | 56 | x=$(echo "1\n2\n4" | "$num" $f) && 57 | assert_eq 1.55556 "$x" "$f with col" 58 | 59 | x=$(echo "1 2 4\n5 6 9" | "$num" $f records) && 60 | assert_eq "1.55556"$'\n'"2.88889" "$x" "$f with records" 61 | -------------------------------------------------------------------------------- /todo/num-test-todo.sh: -------------------------------------------------------------------------------- 1 | 2 | x=$(echo "1 2 4" | ./num middle) && assert_eq 2 "$x" "middle with median exact" 3 | x=$(echo "1 2 3 4" | ./num middle) && assert_eq 2 "$x" "middle with median low" 4 | x=$(echo "1 2 4" | ./num mid) && assert_eq 2 "$x" "mid with median exact" 5 | x=$(echo "1 2 3 4" | ./num mid) && assert_eq 2 "$x" "mid with median low" 6 | 7 | ## Booleans 8 | 9 | #x=$(echo "1 2 3" | ./num is-linear) && assert_eq 1 "$x" "is-linear true with ascending" 10 | #x=$(echo "3 2 1" | ./num is-linear) && assert_eq 1 "$x" "is-linear true with descending" 11 | #x=$(echo "2 2 2" | ./num is-linear) && assert_eq 1 "$x" "is-linear true with all equals" 12 | #x=$(echo "1 2 4" | ./num is-linear) && assert_eq 0 "$x" "is-linear false" 13 | 14 | #x=$(echo "1 2 4" | ./num is-sorted) && assert_eq 1 "$x" "is-sorted true with ascending and uniques" 15 | #x=$(echo "1 2 2 4" | ./num is-sorted) && assert_eq 0 "$x" "is-sorted true with ascending and duplicates" 16 | #x=$(echo "4 2 1" | ./num is-sorted) && assert_eq 1 "$x" "is-sorted true with descending and uniques" 17 | #x=$(echo "4 2 2 1" | ./num is-sorted) && assert_eq 1 "$x" "is-sorted true with descending and duplicates" 18 | #x=$(echo "2 2 2" | ./num is-sorted) && assert_eq 1 "$x" "is-sorted true with all equals" 19 | #x=$(echo "1 4 2" | ./num is-sorted) && assert_eq 0 "$x" "is-sorted false" 20 | 21 | ## Filters 22 | 23 | #x=$(echo "1 2 2 3" | ./num unique) && assert_eq "1 2 3" "$x" "unique" 24 | #x=$(echo "1 2 2 3" | ./num effect-unique) && assert_eq "1 2 3" "$x" "effect-unique" 25 | #x=$(echo "1 2 2 3" | ./num effectunique) && assert_eq "1 2 3" "$x" "effectunique" 26 | #x=$(echo "1 2 2 3" | ./num select-unique) && assert_eq "1 3" "$x" "select-unique" 27 | #x=$(echo "1 2 2 3" | ./num selectunique) && assert_eq "1 3" "$x" "selectunique" 28 | #x=$(echo "1 2 2 3" | ./num reject-unique) && assert_eq "2" "$x" "reject-unique" 29 | #x=$(echo "1 2 2 3" | ./num rejectunique) && assert_eq "2" "$x" "rejectunique" 30 | 31 | #x=$(echo "3 1 2" | ./num sort-ascending) && assert_eq "1 2 3" "$x" "sort-ascending" 32 | #x=$(echo "3 1 2" | ./num sortascending) && assert_eq "1 2 3" "$x" "sortascending" 33 | #x=$(echo "3 1 2" | ./num sort-asc) && assert_eq "1 2 3" "$x" "sort-asc" 34 | #x=$(echo "3 1 2" | ./num sortasc) && assert_eq "1 2 3" "$x" "sortasc" 35 | #x=$(echo "3 1 2" | ./num sort-descending) && assert_eq "3 2 1" "$x" "sort-descending" 36 | #x=$(echo "3 1 2" | ./num sortdescending) && assert_eq "3 2 1" "$x" "sortdescending" 37 | #x=$(echo "3 1 2" | ./num sort-desc) && assert_eq "3 2 1" "$x" "sort-desc" 38 | #x=$(echo "3 1 2" | ./num sortdesc) && assert_eq "3 2 1" "$x" "sortdesc" 39 | 40 | ## Input 41 | 42 | #x=$(echo "1 2\n3 4" | ./num input-fields sum) && assert_eq "3\n7" "$x" "input-fields" 43 | #x=$(echo "1 2\n3 4" | ./num inputfields sum) && assert_eq "3\n7" "$x" "inputfields" 44 | 45 | #x=$(echo "1 2\n3 4" | ./num input-records sum) && assert_eq "21" "$x" "input-records" 46 | #x=$(echo "1 2\n3 4" | ./num inputrecords sum) && assert_eq "21" "$x" "inputrecords" 47 | 48 | #x=$(echo "1 2 4" | ./num input-is-linear && assert_eq "?" "$x" "input-is-linear" 49 | #x=$(echo "1 2 4" | ./num inputislinear) && assert_eq "?" "$x" "inputislinear" 50 | #x=$(echo "1 2 4" | ./num input-linear) && assert_eq "?" "$x" "input-linear" 51 | #x=$(echo "1 2 4" | ./num inputlinear) && assert_eq "?" "$x" "inputlinear" 52 | 53 | #x=$(echo "1 2 4" | ./num input-is-sorted && assert_eq "?" "$x" "input-is-sorted" 54 | #x=$(echo "1 2 4" | ./num inputissorted) && assert_eq "?" "$x" "inputissorted" 55 | #x=$(echo "1 2 4" | ./num input-sorted) && assert_eq "?" "$x" "input-sorted" 56 | #x=$(echo "1 2 4" | ./num inputsorted) && assert_eq "?" "$x" "inputsorted" 57 | 58 | #x=$(echo "1 2 4" | ./num input-is-unique) && assert_eq "?" "$x" "input-is-unique" 59 | #x=$(echo "1 2 4" | ./num inputisunique) && assert_eq "?" "$x" "inputisunique" 60 | #x=$(echo "1 2 4" | ./num input-unique) && assert_eq "?" "$x" "input-unique" 61 | #x=$(echo "1 2 4" | ./num inputunique) && assert_eq "?" "$x" "inputunique" 62 | 63 | ## Output 64 | 65 | #x=$(echo "1 2 4" | ./num n min max output-label) && assert_eq "n 3 minimum 1 maximum 4" "$x" "output-label" 66 | #x=$(echo "1 2 4" | ./num n min max outputlabel) && assert_eq "n 3 minimum 1 maximum 4" "$x" "outputlabel" 67 | -------------------------------------------------------------------------------- /todo/num-trig.awk: -------------------------------------------------------------------------------- 1 | ############################################################################ 2 | # 3 | # num-trig.awk 4 | # 5 | ## 6 | 7 | # These trig functions are thanks to: 8 | # http://www2.keck.hawaii.edu/inst/deimos/procs/calc 9 | # 10 | # abs(x) = absolute value of x 11 | # nint(x) = nearest integer 12 | # log10(x) = common logarithm of x 13 | # 14 | # sind(x) = sine of x (x in degrees) 15 | # cosd(x) = cosine of x (x in degrees) 16 | # 17 | # asin(x) = inverse sine of x (result in radians) 18 | # acos(x) = inverse cosine of x (result in radians) 19 | # atan(x) = inverse tangent of x (result in radians) 20 | # asind(x) = inverse sine of x (result in degrees) 21 | # acosd(x) = inverse cosine of x (result in degrees) 22 | # atan2d(x,y) = inverse tangent of x,y (result in degrees) 23 | 24 | function cosd(x, pi){ pi=atan2(0,-1) ; return cos(x*pi/180.) } 25 | function sind(x, pi){ pi=atan2(0,-1) ; return sin(x*pi/180.) } 26 | function log10(x){ return log(x)/log(10) } 27 | function asin(x){ return atan2(x,(1.-x^2)^0.5) } 28 | function acos(x){ return atan2((1.-x^2)^0.5,x) } 29 | function atan(x){ return atan2(x,1) } 30 | function asind(x, pi,rad){ pi=atan2(0,-1) ; rad=atan2(x,(1.-x^2)^0.5) ; return rad*180./pi } 31 | function acosd(x, pi,rad){ pi=atan2(0,-1) ; rad=atan2((1.-x^2)^0.5,x) ; return rad*180./pi } 32 | function atan2d(x,y, pi,rad){ pi=atan2(0,-1) ; rad=atan2(x,y) ; return rad*180./pi } 33 | --------------------------------------------------------------------------------