github.com/kolbycrouch/elvish@v0.14.1-0.20210614162631-215b9ac1c423/pkg/eval/mods/math/math.go (about)

     1  // Package math exposes functionality from Go's math package as an elvish
     2  // module.
     3  package math
     4  
     5  import (
     6  	"math"
     7  	"math/big"
     8  
     9  	"src.elv.sh/pkg/eval"
    10  	"src.elv.sh/pkg/eval/errs"
    11  	"src.elv.sh/pkg/eval/vals"
    12  	"src.elv.sh/pkg/eval/vars"
    13  )
    14  
    15  // Ns is the namespace for the math: module.
    16  var Ns = eval.NsBuilder{
    17  	"e":  vars.NewReadOnly(math.E),
    18  	"pi": vars.NewReadOnly(math.Pi),
    19  }.AddGoFns("math:", fns).Ns()
    20  
    21  var fns = map[string]interface{}{
    22  	"abs":           abs,
    23  	"acos":          math.Acos,
    24  	"acosh":         math.Acosh,
    25  	"asin":          math.Asin,
    26  	"asinh":         math.Asinh,
    27  	"atan":          math.Atan,
    28  	"atanh":         math.Atanh,
    29  	"ceil":          math.Ceil, // TODO: Make exactness-preserving
    30  	"cos":           math.Cos,
    31  	"cosh":          math.Cosh,
    32  	"floor":         math.Floor, // TODO: Make exactness-preserving
    33  	"is-inf":        isInf,
    34  	"is-nan":        isNaN,
    35  	"log":           math.Log,
    36  	"log10":         math.Log10,
    37  	"log2":          math.Log2,
    38  	"max":           max,
    39  	"min":           min,
    40  	"pow":           math.Pow,         // TODO: Make exactness-preserving for integer exponents
    41  	"pow10":         math.Pow10,       // TODO: Make exactness-preserving for integer exponents
    42  	"round":         math.Round,       // TODO: Make exactness-preserving
    43  	"round-to-even": math.RoundToEven, // TODO: Make exactness-preserving
    44  	"sin":           math.Sin,
    45  	"sinh":          math.Sinh,
    46  	"sqrt":          math.Sqrt,
    47  	"tan":           math.Tan,
    48  	"tanh":          math.Tanh,
    49  	"trunc":         math.Trunc, // TODO: Make exactness-preserving
    50  }
    51  
    52  //elvdoc:var e
    53  //
    54  // ```elvish
    55  // $math:e
    56  // ```
    57  //
    58  // Approximate value of
    59  // [`e`](https://en.wikipedia.org/wiki/E_(mathematical_constant)):
    60  // 2.718281.... This variable is read-only.
    61  
    62  //elvdoc:var pi
    63  //
    64  // ```elvish
    65  // $math:pi
    66  // ```
    67  //
    68  // Approximate value of [`π`](https://en.wikipedia.org/wiki/Pi): 3.141592.... This
    69  // variable is read-only.
    70  
    71  //elvdoc:fn abs
    72  //
    73  // ```elvish
    74  // math:abs $number
    75  // ```
    76  //
    77  // Computes the absolute value `$number`. This function is exactness-preserving.
    78  // Examples:
    79  //
    80  // ```elvish-transcript
    81  // ~> math:abs 2
    82  // ▶ (num 2)
    83  // ~> math:abs -2
    84  // ▶ (num 2)
    85  // ~> math:abs 10000000000000000000
    86  // ▶ (num 10000000000000000000)
    87  // ~> math:abs -10000000000000000000
    88  // ▶ (num 10000000000000000000)
    89  // ~> math:abs 1/2
    90  // ▶ (num 1/2)
    91  // ~> math:abs -1/2
    92  // ▶ (num 1/2)
    93  // ~> math:abs 1.23
    94  // ▶ (num 1.23)
    95  // ~> math:abs -1.23
    96  // ▶ (num 1.23)
    97  // ```
    98  
    99  const (
   100  	maxInt = int(^uint(0) >> 1)
   101  	minInt = -maxInt - 1
   102  )
   103  
   104  var absMinInt = new(big.Int).Abs(big.NewInt(int64(minInt)))
   105  
   106  func abs(n vals.Num) vals.Num {
   107  	switch n := n.(type) {
   108  	case int:
   109  		if n < 0 {
   110  			if n == minInt {
   111  				return absMinInt
   112  			}
   113  			return -n
   114  		}
   115  		return n
   116  	case *big.Int:
   117  		if n.Sign() < 0 {
   118  			return new(big.Int).Abs(n)
   119  		}
   120  		return n
   121  	case *big.Rat:
   122  		if n.Sign() < 0 {
   123  			return new(big.Rat).Abs(n)
   124  		}
   125  		return n
   126  	case float64:
   127  		return math.Abs(n)
   128  	default:
   129  		panic("unreachable")
   130  	}
   131  }
   132  
   133  //elvdoc:fn ceil
   134  //
   135  // ```elvish
   136  // math:ceil $number
   137  // ```
   138  //
   139  // Computes the ceiling of `$number`.
   140  // Read the [Go documentation](https://godoc.org/math#Ceil) for the details of
   141  // how this behaves. Examples:
   142  //
   143  // ```elvish-transcript
   144  // ~> math:ceil 1.1
   145  // ▶ (float64 2)
   146  // ~> math:ceil -2.3
   147  // ▶ (float64 -2)
   148  // ```
   149  
   150  //elvdoc:fn acos
   151  //
   152  // ```elvish
   153  // math:acos $number
   154  // ```
   155  //
   156  // Outputs the arccosine of `$number`, in radians (not degrees). Examples:
   157  //
   158  // ```elvish-transcript
   159  // ~> math:acos 1
   160  // ▶ (float64 1)
   161  // ~> math:acos 1.00001
   162  // ▶ (float64 NaN)
   163  // ```
   164  
   165  //elvdoc:fn acosh
   166  //
   167  // ```elvish
   168  // math:acosh $number
   169  // ```
   170  //
   171  // Outputs the inverse hyperbolic cosine of `$number`. Examples:
   172  //
   173  // ```elvish-transcript
   174  // ~> math:acosh 1
   175  // ▶ (float64 0)
   176  // ~> math:acosh 0
   177  // ▶ (float64 NaN)
   178  // ```
   179  
   180  //elvdoc:fn asin
   181  //
   182  // ```elvish
   183  // math:asin $number
   184  // ```
   185  //
   186  // Outputs the arcsine of `$number`, in radians (not degrees). Examples:
   187  //
   188  // ```elvish-transcript
   189  // ~> math:asin 0
   190  // ▶ (float64 0)
   191  // ~> math:asin 1
   192  // ▶ (float64 1.5707963267948966)
   193  // ~> math:asin 1.00001
   194  // ▶ (float64 NaN)
   195  // ```
   196  
   197  //elvdoc:fn asinh
   198  //
   199  // ```elvish
   200  // math:asinh $number
   201  // ```
   202  //
   203  // Outputs the inverse hyperbolic sine of `$number`. Examples:
   204  //
   205  // ```elvish-transcript
   206  // ~> math:asinh 0
   207  // ▶ (float64 0)
   208  // ~> math:asinh inf
   209  // ▶ (float64 +Inf)
   210  // ```
   211  
   212  //elvdoc:fn atan
   213  //
   214  // ```elvish
   215  // math:atan $number
   216  // ```
   217  //
   218  // Outputs the arctangent of `$number`, in radians (not degrees). Examples:
   219  //
   220  // ```elvish-transcript
   221  // ~> math:atan 0
   222  // ▶ (float64 0)
   223  // ~> math:atan $math:inf
   224  // ▶ (float64 1.5707963267948966)
   225  // ```
   226  
   227  //elvdoc:fn atanh
   228  //
   229  // ```elvish
   230  // math:atanh $number
   231  // ```
   232  //
   233  // Outputs the inverse hyperbolic tangent of `$number`. Examples:
   234  //
   235  // ```elvish-transcript
   236  // ~> math:atanh 0
   237  // ▶ (float64 0)
   238  // ~> math:atanh 1
   239  // ▶ (float64 +Inf)
   240  // ```
   241  
   242  //elvdoc:fn cos
   243  //
   244  // ```elvish
   245  // math:cos $number
   246  // ```
   247  //
   248  // Computes the cosine of `$number` in units of radians (not degrees).
   249  // Examples:
   250  //
   251  // ```elvish-transcript
   252  // ~> math:cos 0
   253  // ▶ (float64 1)
   254  // ~> math:cos 3.14159265
   255  // ▶ (float64 -1)
   256  // ```
   257  
   258  //elvdoc:fn cosh
   259  //
   260  // ```elvish
   261  // math:cosh $number
   262  // ```
   263  //
   264  // Computes the hyperbolic cosine of `$number`. Example:
   265  //
   266  // ```elvish-transcript
   267  // ~> math:cosh 0
   268  // ▶ (float64 1)
   269  // ```
   270  
   271  //elvdoc:fn floor
   272  //
   273  // ```elvish
   274  // math:floor $number
   275  // ```
   276  //
   277  // Computes the floor of `$number`.
   278  // Read the [Go documentation](https://godoc.org/math#Floor) for the details of
   279  // how this behaves. Examples:
   280  //
   281  // ```elvish-transcript
   282  // ~> math:floor 1.1
   283  // ▶ (float64 1)
   284  // ~> math:floor -2.3
   285  // ▶ (float64 -3)
   286  // ```
   287  
   288  //elvdoc:fn is-inf
   289  //
   290  // ```elvish
   291  // math:is-inf &sign=0 $number
   292  // ```
   293  //
   294  // Tests whether the number is infinity. If sign > 0, tests whether `$number`
   295  // is positive infinity. If sign < 0, tests whether `$number` is negative
   296  // infinity. If sign == 0, tests whether `$number` is either infinity.
   297  //
   298  // ```elvish-transcript
   299  // ~> math:is-inf 123
   300  // ▶ $false
   301  // ~> math:is-inf inf
   302  // ▶ $true
   303  // ~> math:is-inf -inf
   304  // ▶ $true
   305  // ~> math:is-inf &sign=1 inf
   306  // ▶ $true
   307  // ~> math:is-inf &sign=-1 inf
   308  // ▶ $false
   309  // ~> math:is-inf &sign=-1 -inf
   310  // ▶ $true
   311  // ```
   312  
   313  type isInfOpts struct{ Sign int }
   314  
   315  func (opts *isInfOpts) SetDefaultOptions() { opts.Sign = 0 }
   316  
   317  func isInf(opts isInfOpts, n vals.Num) bool {
   318  	if f, ok := n.(float64); ok {
   319  		return math.IsInf(f, opts.Sign)
   320  	}
   321  	return false
   322  }
   323  
   324  //elvdoc:fn is-nan
   325  //
   326  // ```elvish
   327  // math:is-nan $number
   328  // ```
   329  //
   330  // Tests whether the number is a NaN (not-a-number).
   331  //
   332  // ```elvish-transcript
   333  // ~> math:is-nan 123
   334  // ▶ $false
   335  // ~> math:is-nan (float64 inf)
   336  // ▶ $false
   337  // ~> math:is-nan (float64 nan)
   338  // ▶ $true
   339  // ```
   340  
   341  func isNaN(n vals.Num) bool {
   342  	if f, ok := n.(float64); ok {
   343  		return math.IsNaN(f)
   344  	}
   345  	return false
   346  }
   347  
   348  //elvdoc:fn log
   349  //
   350  // ```elvish
   351  // math:log $number
   352  // ```
   353  //
   354  // Computes the natural (base *e*) logarithm of `$number`. Examples:
   355  //
   356  // ```elvish-transcript
   357  // ~> math:log 1.0
   358  // ▶ (float64 1)
   359  // ~> math:log -2.3
   360  // ▶ (float64 NaN)
   361  // ```
   362  
   363  //elvdoc:fn log10
   364  //
   365  // ```elvish
   366  // math:log10 $number
   367  // ```
   368  //
   369  // Computes the base 10 logarithm of `$number`. Examples:
   370  //
   371  // ```elvish-transcript
   372  // ~> math:log10 100.0
   373  // ▶ (float64 2)
   374  // ~> math:log10 -1.7
   375  // ▶ (float64 NaN)
   376  // ```
   377  
   378  //elvdoc:fn log2
   379  //
   380  // ```elvish
   381  // math:log2 $number
   382  // ```
   383  //
   384  // Computes the base 2 logarithm of `$number`. Examples:
   385  //
   386  // ```elvish-transcript
   387  // ~> math:log2 8
   388  // ▶ (float64 3)
   389  // ~> math:log2 -5.3
   390  // ▶ (float64 NaN)
   391  // ```
   392  
   393  //elvdoc:fn max
   394  //
   395  // ```elvish
   396  // math:max $number...
   397  // ```
   398  //
   399  // Outputs the maximum number in the arguments. If there are no arguments,
   400  // an exception is thrown. If any number is NaN then NaN is output. This
   401  // function is exactness-preserving.
   402  //
   403  // Examples:
   404  //
   405  // ```elvish-transcript
   406  // ~> math:max 3 5 2
   407  // ▶ (num 5)
   408  // ~> math:max (range 100)
   409  // ▶ (num 99)
   410  // ~> math:max 1/2 1/3 2/3
   411  // ▶ (num 2/3)
   412  // ```
   413  
   414  func max(rawNums ...vals.Num) (vals.Num, error) {
   415  	if len(rawNums) == 0 {
   416  		return nil, errs.ArityMismatch{
   417  			What: "arguments here", ValidLow: 1, ValidHigh: -1, Actual: 0}
   418  	}
   419  	nums := vals.UnifyNums(rawNums, 0)
   420  	switch nums := nums.(type) {
   421  	case []int:
   422  		n := nums[0]
   423  		for i := 1; i < len(nums); i++ {
   424  			if n < nums[i] {
   425  				n = nums[i]
   426  			}
   427  		}
   428  		return n, nil
   429  	case []*big.Int:
   430  		n := nums[0]
   431  		for i := 1; i < len(nums); i++ {
   432  			if n.Cmp(nums[i]) < 0 {
   433  				n = nums[i]
   434  			}
   435  		}
   436  		return n, nil
   437  	case []*big.Rat:
   438  		n := nums[0]
   439  		for i := 1; i < len(nums); i++ {
   440  			if n.Cmp(nums[i]) < 0 {
   441  				n = nums[i]
   442  			}
   443  		}
   444  		return n, nil
   445  	case []float64:
   446  		n := nums[0]
   447  		for i := 1; i < len(nums); i++ {
   448  			n = math.Max(n, nums[i])
   449  		}
   450  		return n, nil
   451  	default:
   452  		panic("unreachable")
   453  	}
   454  }
   455  
   456  //elvdoc:fn min
   457  //
   458  // ```elvish
   459  // math:min $number...
   460  // ```
   461  //
   462  // Outputs the minimum number in the arguments. If there are no arguments
   463  // an exception is thrown. If any number is NaN then NaN is output. This
   464  // function is exactness-preserving.
   465  //
   466  // Examples:
   467  //
   468  // ```elvish-transcript
   469  // ~> math:min
   470  // Exception: arity mismatch: arguments here must be 1 or more values, but is 0 values
   471  // [tty 17], line 1: math:min
   472  // ~> math:min 3 5 2
   473  // ▶ (num 2)
   474  // ~> math:min 1/2 1/3 2/3
   475  // ▶ (num 1/3)
   476  // ```
   477  
   478  func min(rawNums ...vals.Num) (vals.Num, error) {
   479  	if len(rawNums) == 0 {
   480  		return nil, errs.ArityMismatch{
   481  			What: "arguments here", ValidLow: 1, ValidHigh: -1, Actual: 0}
   482  	}
   483  	nums := vals.UnifyNums(rawNums, 0)
   484  	switch nums := nums.(type) {
   485  	case []int:
   486  		n := nums[0]
   487  		for i := 1; i < len(nums); i++ {
   488  			if n > nums[i] {
   489  				n = nums[i]
   490  			}
   491  		}
   492  		return n, nil
   493  	case []*big.Int:
   494  		n := nums[0]
   495  		for i := 1; i < len(nums); i++ {
   496  			if n.Cmp(nums[i]) > 0 {
   497  				n = nums[i]
   498  			}
   499  		}
   500  		return n, nil
   501  	case []*big.Rat:
   502  		n := nums[0]
   503  		for i := 1; i < len(nums); i++ {
   504  			if n.Cmp(nums[i]) > 0 {
   505  				n = nums[i]
   506  			}
   507  		}
   508  		return n, nil
   509  	case []float64:
   510  		n := nums[0]
   511  		for i := 1; i < len(nums); i++ {
   512  			n = math.Min(n, nums[i])
   513  		}
   514  		return n, nil
   515  	default:
   516  		panic("unreachable")
   517  	}
   518  }
   519  
   520  //elvdoc:fn pow
   521  //
   522  // ```elvish
   523  // math:pow $base $exponent
   524  // ```
   525  //
   526  // Output the result of raising `$base` to the power of `$exponent`. Examples:
   527  //
   528  // ```elvish-transcript
   529  // ~> math:pow 3 2
   530  // ▶ (float64 9)
   531  // ~> math:pow -2 2
   532  // ▶ (float64 4)
   533  // ```
   534  //
   535  // @cf math:pow10
   536  
   537  //elvdoc:fn pow10
   538  //
   539  // ```elvish
   540  // math:pow10 $exponent
   541  // ```
   542  //
   543  // Output the result of raising ten to the power of `$exponent` which must be
   544  // an integer. Note that `$exponent > 308` results in +Inf and `$exponent <
   545  // -323` results in zero. Examples:
   546  //
   547  // ```elvish-transcript
   548  // ~> math:pow10 2
   549  // ▶ (float64 100)
   550  // ~> math:pow10 -3
   551  // ▶ (float64 0.001)
   552  // ```
   553  //
   554  // @cf math:pow
   555  
   556  //elvdoc:fn round
   557  //
   558  // ```elvish
   559  // math:round $number
   560  // ```
   561  //
   562  // Outputs the nearest integer, rounding half away from zero.
   563  //
   564  // ```elvish-transcript
   565  // ~> math:round -1.1
   566  // ▶ (float64 -1)
   567  // ~> math:round 2.5
   568  // ▶ (float64 3)
   569  // ```
   570  
   571  //elvdoc:fn round-to-even
   572  //
   573  // ```elvish
   574  // math:round-to-even $number
   575  // ```
   576  //
   577  // Outputs the nearest integer, rounding ties to even. Examples:
   578  //
   579  // ```elvish-transcript
   580  // ~> math:round-to-even -1.1
   581  // ▶ (float64 -1)
   582  // ~> math:round-to-even 2.5
   583  // ▶ (float64 2)
   584  // ```
   585  
   586  //elvdoc:fn sin
   587  //
   588  // ```elvish
   589  // math:sin $number
   590  // ```
   591  //
   592  // Computes the sine of `$number` in units of radians (not degrees). Examples:
   593  //
   594  // ```elvish-transcript
   595  // ~> math:sin 0
   596  // ▶ (float64 0)
   597  // ~> math:sin 3.14159265
   598  // ▶ (float64 3.5897930298416118e-09)
   599  // ```
   600  
   601  //elvdoc:fn sinh
   602  //
   603  // ```elvish
   604  // math:sinh $number
   605  // ```
   606  //
   607  // Computes the hyperbolic sine of `$number`. Example:
   608  //
   609  // ```elvish-transcript
   610  // ~> math:sinh 0
   611  // ▶ (float64 0)
   612  // ```
   613  
   614  //elvdoc:fn sqrt
   615  //
   616  // ```elvish
   617  // math:sqrt $number
   618  // ```
   619  //
   620  // Computes the square-root of `$number`. Examples:
   621  //
   622  // ```elvish-transcript
   623  // ~> math:sqrt 0
   624  // ▶ (float64 0)
   625  // ~> math:sqrt 4
   626  // ▶ (float64 2)
   627  // ~> math:sqrt -4
   628  // ▶ (float64 NaN)
   629  // ```
   630  
   631  //elvdoc:fn tan
   632  //
   633  // ```elvish
   634  // math:tan $number
   635  // ```
   636  //
   637  // Computes the tangent of `$number` in units of radians (not degrees). Examples:
   638  //
   639  // ```elvish-transcript
   640  // ~> math:tan 0
   641  // ▶ (float64 0)
   642  // ~> math:tan 3.14159265
   643  // ▶ (float64 -0.0000000035897930298416118)
   644  // ```
   645  
   646  //elvdoc:fn tanh
   647  //
   648  // ```elvish
   649  // math:tanh $number
   650  // ```
   651  //
   652  // Computes the hyperbolic tangent of `$number`. Example:
   653  //
   654  // ```elvish-transcript
   655  // ~> math:tanh 0
   656  // ▶ (float64 0)
   657  // ```
   658  
   659  //elvdoc:fn trunc
   660  //
   661  // ```elvish
   662  // math:trunc $number
   663  // ```
   664  //
   665  // Outputs the integer portion of `$number`.
   666  //
   667  // ```elvish-transcript
   668  // ~> math:trunc -1.1
   669  // ▶ (float64 -1)
   670  // ~> math:trunc 2.5
   671  // ▶ (float64 2)
   672  // ```