# This is a line comment. It can span over
# multiple lines

# 1. Imports

use prelude        # This is not necessary. The 'prelude'
                   # module will always be loaded upon startup

use units::stoney  # Load a specific module

# 2. Numbers

12345       # integer notation
12_345      # optional decimal separators

0.234       # floating point notation
.234        # without the leading zero

1.234e15    # scientific notation
1.234e+15
1e-9
1.0e-9

0x2A        # hexadecimal
0o52        # octal
0b101010    # binary

NaN         # Not a number
inf         # Infinity

# 3. Simple expressions

3 + (4 - 3)       # Addition and subtraction

1920 / 16 * 9     # Multiplication, division
1920 ÷ 16 × 9     # Unicode-style, '·' is also multiplication
2 pi              # Whitespace is implicit multiplication
meter per second  # 'per' keyword can be used for division

2^3               # Exponentiation
2**3              # Python-style
2³                # Unicode exponents
2^-3              # Negative exponents

mod(17, 4)        # Modulo

3 in -> cm        # Unit conversion, can also be → or ➞
3 in to cm        # Unit conversion with the 'to' keyword

cos(pi/3 + pi)    # Call mathematical functions
pi/3 + pi // cos  # Same, 'arg // f' is equivalent to 'f(arg)'
                  # The '//' operator has the lowest precedence
                  # which makes it very useful for interactive
                  # terminals (press up-arrow, and add '// f')

# 4. Constants

let n = 4                          # Simple numerical constant
let q1 = 2 m/s                     # Right hand side can be any expression
let q2: Velocity = 2 m/s           # With optional type annotation
let q3: Length / Time = 2 m/s      # more complex type annotation

# 5. Function definitions

fn foo(z: Scalar) -> Scalar = 2 * z + 3                   # A simple function
fn speed(len: Length, dur: Time) -> Velocity = len / dur  # Two parameters
fn my_sqrt<T>(q: T^2) -> T = q^(1/2)                      # A generic function
fn is_non_negative(x: Scalar) -> Bool = x ≥ 0             # Returns a bool

# 6. Dimension definitions

dimension Fame                            # A new base dimension
dimension Deceleration = Length / Time^2  # A new derived dimension

# 7. Unit definitions

@aliases(quorks)                 # Optional aliases-decorator
unit quork = 0.35 meter          # A new derived unit

@metric_prefixes                 # Optional decorator to allow 'milliclonk', etc.
@aliases(ck: short)              # short aliases can be used with short prefixes (mck)
unit clonk: Time = 0.2 seconds   # Optional type annotation

@metric_prefixes
@aliases(wh: short)
unit warhol: Fame                # New base unit for the "Fame" dimension

unit thing                       # New base unit with automatically generated
                                 # base dimension "Thing"

# 8. Conditionals

fn bump(x: Scalar) -> Scalar =   # The construct 'if <cond> then <expr> else <expr>'
  if x >= 0 && x <= 1            # is an expression, not a statement. It can span
    then 1                       # multiple lines.
    else 0

# 9. Procedures

print(2 kilowarhol)              # Print the value of an expression
print("hello world")             # Print a message
print("value of pi = {pi}")      # String interpolation
print("sqrt(10) = {sqrt(10)}")   # Expressions in string interpolation
print("value of π ≈ {π:.3}")     # Format specifiers

assert(1 yard < 1 meter)         # Assertion

assert_eq(1 ft, 12 in)           # Assert that two quantities are equal
assert_eq(1 yd, 1 m, 10 cm)      # Assert that two quantities are equal, up to
                                 # the given precision
type(2 m/s)                      # Print the type of an expression


# 10. Structs

struct Element {                 # Define a struct
    name: String,
    atomic_number: Scalar,
    density: MassDensity,
}

let hydrogen = Element {         # Instantiate it
    name: "Hydrogen",
    atomic_number: 1,
    density: 0.08988 g/L,
}

hydrogen.density                 # Access the field of a struct
