github.com/markusbkk/elvish@v0.0.0-20231204143114-91dc52438621/pkg/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  	"github.com/markusbkk/elvish/pkg/eval"
    10  	"github.com/markusbkk/elvish/pkg/eval/errs"
    11  	"github.com/markusbkk/elvish/pkg/eval/vals"
    12  	"github.com/markusbkk/elvish/pkg/eval/vars"
    13  )
    14  
    15  // Ns is the namespace for the math: module.
    16  var Ns = eval.BuildNsNamed("math").
    17  	AddVars(map[string]vars.Var{
    18  		"e":  vars.NewReadOnly(math.E),
    19  		"pi": vars.NewReadOnly(math.Pi),
    20  	}).
    21  	AddGoFns(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":          ceil,
    30  		"cos":           math.Cos,
    31  		"cosh":          math.Cosh,
    32  		"floor":         floor,
    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":           pow,
    41  		"round":         round,
    42  		"round-to-even": roundToEven,
    43  		"sin":           math.Sin,
    44  		"sinh":          math.Sinh,
    45  		"sqrt":          math.Sqrt,
    46  		"tan":           math.Tan,
    47  		"tanh":          math.Tanh,
    48  		"trunc":         trunc,
    49  	}).Ns()
    50  
    51  //elvdoc:var e
    52  //
    53  // ```elvish
    54  // $math:e
    55  // ```
    56  //
    57  // Approximate value of
    58  // [`e`](https://en.wikipedia.org/wiki/E_(mathematical_constant)):
    59  // 2.718281.... This variable is read-only.
    60  
    61  //elvdoc:var pi
    62  //
    63  // ```elvish
    64  // $math:pi
    65  // ```
    66  //
    67  // Approximate value of [`π`](https://en.wikipedia.org/wiki/Pi): 3.141592.... This
    68  // variable is read-only.
    69  
    70  //elvdoc:fn abs
    71  //
    72  // ```elvish
    73  // math:abs $number
    74  // ```
    75  //
    76  // Computes the absolute value `$number`. This function is exactness-preserving.
    77  // Examples:
    78  //
    79  // ```elvish-transcript
    80  // ~> math:abs 2
    81  // ▶ (num 2)
    82  // ~> math:abs -2
    83  // ▶ (num 2)
    84  // ~> math:abs 10000000000000000000
    85  // ▶ (num 10000000000000000000)
    86  // ~> math:abs -10000000000000000000
    87  // ▶ (num 10000000000000000000)
    88  // ~> math:abs 1/2
    89  // ▶ (num 1/2)
    90  // ~> math:abs -1/2
    91  // ▶ (num 1/2)
    92  // ~> math:abs 1.23
    93  // ▶ (num 1.23)
    94  // ~> math:abs -1.23
    95  // ▶ (num 1.23)
    96  // ```
    97  
    98  const (
    99  	maxInt = int(^uint(0) >> 1)
   100  	minInt = -maxInt - 1
   101  )
   102  
   103  var absMinInt = new(big.Int).Abs(big.NewInt(int64(minInt)))
   104  
   105  func abs(n vals.Num) vals.Num {
   106  	switch n := n.(type) {
   107  	case int:
   108  		if n < 0 {
   109  			if n == minInt {
   110  				return absMinInt
   111  			}
   112  			return -n
   113  		}
   114  		return n
   115  	case *big.Int:
   116  		if n.Sign() < 0 {
   117  			return new(big.Int).Abs(n)
   118  		}
   119  		return n
   120  	case *big.Rat:
   121  		if n.Sign() < 0 {
   122  			return new(big.Rat).Abs(n)
   123  		}
   124  		return n
   125  	case float64:
   126  		return math.Abs(n)
   127  	default:
   128  		panic("unreachable")
   129  	}
   130  }
   131  
   132  //elvdoc:fn acos
   133  //
   134  // ```elvish
   135  // math:acos $number
   136  // ```
   137  //
   138  // Outputs the arccosine of `$number`, in radians (not degrees). Examples:
   139  //
   140  // ```elvish-transcript
   141  // ~> math:acos 1
   142  // ▶ (float64 1)
   143  // ~> math:acos 1.00001
   144  // ▶ (float64 NaN)
   145  // ```
   146  
   147  //elvdoc:fn acosh
   148  //
   149  // ```elvish
   150  // math:acosh $number
   151  // ```
   152  //
   153  // Outputs the inverse hyperbolic cosine of `$number`. Examples:
   154  //
   155  // ```elvish-transcript
   156  // ~> math:acosh 1
   157  // ▶ (float64 0)
   158  // ~> math:acosh 0
   159  // ▶ (float64 NaN)
   160  // ```
   161  
   162  //elvdoc:fn asin
   163  //
   164  // ```elvish
   165  // math:asin $number
   166  // ```
   167  //
   168  // Outputs the arcsine of `$number`, in radians (not degrees). Examples:
   169  //
   170  // ```elvish-transcript
   171  // ~> math:asin 0
   172  // ▶ (float64 0)
   173  // ~> math:asin 1
   174  // ▶ (float64 1.5707963267948966)
   175  // ~> math:asin 1.00001
   176  // ▶ (float64 NaN)
   177  // ```
   178  
   179  //elvdoc:fn asinh
   180  //
   181  // ```elvish
   182  // math:asinh $number
   183  // ```
   184  //
   185  // Outputs the inverse hyperbolic sine of `$number`. Examples:
   186  //
   187  // ```elvish-transcript
   188  // ~> math:asinh 0
   189  // ▶ (float64 0)
   190  // ~> math:asinh inf
   191  // ▶ (float64 +Inf)
   192  // ```
   193  
   194  //elvdoc:fn atan
   195  //
   196  // ```elvish
   197  // math:atan $number
   198  // ```
   199  //
   200  // Outputs the arctangent of `$number`, in radians (not degrees). Examples:
   201  //
   202  // ```elvish-transcript
   203  // ~> math:atan 0
   204  // ▶ (float64 0)
   205  // ~> math:atan $math:inf
   206  // ▶ (float64 1.5707963267948966)
   207  // ```
   208  
   209  //elvdoc:fn atanh
   210  //
   211  // ```elvish
   212  // math:atanh $number
   213  // ```
   214  //
   215  // Outputs the inverse hyperbolic tangent of `$number`. Examples:
   216  //
   217  // ```elvish-transcript
   218  // ~> math:atanh 0
   219  // ▶ (float64 0)
   220  // ~> math:atanh 1
   221  // ▶ (float64 +Inf)
   222  // ```
   223  
   224  //elvdoc:fn ceil
   225  //
   226  // ```elvish
   227  // math:ceil $number
   228  // ```
   229  //
   230  // Computes the least integer greater than or equal to `$number`. This function
   231  // is exactness-preserving.
   232  //
   233  // The results for the special floating-point values -0.0, +0.0, -Inf, +Inf and
   234  // NaN are themselves.
   235  //
   236  // Examples:
   237  //
   238  // ```elvish-transcript
   239  // ~> math:floor 1
   240  // ▶ (num 1)
   241  // ~> math:floor 3/2
   242  // ▶ (num 1)
   243  // ~> math:floor -3/2
   244  // ▶ (num -2)
   245  // ~> math:floor 1.1
   246  // ▶ (num 1.0)
   247  // ~> math:floor -1.1
   248  // ▶ (num -2.0)
   249  // ```
   250  
   251  var (
   252  	big1 = big.NewInt(1)
   253  	big2 = big.NewInt(2)
   254  )
   255  
   256  func ceil(n vals.Num) vals.Num {
   257  	return integerize(n,
   258  		math.Ceil,
   259  		func(n *big.Rat) *big.Int {
   260  			q := new(big.Int).Div(n.Num(), n.Denom())
   261  			return q.Add(q, big1)
   262  		})
   263  }
   264  
   265  //elvdoc:fn cos
   266  //
   267  // ```elvish
   268  // math:cos $number
   269  // ```
   270  //
   271  // Computes the cosine of `$number` in units of radians (not degrees).
   272  // Examples:
   273  //
   274  // ```elvish-transcript
   275  // ~> math:cos 0
   276  // ▶ (float64 1)
   277  // ~> math:cos 3.14159265
   278  // ▶ (float64 -1)
   279  // ```
   280  
   281  //elvdoc:fn cosh
   282  //
   283  // ```elvish
   284  // math:cosh $number
   285  // ```
   286  //
   287  // Computes the hyperbolic cosine of `$number`. Example:
   288  //
   289  // ```elvish-transcript
   290  // ~> math:cosh 0
   291  // ▶ (float64 1)
   292  // ```
   293  
   294  //elvdoc:fn floor
   295  //
   296  // ```elvish
   297  // math:floor $number
   298  // ```
   299  //
   300  // Computes the greatest integer less than or equal to `$number`. This function
   301  // is exactness-preserving.
   302  //
   303  // The results for the special floating-point values -0.0, +0.0, -Inf, +Inf and
   304  // NaN are themselves.
   305  //
   306  // Examples:
   307  //
   308  // ```elvish-transcript
   309  // ~> math:floor 1
   310  // ▶ (num 1)
   311  // ~> math:floor 3/2
   312  // ▶ (num 1)
   313  // ~> math:floor -3/2
   314  // ▶ (num -2)
   315  // ~> math:floor 1.1
   316  // ▶ (num 1.0)
   317  // ~> math:floor -1.1
   318  // ▶ (num -2.0)
   319  // ```
   320  
   321  func floor(n vals.Num) vals.Num {
   322  	return integerize(n,
   323  		math.Floor,
   324  		func(n *big.Rat) *big.Int {
   325  			return new(big.Int).Div(n.Num(), n.Denom())
   326  		})
   327  }
   328  
   329  //elvdoc:fn is-inf
   330  //
   331  // ```elvish
   332  // math:is-inf &sign=0 $number
   333  // ```
   334  //
   335  // Tests whether the number is infinity. If sign > 0, tests whether `$number`
   336  // is positive infinity. If sign < 0, tests whether `$number` is negative
   337  // infinity. If sign == 0, tests whether `$number` is either infinity.
   338  //
   339  // ```elvish-transcript
   340  // ~> math:is-inf 123
   341  // ▶ $false
   342  // ~> math:is-inf inf
   343  // ▶ $true
   344  // ~> math:is-inf -inf
   345  // ▶ $true
   346  // ~> math:is-inf &sign=1 inf
   347  // ▶ $true
   348  // ~> math:is-inf &sign=-1 inf
   349  // ▶ $false
   350  // ~> math:is-inf &sign=-1 -inf
   351  // ▶ $true
   352  // ```
   353  
   354  type isInfOpts struct{ Sign int }
   355  
   356  func (opts *isInfOpts) SetDefaultOptions() { opts.Sign = 0 }
   357  
   358  func isInf(opts isInfOpts, n vals.Num) bool {
   359  	if f, ok := n.(float64); ok {
   360  		return math.IsInf(f, opts.Sign)
   361  	}
   362  	return false
   363  }
   364  
   365  //elvdoc:fn is-nan
   366  //
   367  // ```elvish
   368  // math:is-nan $number
   369  // ```
   370  //
   371  // Tests whether the number is a NaN (not-a-number).
   372  //
   373  // ```elvish-transcript
   374  // ~> math:is-nan 123
   375  // ▶ $false
   376  // ~> math:is-nan (float64 inf)
   377  // ▶ $false
   378  // ~> math:is-nan (float64 nan)
   379  // ▶ $true
   380  // ```
   381  
   382  func isNaN(n vals.Num) bool {
   383  	if f, ok := n.(float64); ok {
   384  		return math.IsNaN(f)
   385  	}
   386  	return false
   387  }
   388  
   389  //elvdoc:fn log
   390  //
   391  // ```elvish
   392  // math:log $number
   393  // ```
   394  //
   395  // Computes the natural (base *e*) logarithm of `$number`. Examples:
   396  //
   397  // ```elvish-transcript
   398  // ~> math:log 1.0
   399  // ▶ (float64 1)
   400  // ~> math:log -2.3
   401  // ▶ (float64 NaN)
   402  // ```
   403  
   404  //elvdoc:fn log10
   405  //
   406  // ```elvish
   407  // math:log10 $number
   408  // ```
   409  //
   410  // Computes the base 10 logarithm of `$number`. Examples:
   411  //
   412  // ```elvish-transcript
   413  // ~> math:log10 100.0
   414  // ▶ (float64 2)
   415  // ~> math:log10 -1.7
   416  // ▶ (float64 NaN)
   417  // ```
   418  
   419  //elvdoc:fn log2
   420  //
   421  // ```elvish
   422  // math:log2 $number
   423  // ```
   424  //
   425  // Computes the base 2 logarithm of `$number`. Examples:
   426  //
   427  // ```elvish-transcript
   428  // ~> math:log2 8
   429  // ▶ (float64 3)
   430  // ~> math:log2 -5.3
   431  // ▶ (float64 NaN)
   432  // ```
   433  
   434  //elvdoc:fn max
   435  //
   436  // ```elvish
   437  // math:max $number...
   438  // ```
   439  //
   440  // Outputs the maximum number in the arguments. If there are no arguments,
   441  // an exception is thrown. If any number is NaN then NaN is output. This
   442  // function is exactness-preserving.
   443  //
   444  // Examples:
   445  //
   446  // ```elvish-transcript
   447  // ~> math:max 3 5 2
   448  // ▶ (num 5)
   449  // ~> math:max (range 100)
   450  // ▶ (num 99)
   451  // ~> math:max 1/2 1/3 2/3
   452  // ▶ (num 2/3)
   453  // ```
   454  
   455  func max(rawNums ...vals.Num) (vals.Num, error) {
   456  	if len(rawNums) == 0 {
   457  		return nil, errs.ArityMismatch{What: "arguments", ValidLow: 1, ValidHigh: -1, Actual: 0}
   458  	}
   459  	nums := vals.UnifyNums(rawNums, 0)
   460  	switch nums := nums.(type) {
   461  	case []int:
   462  		n := nums[0]
   463  		for i := 1; i < len(nums); i++ {
   464  			if n < nums[i] {
   465  				n = nums[i]
   466  			}
   467  		}
   468  		return n, nil
   469  	case []*big.Int:
   470  		n := nums[0]
   471  		for i := 1; i < len(nums); i++ {
   472  			if n.Cmp(nums[i]) < 0 {
   473  				n = nums[i]
   474  			}
   475  		}
   476  		return n, nil
   477  	case []*big.Rat:
   478  		n := nums[0]
   479  		for i := 1; i < len(nums); i++ {
   480  			if n.Cmp(nums[i]) < 0 {
   481  				n = nums[i]
   482  			}
   483  		}
   484  		return n, nil
   485  	case []float64:
   486  		n := nums[0]
   487  		for i := 1; i < len(nums); i++ {
   488  			n = math.Max(n, nums[i])
   489  		}
   490  		return n, nil
   491  	default:
   492  		panic("unreachable")
   493  	}
   494  }
   495  
   496  //elvdoc:fn min
   497  //
   498  // ```elvish
   499  // math:min $number...
   500  // ```
   501  //
   502  // Outputs the minimum number in the arguments. If there are no arguments
   503  // an exception is thrown. If any number is NaN then NaN is output. This
   504  // function is exactness-preserving.
   505  //
   506  // Examples:
   507  //
   508  // ```elvish-transcript
   509  // ~> math:min
   510  // Exception: arity mismatch: arguments must be 1 or more values, but is 0 values
   511  // [tty 17], line 1: math:min
   512  // ~> math:min 3 5 2
   513  // ▶ (num 2)
   514  // ~> math:min 1/2 1/3 2/3
   515  // ▶ (num 1/3)
   516  // ```
   517  
   518  func min(rawNums ...vals.Num) (vals.Num, error) {
   519  	if len(rawNums) == 0 {
   520  		return nil, errs.ArityMismatch{What: "arguments", ValidLow: 1, ValidHigh: -1, Actual: 0}
   521  	}
   522  	nums := vals.UnifyNums(rawNums, 0)
   523  	switch nums := nums.(type) {
   524  	case []int:
   525  		n := nums[0]
   526  		for i := 1; i < len(nums); i++ {
   527  			if n > nums[i] {
   528  				n = nums[i]
   529  			}
   530  		}
   531  		return n, nil
   532  	case []*big.Int:
   533  		n := nums[0]
   534  		for i := 1; i < len(nums); i++ {
   535  			if n.Cmp(nums[i]) > 0 {
   536  				n = nums[i]
   537  			}
   538  		}
   539  		return n, nil
   540  	case []*big.Rat:
   541  		n := nums[0]
   542  		for i := 1; i < len(nums); i++ {
   543  			if n.Cmp(nums[i]) > 0 {
   544  				n = nums[i]
   545  			}
   546  		}
   547  		return n, nil
   548  	case []float64:
   549  		n := nums[0]
   550  		for i := 1; i < len(nums); i++ {
   551  			n = math.Min(n, nums[i])
   552  		}
   553  		return n, nil
   554  	default:
   555  		panic("unreachable")
   556  	}
   557  }
   558  
   559  //elvdoc:fn pow
   560  //
   561  // ```elvish
   562  // math:pow $base $exponent
   563  // ```
   564  //
   565  // Outputs the result of raising `$base` to the power of `$exponent`.
   566  //
   567  // This function produces an exact result when `$base` is exact and `$exponent`
   568  // is an exact integer. Otherwise it produces an inexact result.
   569  //
   570  // Examples:
   571  //
   572  // ```elvish-transcript
   573  // ~> math:pow 3 2
   574  // ▶ (num 9)
   575  // ~> math:pow -2 2
   576  // ▶ (num 4)
   577  // ~> math:pow 1/2 3
   578  // ▶ (num 1/8)
   579  // ~> math:pow 1/2 -3
   580  // ▶ (num 8)
   581  // ~> math:pow 9 1/2
   582  // ▶ (num 3.0)
   583  // ~> math:pow 12 1.1
   584  // ▶ (num 15.38506624784179)
   585  // ```
   586  
   587  func pow(base, exp vals.Num) vals.Num {
   588  	if isExact(base) && isExactInt(exp) {
   589  		// Produce exact result
   590  		switch exp {
   591  		case 0:
   592  			return 1
   593  		case 1:
   594  			return base
   595  		case -1:
   596  			return new(big.Rat).Inv(vals.PromoteToBigRat(base))
   597  		}
   598  		exp := vals.PromoteToBigInt(exp)
   599  		if isExactInt(base) && exp.Sign() > 0 {
   600  			base := vals.PromoteToBigInt(base)
   601  			return new(big.Int).Exp(base, exp, nil)
   602  		}
   603  		base := vals.PromoteToBigRat(base)
   604  		if exp.Sign() < 0 {
   605  			base = new(big.Rat).Inv(base)
   606  			exp = new(big.Int).Neg(exp)
   607  		}
   608  		return new(big.Rat).SetFrac(
   609  			new(big.Int).Exp(base.Num(), exp, nil),
   610  			new(big.Int).Exp(base.Denom(), exp, nil))
   611  	}
   612  
   613  	// Produce inexact result
   614  	basef := vals.ConvertToFloat64(base)
   615  	expf := vals.ConvertToFloat64(exp)
   616  	return math.Pow(basef, expf)
   617  }
   618  
   619  func isExact(n vals.Num) bool {
   620  	switch n.(type) {
   621  	case int, *big.Int, *big.Rat:
   622  		return true
   623  	default:
   624  		return false
   625  	}
   626  }
   627  
   628  func isExactInt(n vals.Num) bool {
   629  	switch n.(type) {
   630  	case int, *big.Int:
   631  		return true
   632  	default:
   633  		return false
   634  	}
   635  }
   636  
   637  //elvdoc:fn round
   638  //
   639  // ```elvish
   640  // math:round $number
   641  // ```
   642  //
   643  // Outputs the nearest integer, rounding half away from zero. This function is
   644  // exactness-preserving.
   645  //
   646  // The results for the special floating-point values -0.0, +0.0, -Inf, +Inf and
   647  // NaN are themselves.
   648  //
   649  // Examples:
   650  //
   651  // ```elvish-transcript
   652  // ~> math:round 2
   653  // ▶ (num 2)
   654  // ~> math:round 1/3
   655  // ▶ (num 0)
   656  // ~> math:round 1/2
   657  // ▶ (num 1)
   658  // ~> math:round 2/3
   659  // ▶ (num 1)
   660  // ~> math:round -1/3
   661  // ▶ (num 0)
   662  // ~> math:round -1/2
   663  // ▶ (num -1)
   664  // ~> math:round -2/3
   665  // ▶ (num -1)
   666  // ~> math:round 2.5
   667  // ▶ (num 3.0)
   668  // ```
   669  
   670  func round(n vals.Num) vals.Num {
   671  	return integerize(n,
   672  		math.Round,
   673  		func(n *big.Rat) *big.Int {
   674  			q, m := new(big.Int).QuoRem(n.Num(), n.Denom(), new(big.Int))
   675  			m = m.Mul(m, big2)
   676  			if m.CmpAbs(n.Denom()) < 0 {
   677  				return q
   678  			}
   679  			if n.Sign() < 0 {
   680  				return q.Sub(q, big1)
   681  			}
   682  			return q.Add(q, big1)
   683  		})
   684  }
   685  
   686  //elvdoc:fn round-to-even
   687  //
   688  // ```elvish
   689  // math:round-to-even $number
   690  // ```
   691  //
   692  // Outputs the nearest integer, rounding ties to even. This function is
   693  // exactness-preserving.
   694  //
   695  // The results for the special floating-point values -0.0, +0.0, -Inf, +Inf and
   696  // NaN are themselves.
   697  //
   698  // Examples:
   699  //
   700  // ```elvish-transcript
   701  // ~> math:round-to-even 2
   702  // ▶ (num 2)
   703  // ~> math:round-to-even 1/2
   704  // ▶ (num 0)
   705  // ~> math:round-to-even 3/2
   706  // ▶ (num 2)
   707  // ~> math:round-to-even 5/2
   708  // ▶ (num 2)
   709  // ~> math:round-to-even -5/2
   710  // ▶ (num -2)
   711  // ~> math:round-to-even 2.5
   712  // ▶ (num 2.0)
   713  // ~> math:round-to-even 1.5
   714  // ▶ (num 2.0)
   715  // ```
   716  
   717  func roundToEven(n vals.Num) vals.Num {
   718  	return integerize(n,
   719  		math.RoundToEven,
   720  		func(n *big.Rat) *big.Int {
   721  			q, m := new(big.Int).QuoRem(n.Num(), n.Denom(), new(big.Int))
   722  			m = m.Mul(m, big2)
   723  			if diff := m.CmpAbs(n.Denom()); diff < 0 || diff == 0 && q.Bit(0) == 0 {
   724  				return q
   725  			}
   726  			if n.Sign() < 0 {
   727  				return q.Sub(q, big1)
   728  			}
   729  			return q.Add(q, big1)
   730  		})
   731  }
   732  
   733  //elvdoc:fn sin
   734  //
   735  // ```elvish
   736  // math:sin $number
   737  // ```
   738  //
   739  // Computes the sine of `$number` in units of radians (not degrees). Examples:
   740  //
   741  // ```elvish-transcript
   742  // ~> math:sin 0
   743  // ▶ (float64 0)
   744  // ~> math:sin 3.14159265
   745  // ▶ (float64 3.5897930298416118e-09)
   746  // ```
   747  
   748  //elvdoc:fn sinh
   749  //
   750  // ```elvish
   751  // math:sinh $number
   752  // ```
   753  //
   754  // Computes the hyperbolic sine of `$number`. Example:
   755  //
   756  // ```elvish-transcript
   757  // ~> math:sinh 0
   758  // ▶ (float64 0)
   759  // ```
   760  
   761  //elvdoc:fn sqrt
   762  //
   763  // ```elvish
   764  // math:sqrt $number
   765  // ```
   766  //
   767  // Computes the square-root of `$number`. Examples:
   768  //
   769  // ```elvish-transcript
   770  // ~> math:sqrt 0
   771  // ▶ (float64 0)
   772  // ~> math:sqrt 4
   773  // ▶ (float64 2)
   774  // ~> math:sqrt -4
   775  // ▶ (float64 NaN)
   776  // ```
   777  
   778  //elvdoc:fn tan
   779  //
   780  // ```elvish
   781  // math:tan $number
   782  // ```
   783  //
   784  // Computes the tangent of `$number` in units of radians (not degrees). Examples:
   785  //
   786  // ```elvish-transcript
   787  // ~> math:tan 0
   788  // ▶ (float64 0)
   789  // ~> math:tan 3.14159265
   790  // ▶ (float64 -0.0000000035897930298416118)
   791  // ```
   792  
   793  //elvdoc:fn tanh
   794  //
   795  // ```elvish
   796  // math:tanh $number
   797  // ```
   798  //
   799  // Computes the hyperbolic tangent of `$number`. Example:
   800  //
   801  // ```elvish-transcript
   802  // ~> math:tanh 0
   803  // ▶ (float64 0)
   804  // ```
   805  
   806  //elvdoc:fn trunc
   807  //
   808  // ```elvish
   809  // math:trunc $number
   810  // ```
   811  //
   812  // Outputs the integer portion of `$number`. This function is exactness-preserving.
   813  //
   814  // The results for the special floating-point values -0.0, +0.0, -Inf, +Inf and
   815  // NaN are themselves.
   816  //
   817  // Examples:
   818  //
   819  // ```elvish-transcript
   820  // ~> math:trunc 1
   821  // ▶ (num 1)
   822  // ~> math:trunc 3/2
   823  // ▶ (num 1)
   824  // ~> math:trunc 5/3
   825  // ▶ (num 1)
   826  // ~> math:trunc -3/2
   827  // ▶ (num -1)
   828  // ~> math:trunc -5/3
   829  // ▶ (num -1)
   830  // ~> math:trunc 1.7
   831  // ▶ (num 1.0)
   832  // ~> math:trunc -1.7
   833  // ▶ (num -1.0)
   834  // ```
   835  
   836  func trunc(n vals.Num) vals.Num {
   837  	return integerize(n,
   838  		math.Trunc,
   839  		func(n *big.Rat) *big.Int {
   840  			return new(big.Int).Quo(n.Num(), n.Denom())
   841  		})
   842  }
   843  
   844  func integerize(n vals.Num, fnFloat func(float64) float64, fnRat func(*big.Rat) *big.Int) vals.Num {
   845  	switch n := n.(type) {
   846  	case int:
   847  		return n
   848  	case *big.Int:
   849  		return n
   850  	case *big.Rat:
   851  		if n.Denom().IsInt64() && n.Denom().Int64() == 1 {
   852  			// Elvish always normalizes *big.Rat with a denominator of 1 to
   853  			// *big.Int, but we still try to be defensive here.
   854  			return n.Num()
   855  		}
   856  		return fnRat(n)
   857  	case float64:
   858  		return fnFloat(n)
   859  	default:
   860  		panic("unreachable")
   861  	}
   862  }