Download
214 lines 7.2 KiBNomsuModified 2019-03-27 raw
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215
#!/usr/bin/env nomsu -V7.0.0
###
    This file defines some common math literals and functions
    
use "core/metaprogramming"
use "core/text"
use "core/operators"
use "core/control_flow"
use "core/collections"

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

external:
    ### Literals:
    test:
        unless (all of [inf, NaN, pi, tau, golden ratio, e]):
            fail "math constants failed"
        $nan = (NaN)
        unless ($nan != $nan):
            fail "NaN failed"
    
    [infinity, inf] all compile to "math.huge"
    [not a number, NaN, nan] all compile to "(0/0)"
    [pi, Pi, PI] all compile to "math.pi"
    [tau, Tau, TAU] all compile to "(2*math.pi)"
    (golden ratio) compiles to "((1+math.sqrt(5))/2)"
    (e) compiles to "math.exp(1)"
    
    ### Functions:
    test:
        assume (("5" as a number) == 5)
    $($ as a number) = $(tonumber $)
    $($ as number) = $(tonumber $)
    test:
        unless
            all of [
                abs 5, | 5 |, sqrt 5, √5, sine 5, cosine 5, tangent 5, arc sine 5, arc cosine 5
                arc tangent 5, arc tangent 5 / 10, hyperbolic sine 5, hyperbolic cosine 5
                hyperbolic tangent 5, e^ 5, ln 5, log 5 base 2, floor 5, ceiling 5, round 5
            ]
        ..:
            fail "math functions failed"
    
    [$(absolute value $), $(absolute value of $), $(| $ |), $(abs $)] =
        [$math.abs, $math.abs, $math.abs, $math.abs]
    
    [$(square root $), $(square root of $), $(√$), $(sqrt $)] =
        [$math.sqrt, $math.sqrt, $math.sqrt, $math.sqrt]
    
    [$(sine $), $(sin $)] = [$math.sin, $math.sin]
    [$(cosine $), $(cos $)] = [$math.cos, $math.cos]
    [$(tangent $), $(tan $)] = [$math.tan, $math.tan]
    [$(arc sine $), $(asin $)] = [$math.asin, $math.asin]
    [$(arc cosine $), $(acos $)] = [$math.acos, $math.acos]
    [$(arc tangent $), $(atan $)] = [$math.atan, $math.atan]
    [$(arc tangent $y / $x), $(atan2 $y $x)] = [$math.atan2, $math.atan2]
    [$(hyperbolic sine $), $(sinh $)] = [$math.sinh, $math.sinh]
    [$(hyperbolic cosine $), $(cosh $)] = [$math.cosh, $math.cosh]
    [$(hyperbolic tangent $), $(tanh $)] = [$math.tanh, $math.tanh]
    [$(e^ $), $(exp $)] = [$math.exp, $math.exp]
    [$(natural log $), $(ln $), $(log $), $(log $ base $)] =
        [$math.log, $math.log, $math.log, $math.log]
    $(floor $) = $math.floor
    [$(ceiling $), $(ceil $)] = [$math.ceil, $math.ceil]
    [round $, $ rounded] all mean (floor ($ + 0.5))
    test:
        unless ((463 to the nearest 100) == 500): fail "rounding failed"
        unless ((2.6 to the nearest 0.25) == 2.5): fail "rounding failed"
    ($n to the nearest $rounder) means ($rounder * (floor ($n / $rounder + 0.5)))
    
    ### Any/all
    [all of $items, all $items] all mean:
        for $ in $items:
            unless $:
                return (no)
        
        return (yes)
    [not all of $items, not all $items] all parse as (not (all of $items))
    [any of $items, any $items] all mean:
        for $ in $items:
            if $:
                return (yes)
        
        return (no)
    [none of $items, none $items] all parse as (not (any of $items))
    
    ### Sum/product
    [sum of $items, sum $items] all mean:
        $total = 0
        for $ in $items:
            $total += $
        return $total
    
    [product of $items, product $items] all mean:
        $prod = 1
        for $ in $items:
            $prod *= $
        return $prod
    
    [avg of $items, average of $items] all mean ((sum of $items) / #$items)
    
    ### Min/max
    [min of $items, smallest of $items, lowest of $items] all mean:
        $best = (nil)
        for $ in $items:
            if (($best == (nil)) or ($ < $best)): $best = $
        return $best
    
    [max of $items, biggest of $items, largest of $items, highest of $items] all mean:
        $best = (nil)
        for $ in $items:
            if (($best == (nil)) or ($ > $best)): $best = $
        return $best
    
    test:
        assume ((min of [3, -4, 1, 2] by $ = ($ * $)) == 1)
        assume ((max of [3, -4, 1, 2] by $ = ($ * $)) == -4)
    
    (min of $items by $item = $value_expr) parses as
        result of:
            $best = (nil)
            $best_key = (nil)
            for $item in $items:
                $key = $value_expr
                if (($best == (nil)) or ($key < $best_key)):
                    $best = $item
                    $best_key = $key
            
            return $best
    
    (max of $items by $item = $value_expr) parses as
        result of:
            $best = (nil)
            $best_key = (nil)
            for $item in $items:
                $key = $value_expr
                if (($best == (nil)) or ($key > $best_key)):
                    $best = $item
                    $best_key = $key
            
            return $best
    
    test:
        assume (100 clamped between 0 and 10) == 10
    
    ($ clamped between $min and $max) means:
        when:
            ($ < $min):
                return $min
            
            ($ > $max):
                return $max
            
            else:
                return $
    
    test:
        assume (-0.1 smoothed by 2.7) == 0
        assume (0 smoothed by 2.7) == 0
        assume (0.5 smoothed by 2.7) == 0.5
        assume (1 smoothed by 2.7) == 1
        assume (1.1 smoothed by 2.7) == 1
    
    ($ smoothed by $smoothness) means:
        $ = ($ clamped between 0 and 1)
        if ($smoothness == 0): return $
        $k = (2 ^ $smoothness)
        if ($ < 0.5):
            return (0.5 * (2 * $) ^ $k)
        ..else:
            return (1 - 0.5 * (2 - 2 * $) ^ $k)
    
    test:
        assume (5 to 7 mixed by -1) == 5
        assume (5 to 7 mixed by 0) == 5
        assume (5 to 7 mixed by 0.5) == 6
        assume (5 to 7 mixed by 1) == 7
        assume (5 to 7 mixed by 2) == 7
    
    ($lo to $hi mixed by $amount) means:
        $ = ($amount clamped between 0 and 1)
        return ((1 - $) * $lo + $ * $hi)
    
    test:
        assume ([0, 1, 11] mixed by 0) == 0
        assume ([0, 1, 11] mixed by 0.25) == 0.5
        assume ([0, 1, 11] mixed by 0.5) == 1
        assume ([0, 1, 11] mixed by 0.75) == 6
        assume ([0, 1, 11] mixed by 1) == 11
        assume ([99] mixed by 0.5) == 99
    
    ($nums mixed by $amount) means:
        $ = ($amount clamped between 0 and 1)
        $i = (1 + ($ * (#$nums - 1)))
        if ((floor $i) == #$nums):
            return $nums.(floor $i)
        [$lo, $hi] = [$nums.(floor $i), $nums.(floor ($i + 1))]
        return ($lo to $hi mixed by ($i mod 1))
    
    ### Random functions
    (seed random with $) means:
        lua> ("
            math.randomseed(\$);
            for i=1,20 do math.random(); end
        ")
    (seed random) parses as (seed random with (=lua "os.time()"))
    [random number, random, rand] all compile to "math.random()"
    [random int $n, random integer $n, randint $n] all compile to
        "math.random(\($n as lua expr))"
    
    [random from $low to $high, random number from $low to $high, rand $low $high]
    ..all compile to "math.random(\($low as lua expr), \($high as lua expr))"
    
    [random choice from $elements, random choice $elements, random $elements]
    ..all mean (=lua "\$elements[math.random(#\$elements)]")