diff options
| -rw-r--r-- | doc/logic.md | 2 | ||||
| -rw-r--r-- | docs/bqn.js | 45 | ||||
| -rw-r--r-- | docs/doc/logic.html | 2 | ||||
| -rw-r--r-- | docs/spec/system.html | 3 | ||||
| -rw-r--r-- | spec/system.md | 4 |
5 files changed, 48 insertions, 8 deletions
diff --git a/doc/logic.md b/doc/logic.md index 793699f1..53e9409e 100644 --- a/doc/logic.md +++ b/doc/logic.md @@ -98,7 +98,7 @@ Some other logical identities don't always hold. For example, in boolean logic A ### Why not GCD and LCM? -APL provides [GCD](https://aplwiki.com/wiki/GCD) and [LCM](https://aplwiki.com/wiki/LCM) as extensions of And and Or, while BQN doesn't make these functions primitives. The main reason for omitting them functions is that they are complicated and, when applied to real or complex numbers, require a significant number of design decisions where there's no obvious choice (for example, whether to use comparison tolerance). On the other hand, these functions are fairly easy to implement, which allows the programmer to control the details, and also add functionality such as the extended GCD. Possible implementations for GCD and LCM are shown in [bqncrate](https://mlochbaum.github.io/bqncrate) ([GCD](https://mlochbaum.github.io/bqncrate/?q=gcd), [LCM](https://mlochbaum.github.io/bqncrate/?q=lcm)). +APL provides [GCD](https://aplwiki.com/wiki/GCD) and [LCM](https://aplwiki.com/wiki/LCM) as extensions of And and Or, while BQN doesn't make these functions primitives. The main reason for omitting them functions is that they are complicated and, when applied to real or complex numbers, require a significant number of design decisions where there's no obvious choice (for example, whether to use comparison tolerance). On the other hand, these functions are fairly easy to implement, which allows the programmer to control the details, and also add functionality such as the extended GCD. Possible implementations for GCD and LCM are shown in [bqncrate](https://mlochbaum.github.io/bqncrate) ([GCD](https://mlochbaum.github.io/bqncrate/?q=gcd), [LCM](https://mlochbaum.github.io/bqncrate/?q=lcm)), and `•math.GCD` and `•math.LCM` are also supported. A secondary reason is that the GCD falls short as an extension of Or, because its identity value 0 is not total. `0∨x`, for a real number `x`, is actually equal to `|x` and not `x`: for example, `0∨¯2` is `2` in APL. This means the identity `0∨x ←→ x` isn't reliable in APL. diff --git a/docs/bqn.js b/docs/bqn.js index c5e0b475..795f4fba 100644 --- a/docs/bqn.js +++ b/docs/bqn.js @@ -38,7 +38,6 @@ let makens = (keys, vals) => { let n = Array(keys.length).fill().map((_,i)=>i); n.names=keys.map(k=>k.toLowerCase()); vals.ns=n; return vals; } -let obj2ns = (obj, keys, f) => makens(keys, keys.map(k=>(f?f:(v=>v))(obj[k]))); let listkeys = x => { let s=x.ns, k=Object.keys(s).filter(n=>!isNaN(n)); return k.map(n=>s.names[+n]).sort(); @@ -661,16 +660,52 @@ let primitives = dynsys(state => { return list(gl.map((g,i) => list([g,rt[i]]))); }); +let isint = n => isnum(n) && n===(n|0); +let isnat = n => isint(n) && n>=0; +let fact = (x,w) => { + if (has(w)) throw Error("•math.Fact: Left argument not allowed"); + if (!isnat(x)) throw Error("•math.Fact: Argument other than a natural number not yet supported"); + let p = 1; while (x>0 && p<Infinity) { p*=x; x--; } + return p; +} +let comb = (x,w) => { + if (!has(w)) throw Error("•math.Comb: Left argument required"); + if (!(isint(w) && isint(x))) throw Error("•math.Comb: Non-integer arguments not yet supported"); + let n=w, k=Math.min(x, n-x); + let sgn = 1; + if (n >= 0) { + if (k<0) return 0; + } else { + let j=n-k; if (j<0) return 0; if (j&1) sgn = -1; + let t = Math.min(j, -1-n); n = -1-k; k = t; + } + if (k > 514) return Infinity; + let p = 1; + for (let i=0; i<k; i++) { + p*= (n-i) / (k-i); + if (p === Infinity) return sgn*p; + } + return sgn * Math.round(p); +} +let gcd = (x,w) => { + if (!has(w)) throw Error("•math.GCD: Left argument required"); + if (!(isnat(w) && isnat(x))) throw Error("•math.GCD: Arguments other than natural numbers not yet supported"); + while (w) { let t=w; w=x%w; x=t; } + return x; +} +let lcm = (x,w) => w===0 ? 0 : (w / gcd(x,w)) * x; +let pervfn = f => { f.prim=null; return runtime[61](f,0); } // ⚇ let mathfn = f => { - f.prim=null; let p=runtime[61](f,0); // ⚇ + let p=pervfn(f); return f!==Math.atan2 && f!==Math.hypot ? ((x,w) => {if ( has(w)) throw Error("Left argument not allowed"); return p(x);}) : ((x,w) => {if (!has(w)) throw Error("Left argument required"); return p(x,w);}); } let trig = "cos cosh sin sinh tan tanh".split(" "); -let mathns = obj2ns(Math, - trig.concat(trig.map(n=>"a"+n),"cbrt expm1 hypot log10 log1p log2 round trunc atan2".split(" ")), - f=>typeof f==="function"?mathfn(f):f +let mathkeys = trig.concat(trig.map(n=>"a"+n),"cbrt expm1 hypot log10 log1p log2 round trunc atan2".split(" ")); +let mathns = makens( + mathkeys.concat(["fact","comb","gcd","lcm"]), + mathkeys.map(k=>mathfn(Math[k])).concat([fact,comb,gcd,lcm].map(pervfn)) ); trig.map((_,i)=>{let f=mathns[i],g=mathns[i+trig.length]; f.inverse=g; g.inverse=f;}); diff --git a/docs/doc/logic.html b/docs/doc/logic.html index 9b18b817..c1490385 100644 --- a/docs/doc/logic.html +++ b/docs/doc/logic.html @@ -132,6 +132,6 @@ <p>It's not hard to prove that the bilinear extensions have these identity values. Of course <code><span class='Number'>1</span><span class='Function'>∧</span><span class='Value'>x</span></code> is <code><span class='Number'>1</span><span class='Function'>×</span><span class='Value'>x</span></code>, or <code><span class='Value'>x</span></code>, and <code><span class='Number'>0</span><span class='Function'>∨</span><span class='Value'>x</span></code> is <code><span class='Number'>0</span><span class='Function'>×</span><span class='Modifier2'>⌾</span><span class='Function'>¬</span><span class='Value'>x</span></code>, or <code><span class='Function'>¬</span><span class='Number'>1</span><span class='Function'>׬</span><span class='Value'>x</span></code>, giving <code><span class='Function'>¬¬</span><span class='Value'>x</span></code> or <code><span class='Value'>x</span></code> again. Both functions are commutative, so these values are identities on the right as well.</p> <p>Some other logical identities don't always hold. For example, in boolean logic And distributes over Or and vice-versa: <code><span class='Value'>a</span><span class='Function'>∧</span><span class='Value'>b</span><span class='Function'>∨</span><span class='Value'>c</span> <span class='Gets'>←→</span> <span class='Paren'>(</span><span class='Value'>a</span><span class='Function'>∧</span><span class='Value'>b</span><span class='Paren'>)</span><span class='Function'>∨</span><span class='Paren'>(</span><span class='Value'>a</span><span class='Function'>∧</span><span class='Value'>c</span><span class='Paren'>)</span></code>. But substituting <code><span class='Function'>×</span></code> for <code><span class='Function'>∧</span></code> and <code><span class='Function'>+-×</span></code> for <code><span class='Function'>∨</span></code> we find that the left hand side is <code><span class='Paren'>(</span><span class='Value'>a</span><span class='Function'>×</span><span class='Value'>b</span><span class='Paren'>)</span><span class='Function'>+</span><span class='Paren'>(</span><span class='Value'>a</span><span class='Function'>×</span><span class='Value'>c</span><span class='Paren'>)</span><span class='Function'>+</span><span class='Paren'>(</span><span class='Value'>a</span><span class='Function'>×</span><span class='Value'>b</span><span class='Function'>×</span><span class='Value'>c</span><span class='Paren'>)</span></code> while the right gives <code><span class='Paren'>(</span><span class='Value'>a</span><span class='Function'>×</span><span class='Value'>b</span><span class='Paren'>)</span><span class='Function'>+</span><span class='Paren'>(</span><span class='Value'>a</span><span class='Function'>×</span><span class='Value'>c</span><span class='Paren'>)</span><span class='Function'>+</span><span class='Paren'>(</span><span class='Value'>a</span><span class='Function'>×</span><span class='Value'>b</span><span class='Function'>×</span><span class='Value'>a</span><span class='Function'>×</span><span class='Value'>c</span><span class='Paren'>)</span></code>. These are equivalent for arbitrary <code><span class='Value'>b</span></code> and <code><span class='Value'>c</span></code> only if <code><span class='Value'>a</span><span class='Function'>=</span><span class='Value'>a</span><span class='Function'>×</span><span class='Value'>a</span></code>, that is, <code><span class='Value'>a</span></code> is 0 or 1. In terms of probabilities the difference when <code><span class='Value'>a</span></code> is not boolean is caused by failure of independence. On the left hand side, the two arguments of every logical function are independent. On the right hand side, each pair of arguments to <code><span class='Function'>∧</span></code> are independent, but the two arguments to <code><span class='Function'>∨</span></code>, <code><span class='Value'>a</span><span class='Function'>∧</span><span class='Value'>b</span></code> and <code><span class='Value'>a</span><span class='Function'>∧</span><span class='Value'>c</span></code>, are not. The relationship between these arguments means that logical equivalences no longer apply.</p> <h3 id="why-not-gcd-and-lcm"><a class="header" href="#why-not-gcd-and-lcm">Why not GCD and LCM?</a></h3> -<p>APL provides <a href="https://aplwiki.com/wiki/GCD">GCD</a> and <a href="https://aplwiki.com/wiki/LCM">LCM</a> as extensions of And and Or, while BQN doesn't make these functions primitives. The main reason for omitting them functions is that they are complicated and, when applied to real or complex numbers, require a significant number of design decisions where there's no obvious choice (for example, whether to use comparison tolerance). On the other hand, these functions are fairly easy to implement, which allows the programmer to control the details, and also add functionality such as the extended GCD. Possible implementations for GCD and LCM are shown in <a href="https://mlochbaum.github.io/bqncrate">bqncrate</a> (<a href="https://mlochbaum.github.io/bqncrate/?q=gcd">GCD</a>, <a href="https://mlochbaum.github.io/bqncrate/?q=lcm">LCM</a>).</p> +<p>APL provides <a href="https://aplwiki.com/wiki/GCD">GCD</a> and <a href="https://aplwiki.com/wiki/LCM">LCM</a> as extensions of And and Or, while BQN doesn't make these functions primitives. The main reason for omitting them functions is that they are complicated and, when applied to real or complex numbers, require a significant number of design decisions where there's no obvious choice (for example, whether to use comparison tolerance). On the other hand, these functions are fairly easy to implement, which allows the programmer to control the details, and also add functionality such as the extended GCD. Possible implementations for GCD and LCM are shown in <a href="https://mlochbaum.github.io/bqncrate">bqncrate</a> (<a href="https://mlochbaum.github.io/bqncrate/?q=gcd">GCD</a>, <a href="https://mlochbaum.github.io/bqncrate/?q=lcm">LCM</a>), and <code><span class='Value'>•math.</span><span class='Function'>GCD</span></code> and <code><span class='Value'>•math.</span><span class='Function'>LCM</span></code> are also supported.</p> <p>A secondary reason is that the GCD falls short as an extension of Or, because its identity value 0 is not total. <code><span class='Number'>0</span><span class='Function'>∨</span><span class='Value'>x</span></code>, for a real number <code><span class='Value'>x</span></code>, is actually equal to <code><span class='Function'>|</span><span class='Value'>x</span></code> and not <code><span class='Value'>x</span></code>: for example, <code><span class='Number'>0</span><span class='Function'>∨</span><span class='Number'>¯2</span></code> is <code><span class='Number'>2</span></code> in APL. This means the identity <code><span class='Number'>0</span><span class='Function'>∨</span><span class='Value'>x</span> <span class='Gets'>←→</span> <span class='Value'>x</span></code> isn't reliable in APL.</p> <p>Unrelatedly, the reason BQN discards APL's <code><span class='Value'>~</span></code> for negation is that it looks like <code><span class='Modifier'>˜</span></code>, and is less common in mathematics today.</p> diff --git a/docs/spec/system.html b/docs/spec/system.html index 5e076378..9b43e094 100644 --- a/docs/spec/system.html +++ b/docs/spec/system.html @@ -601,9 +601,10 @@ <p>More accurately the modifier <code><span class='Modifier2'>•_maxTime_</span></code> <em>may</em> fail if execution of <code><span class='Function'>𝔽</span></code> takes over <code><span class='Value'>𝕨</span><span class='Function'>𝔾</span><span class='Value'>𝕩</span></code> seconds, and should fail as quickly as it is practically able to. The most likely way to implement this modifier is to interrupt execution at the given time. If <code><span class='Function'>𝔽</span></code> completes before the interrupt there is no need to measure the amount of time it actually took.</p> <h2 id="math"><a class="header" href="#math">Math</a></h2> <p>System namespace <code><span class='Value'>•math</span></code> contains mathematical utilities that are not easily implemented with basic arithmetic, analogous to C's <code><span class='Value'>math.h</span></code>.</p> -<p>Constants <code><span class='Value'>ln10</span><span class='Gets'>⇐</span><span class='Function'>⋆</span><span class='Modifier'>⁼</span><span class='Number'>10</span></code>, <code><span class='Value'>ln2</span><span class='Gets'>⇐</span><span class='Function'>⋆</span><span class='Modifier'>⁼</span><span class='Number'>2</span></code>, <code><span class='Value'>log10e</span><span class='Gets'>⇐</span><span class='Function'>÷⋆</span><span class='Modifier'>⁼</span><span class='Number'>10</span></code>, <code><span class='Value'>log2e</span><span class='Gets'>⇐</span><span class='Function'>÷⋆</span><span class='Modifier'>⁼</span><span class='Number'>2</span></code> computed in full precision.</p> <p>Other correctly-rounded arithmetic: monadic <code><span class='Function'>Cbrt</span><span class='Gets'>⇐</span><span class='Number'>3</span><span class='Modifier2'>⊸</span><span class='Function'>√</span></code>, <code><span class='Function'>Log2</span><span class='Gets'>⇐</span><span class='Number'>2</span><span class='Function'>⋆</span><span class='Modifier'>⁼</span><span class='Function'>⊢</span></code>, <code><span class='Function'>Log10</span><span class='Gets'>⇐</span><span class='Number'>10</span><span class='Function'>⋆</span><span class='Modifier'>⁼</span><span class='Function'>⊢</span></code>, <code><span class='Function'>Log1p</span><span class='Gets'>⇐</span><span class='Function'>⋆</span><span class='Modifier'>⁼</span><span class='Number'>1</span><span class='Modifier2'>⊸</span><span class='Function'>+</span></code>, <code><span class='Function'>Expm1</span><span class='Gets'>⇐</span><span class='Number'>1</span><span class='Function'>-</span><span class='Modifier'>˜</span><span class='Function'>⋆</span></code>; dyadic <code><span class='Function'>Hypot</span><span class='Gets'>⇐</span><span class='Function'>+</span><span class='Modifier2'>⌾</span><span class='Paren'>(</span><span class='Function'>×</span><span class='Modifier'>˜</span><span class='Paren'>)</span></code>.</p> <p>Standard trigonometric functions <code><span class='Function'>Sin</span></code>, <code><span class='Function'>Cos</span></code>, <code><span class='Function'>Tan</span></code>, <code><span class='Function'>Sinh</span></code>, <code><span class='Function'>Cosh</span></code>, <code><span class='Function'>Tanh</span></code>, with inverses preceded by <code><span class='Value'>a</span></code> (<code><span class='Function'>ASin</span></code>, etc.) and accessable with <code><span class='Modifier'>⁼</span></code>. Additionally, the dyadic function <code><span class='Function'>ATan2</span></code> giving the angle of vector <code><span class='Value'>𝕨</span><span class='Ligature'>‿</span><span class='Value'>𝕩</span></code> relative to <code><span class='Number'>1</span><span class='Ligature'>‿</span><span class='Number'>0</span></code>. All trig functions measure angles in radians.</p> +<p>Special functions <code><span class='Function'>Fact</span></code> and <code><span class='Function'>LogFact</span></code> giving the factorial and its natural logarithm, possibly generalized to reals as the gamma function Γ(1+𝕩), and <code><span class='Function'>Comb</span></code> giving the binomial function "<code><span class='Value'>𝕨</span></code> choose <code><span class='Value'>𝕩</span></code>". Also the error function <code><span class='Function'>Erf</span></code> and its complement <code><span class='Function'>ErfC</span></code>. The implementations <code><span class='Function'>LogFact</span> <span class='Gets'>←</span> <span class='Function'>⋆</span><span class='Modifier'>⁼</span><span class='Function'>Fact</span></code> and <code><span class='Function'>ErfC</span> <span class='Gets'>←</span> <span class='Number'>1</span><span class='Function'>-Erf</span></code> are mathematically correct but these two functions should support greater precision for a large argument.</p> +<p>The greatest common divison <code><span class='Function'>GCD</span></code> and least common multiple <code><span class='Function'>LCM</span></code> of two numbers. Behavior for arguments other than natural numbers is not yet specified.</p> <h2 id="random-generation"><a class="header" href="#random-generation">Random generation</a></h2> <p><code><span class='Function'>•MakeRand</span></code> initializes a deterministic pseudorandom number generator with seed value <code><span class='Value'>𝕩</span></code>. <code><span class='Value'>•rand</span></code>, if it exists, is a globally accessible generator initialized at first use; this initialization should use randomness from an outside source if available. These random generators aren't required to be cryptographically secure and should always be treated as insecure. A random generator has the following member functions:</p> <table> diff --git a/spec/system.md b/spec/system.md index 0d6cffbf..5ba463bb 100644 --- a/spec/system.md +++ b/spec/system.md @@ -291,6 +291,10 @@ Other correctly-rounded arithmetic: monadic `Cbrt⇐3⊸√`, `Log2⇐2⋆⁼⊢ Standard trigonometric functions `Sin`, `Cos`, `Tan`, `Sinh`, `Cosh`, `Tanh`, with inverses preceded by `a` (`ASin`, etc.) and accessable with `⁼`. Additionally, the dyadic function `ATan2` giving the angle of vector `𝕨‿𝕩` relative to `1‿0`. All trig functions measure angles in radians. +Special functions `Fact` and `LogFact` giving the factorial and its natural logarithm, possibly generalized to reals as the gamma function Γ(1+𝕩), and `Comb` giving the binomial function "`𝕨` choose `𝕩`". Also the error function `Erf` and its complement `ErfC`. The implementations `LogFact ← ⋆⁼Fact` and `ErfC ← 1-Erf` are mathematically correct but these two functions should support greater precision for a large argument. + +The greatest common divison `GCD` and least common multiple `LCM` of two numbers. Behavior for arguments other than natural numbers is not yet specified. + ## Random generation `•MakeRand` initializes a deterministic pseudorandom number generator with seed value `𝕩`. `•rand`, if it exists, is a globally accessible generator initialized at first use; this initialization should use randomness from an outside source if available. These random generators aren't required to be cryptographically secure and should always be treated as insecure. A random generator has the following member functions: |
