#### 3.2Numbers

Pyret numbers are of two kinds: exact numbers, or Exactnums, and rough numbers or Roughnums. Both are real; finite; and written in base ten.

Note that imagaginary numbers were implemented in earlier versions of Pyret, but are not currently supported.

Exactnums are arbitrarily precise rational numbers, including integers and rational fractions. For integers whose magnitude is less than (num-expt(2, 53) - 1), Pyret internally uses JavaScript fixnums, in order to optimize basic arithmetic.

Roughnums are numbers that are necessarily or deliberately imprecise. These correspond to the same set of values covered by JavaScript fixnums (a.k.a. doubles), and thus cover a large but limited range (magnitude less than 1.7976931348623157e308).

Operations on Exactnums typically return Exactnums. However, if the operation can yield irrationals, and it is not possible to determine that a particular result is definitely rational, that result is returned as a Roughnum. Thus, trigonometric functions on Exactnums typically yield Roughnum answers, except for well-known edge cases such as the sine or cosine of zero. Fractional powers of rationals are usually Roughnum, except for small roots where it can be ascertained that an exact root is possible.

Operations that are non-casting and with at least one argument that is Roughnum automatically coerce the result to be a Roughnum. This is known as Roughnum contagion.

Exactnums allow the usual comparison predicates. Roughnums do too, with the significant exception that trying to compare Roughnums for equality throws an error. To write an equality function that handles Roughnums, use within, as documented in Bounded Equalities.

An operation whose numerical result is not determinate or finite throws an error, with the message signaling either an overflow or some more specific problem.

##### 3.2.1Number Annotations

Several specific type annotations are provided for numbers to allow more precise value requirements to be specified.

Examples:
```fun round-distance(d :: NumNonNegative) -> Exactnum:
num-round(d)
end```

The type of number values.
The type of exact number values.
The type of necessarily or deliberately imprecise values.
The type of Exactnum integer values.
The type of exact rational number values. Same as Exactnum.
The type of number values that are greater than zero.
The type of number values that are less than zero.
The type of number values that are less than or equal to zero.
The type of number values that are equal to or greater than zero.

##### 3.2.2Number Literals

Exactnums can be integers, fractions represented with a solidus, or decimals, with an optional exponent. In the following, the numerals on the same line all denote the same Pyret number.

Examples:
```42 +42
-42
22/7
-22/7
2.718281828 +2.718281828
-2.718281828
1/2 0.5
6.022e23 +6.022e23 6.022e+23 +6.022e+23
-6.022e23 -6.022e+23
-6.022e-23```

Exactnums are of arbitrary precision.

Roughnums are represented with a leading tilde. You can think of the tilde as representing a person waving his or her hands vaguely.

They are integers, fractions or decimals, with an optional exponent.

Examples:
```~42 ~+42
~-42
~2.718281828 ~+2.718281828
~-2.718281828
~6.022e23 ~+6.022e23 ~6.022e+23 ~+6.022e+23
~-6.022e23 ~-6.022e+23
~-6.022e-23```

Roughnums cannot be made arbitrarily precise. The absolute value ranges between 0 and 1.7976931348623157e+308 (JavaScript’s Number.MAX_VALUE) with a granularity of 5e-324 (JavaScript’s Number.MIN_VALUE).

##### 3.2.3Number Functions

num-equal :: (n1 :: Number, n2 :: Number) -> Boolean

If both arguments are Exactnums, returns a Boolean. If either argument is Roughnum, raises an error.

Examples:
```check:
num-equal(2, 2) is true
num-equal(2, 3) is false
num-equal(1/2, 0.5) is true
num-equal(1 / 2, 0.5) is true
num-equal(1/3, 0.33) is false
num-equal(1/3, ~0.33)
raises "roughnums cannot be compared for equality"
end```

num-max :: (n1 :: Number, n2 :: Number) -> Number

Returns the greater of the two arguments.

Examples:
```check:
num-max(1, 2) is 2
num-max(2, ~3) is-roughly ~3
num-max(4, ~4) is 4
num-max(~4, 4) is-roughly ~4
num-max(-1.1, 0) is 0
end```

num-min :: (n1 :: Number, n2 :: Number) -> Number

Returns the lesser of the two arguments.

Examples:
```check:
num-min(1, 2) is 1
num-min(2, ~3) is 2
num-min(4, ~4) is 4
num-min(~4, 4) is-roughly ~4
num-min(-1.1, 0) is -1.1
end```

num-abs :: (n :: Number) -> Number

Returns the absolute value of the argument. The result is an Exactnum only if the argument is.

Examples:
```check:
num-abs(2) is 2
num-abs(-2.1) is 2.1
num-abs(~2) is-roughly ~2
num-abs(~-2.1) is-roughly ~2.1
end```

num-sin :: (n :: Number) -> Number

Returns the sine of the argument, usually as a Roughnum. If the argument is Exactnum 0, the result is Exactnum 0 too.

Examples:
```check:
num-sin(0) is 0
num-sin(1) is%(within-abs(0.01)) 0.84
end```

num-cos :: (n :: Number) -> Number

Returns the cosine of the argument, usually as a Roughnum. If the argument is Exactnum 0, the result is Exactnum 1.

Examples:
```check:
num-cos(0) is 1
num-cos(1) is%(within-abs(0.01)) 0.54
end```

num-tan :: (n :: Number) -> Number

Returns the tangent of the argument, usually as a Roughnum. If the argument is Exactnum 0, the result is Exactnum 1.

Examples:
```check:
num-tan(0) is 0
num-tan(1) is%(within-abs(0.01)) 1.56
end```

num-asin :: (n :: Number) -> Number

Returns the arcsine of the argument as an angle in radians in the range [-pi/2, pi/2], usually as a Roughnum. If the argument is Exactnum 0, the result is Exactnum 0.

Examples:
```check:
num-asin(0) is 0
num-asin(0.84) is%(within-abs(0.01)) 1
end```

num-acos :: (n :: Number) -> Number

Returns the arccosine of the argument as an angle in radians in the range [0, pi], usually as a Roughnum. However, if the argument is Exactnum 1, the result is Exactnum 0.

Examples:
```check:
num-acos(1) is 0
num-acos(0.54) is%(within-abs(0.01)) 1
end```

num-atan :: (n :: Number) -> Number

Returns the arctangent of the argument as an angle in radians in the range (-pi/2, pi/2), usually as a Roughnum. However, if the argument is Exactnum 0, the result is Exactnum 0.

Examples:
```check:
num-atan(0) is 0
num-atan(1) is-roughly (3.141592 * 1/4) # 45 degrees = pi/4 radians
num-atan(-1) is-roughly (-3.141592 * 1/4) # 315 degrees = -pi/4 radians
num-atan(100000000000) is-roughly (3.141592 / 2) # 90 degrees = pi/2 radians
num-atan(-100000000000) is-roughly (-3.141592 / 2) # 270 degrees = -pi/2 radians
end```

num-atan2 :: (dy :: Number, dx :: Number) -> Number

The num-atan function takes a tangent value and returns a corresponding angle, but it is not clear which angle to return: for example, both num-tan(3.141592 * 1/4) and num-tan(3.141592 * 5/4) have a tangent of ~1. The num-atan2 function produces an angle in radians in the range [0, 2pi], where the tangent value is the ratio of the two arguments: the two arguments represent the (signed) height and width of a triangle whose angle is unknown (i.e., their ratio is the "rise over run", defining the tangent of that angle). The return value of num-atan2 chooses which angle to return based on the following table:

Examples:
```check:
num-atan2(0, 1) is 0
num-atan2(1, 1) is-roughly (3.141592 * 1/4) # 45 degrees
num-atan2(1, -1) is-roughly (3.141592 * 3/4) # 135 degrees
num-atan2(-1, -1) is-roughly (3.141592 * 5/4) # 225 degrees
num-atan2(-1, 1) is-roughly (3.141592 * 7/4) # 315 degrees
num-atan2(1, 0) is-roughly (3.141592 * 1/2) # 90 degrees
num-atan2(-1, 0) is-roughly (3.141592 * 3/2) # 270 degrees
end```

num-modulo :: (n :: Number, divisor :: Number) -> Number

Returns the modulus of the first argument with respect to the second, i.e. the remainder when dividing the first number by the second.

Examples:
```check:
num-modulo(5, 2) is 1
num-modulo(-5, 2) is 1
num-modulo(-5, -2) is -1
num-modulo(7, 3) is 1
num-modulo(0, 5) is 0
num-modulo(-7, 3) is 2
end```

It is useful for calculating if one number is a multiple of another, by checking for a zero remainder.

Examples:
```fun is-even(n :: Number) -> Boolean:
num-modulo(n, 2) == 0
where:
is-even(6) is true
is-even(3) is false
end```

Returns the integer part of its argument by cutting off any decimal part. Does not do any rounding.

Examples:
```check:
num-truncate(3.14) is 3
num-truncate(-3.14) is -3
num-truncate(~3.14) is-roughly ~3
num-truncate(~-3.14) is-roughly ~-3
end```

num-sqrt :: (n :: Number) -> Number

Returns the square root of the given argument. If the argument is an Exactnum and a perfect square, the result is an Exactnum, otherwise, it is a Roughnum.

Examples:
```check:
num-sqrt(4) is 2
num-sqrt(5) is%(within-abs(0.001)) ~2.236
num-sqrt(~4) is%(within-abs(0.001)) ~2
num-sqrt(~5) is%(within-abs(0.001)) ~2.236
num-sqrt(0.04) is 1/5
num-sqrt(-1) raises "negative argument"
end```

num-sqr :: (n :: Number) -> Number

Returns the square of the given argument.

Examples:
```check:
num-sqr(4) is 16
num-sqr(5) is 25
num-sqr(-4) is 16
num-sqr(~4) is-roughly ~16
num-sqr(0.04) is 1/625
end```

Returns the smallest integer Exactnum greater than or equal to the argument.

Examples:
```check:
num-ceiling(4.2) is 5
num-ceiling(-4.2) is -4
end```

Returns the largest integer Exactnum less than or equal to the argument.

Examples:
```check:
num-floor(4.2) is 4
num-floor(-4.2) is -5
end```

Returns the closest integer Exactnum to the argument.

Examples:
```check:
num-round(4.2) is 4
num-round(4.8) is 5
num-round(-4.2) is -4
num-round(-4.8) is -5
end```

If the argument is midway between integers, returns the integer further away from zero.

Examples:
```check:
num-round(3.5) is 4
num-round(2.5) is 3
end```

Similar to num-round, except that if the argument is midway between integers, returns the even integer Exactnum.

Examples:
```check:
num-round-even(3.5) is 4
num-round-even(2.5) is 2
end```

num-log :: (n :: Number) -> Number

Returns the natural logarithm (ln) of the argument, usually as a Roughnum. If the argument is Exactnum 1, the result is Exactnum 0. If the argument is non-positive, an error is thrown.

Examples:
```check:
num-log(1) is 0
num-log(0) raises "non-positive argument"
num-log(-1) raises "non-positive argument"
num-log(2.718281828) is%(within-abs(0.01)) 1
num-log(10) is%(within-abs(0.1)) 2.3
end```

num-exp :: (n :: Number) -> Number

Returns e raised to the argument, usually as a Roughnum. However, if the argument is Exactnum 0, the result is Exactnum 1.

Examples:
```check:
num-exp(-1) is%(within-abs(0.0001)) (1 / num-exp(1))
num-exp(0) is 1
num-exp(1) is%(within-abs(0.0001)) 2.718281828
num-exp(3) is%(within-abs(0.0001)) num-expt(2.718281828, 3)
num-exp(710) raises "exp: argument too large: 710"
end```

num-expt :: (base :: Number, exponent :: Number) -> Number

Returns the first argument raised to the second argument. An error is thrown if the first argument is 0 and the second is negative. If the first argument is Exactnum 0 or 1, or the second argument is Exactnum 0, then the result is an Exactnum even if the other argument is a Roughnum.

Examples:
```check:
num-expt(3, 0) is 1
num-expt(1, 3) is 1
num-expt(0, 0) is 1
num-expt(0, 3) is 0
num-expt(0, -3) raises "division by zero"
num-expt(2, 3) is 8
num-expt(2, -3) is 1/8
end```

Given a number, returns the Roughnum version.

Examples:
```check:
num-is-roughnum(num-to-roughnum(3.14)) is true
num-is-roughnum(num-to-roughnum(~3.14)) is true
end```

Returns true if argument is an Exactnum integer.

Examples:
```check:
num-is-integer(2) is true
num-is-integer(1/2) is false
num-is-integer(1.609) is false
num-is-integer(~2) is false
end```

Returns true if argument is an Exactnum rational.

Examples:
```check:
num-is-rational(2) is true
num-is-rational(1/2) is true
num-is-rational(1.609) is true
num-is-rational(~2) is false
end```

Returns true if argument is a Roughnum.

Examples:
```check:
num-is-roughnum(2) is false
num-is-roughnum(1/2) is false
num-is-roughnum(1.609) is false
num-is-roughnum(~2) is true
end```

Returns true if argument is greater than zero.

Examples:
```check:
num-is-positive(~-2) is false
num-is-positive(-2) is false
num-is-positive(0) is false
num-is-positive(-0) is false
num-is-positive(2) is true
num-is-positive(~2) is true
end```

Returns true if argument is less than zero.

Examples:
```check:
num-is-negative(~-2) is true
num-is-negative(-2) is true
num-is-negative(0) is false
num-is-negative(-0) is false
num-is-negative(2) is false
num-is-negative(~2) is false
end```

Returns true if argument is less than or equal to zero.

Examples:
```check:
num-is-non-positive(~-2) is true
num-is-non-positive(-2) is true
num-is-non-positive(0) is true
num-is-non-positive(-0) is true
num-is-non-positive(2) is false
num-is-non-positive(~2) is false
end```

Returns true if argument is greater than or equal to zero.

Examples:
```check:
num-is-non-negative(~-2) is false
num-is-non-negative(-2) is false
num-is-non-negative(0) is true
num-is-non-negative(-0) is true
num-is-non-negative(2) is true
num-is-non-negative(~2) is true
end```

Returns a String representing a literal form of the number.

Examples:
```check:
num-to-string(2.5) is "5/2"
num-to-string(2) is "2"
num-to-string(2/3) is "2/3"
num-to-string(~2.718) is "~2.718"
num-to-string(~6.022e23) is "~6.022e+23"
end```

num-to-string-digits :: (n :: Number, digits :: Number) -> String

Converts the number to a String, providing digits precision in the output. If digits is positive, provides that many digits to the right of the decimal point (including adding zeroes beyond the actual precision of the number). If digits is negative, rounds that many positions to the left of the decimal, replacing them with zeroes.

Note that num-to-string-digits is only for formatting, and its output’s apparent precision may be unrelated to the actual precision of the input number, which may have been an approximation, or unrepresentable in decimal.

Examples:
```check:
num-to-string-digits(2/3, 3) is "0.667"
num-to-string-digits(-2/3, 3) is "-0.667"
num-to-string-digits(5, 2) is "5.00"
num-to-string-digits(5, 0) is "5"
num-to-string-digits(555, -2) is "600"
end```

num-within-abs :: (tol :: Number) -> (Any, Any -> Boolean)

Returns a predicate that checks if the difference of its two arguments is less than tol.

Examples:
```check:
1  is%(num-within-abs(0.1))       1
1  is%(num-within-abs(0.1))      ~1
~3  is%(num-within-abs(0.1))      ~3
~2  is-not%(num-within-abs(0.1))  ~3
~2  is%(num-within-abs(1.1))      ~3
~2  is%(num-within-abs(~1))       ~3
2  is%(num-within-abs(1))        ~3
5  is%(num-within-abs(4))         3

num-within-abs(-0.1)(1, 1.05) raises "negative tolerance"
end```

num-within-rel :: (tol :: Number) -> (Any, Any -> Boolean)

Returns a predicate that checks that its first number argument is no more than the fraction tol off from its second argument.

This function is a.k.a. num-within.

Examples:
```check:
100000 is%(num-within-rel(0.1)) 95000
100000 is-not%(num-within-rel(0.1)) 85000
end```

within :: (tol :: Number) -> (Any, Any -> Boolean)

within-abs :: (tol :: Number) -> (Any, Any -> Boolean)

within-rel :: (tol :: Number) -> (Any, Any -> Boolean)

within-abs-now :: (tol :: Number) -> (Any, Any -> Boolean)

within-rel-now :: (tol :: Number) -> (Any, Any -> Boolean)

These comparison functions compare both numbers and structures, and are documented in Bounded Equalities.

within-abs3 :: (tol :: Number) -> (Any, Any -> EqualityResult)

within-rel3 :: (tol :: Number) -> (Any, Any -> EqualityResult)

These comparison functions are like the ones above, but return EqualityResults, and are documented in Total Equality Functions (Avoiding Incomparability Errors).

##### 3.2.4Random Numbers

num-random :: (max :: Number) -> Number

Returns a pseudo-random positive integer from 0 to max - 1.

Examples:
```check:
fun between(min, max):
lam(v): (v >= min) and (v <= max) end
end
for each(i from range(0, 100)):
block:
n = num-random(10)
print(n)
n satisfies between(0, 10 - 1)
end
end
end```

Sets the random seed. Setting the seed to a particular number makes all future uses of random produce the same sequence of numbers. Useful for testing and debugging functions that have random behavior.

Examples:
```check:
num-random-seed(0)
n = num-random(1000)
n2 = num-random(1000)

n is-not n2

num-random-seed(0)
n3 = num-random(1000)
n3 is n
n4 = num-random(1000)
n4 is n2
end```

The random seed is set globally. If it is set in tests in a game or another program that should not run the same way every time, add an identifier you can set as a flag indicating if you are running the code in testing or production.

Examples:
```
IS-TESTING = true  # change as needed

when IS-TESTING:
num-random-seed(...)
end```

##### 3.2.5Other Number Functions

A few other number functions are useful in limited cases that don’t come up in most programs.

Returns true if the argument is represented directly as a primitive JavaScript number (i.e., JavaScript double).

Examples:
```check:
num-is-fixnum(10) is true
num-is-fixnum(~10) is false
num-is-fixnum(1000000000000000) is true
num-is-fixnum(10000000000000000) is false
num-is-fixnum(1.5) is false
end```

Pyret represents Exactnums that are non-integers as tuples, and hence even small rationals such as 1.5 are considered non-fixnum, although they could be represented as JavaScript doubles.

Given a Roughnum, returns an Exactnum number most equal to it. Given an Exactnum num, returns it directly.

It is not good practice to indiscriminately convert Roughnums to Exactnums to make comparison easier. Use within() or is-roughly.

Examples:
```check:
num-sqrt(2) is%(within-abs(0.000001)) ~1.4142135623730951
num-exact(num-sqrt(2)) is 1.4142135623730951
num-to-rational(num-sqrt(2)) is 1.4142135623730951
end```