# Symbolic Expressions

## Lists of Expressions

Expressions can be grouped into lists with an arbitrary structure. Important special cases are scalar, vector, and tensor expressions.

### Creating Lists

• Vector expressions
```Expr v = List(vx, vy, vz);
```
• Heterogeneous lists
```// Form vector {vx, vy, vz}
Expr v = List(vx, vy, vz);
Expr q = new TestFunction(new Lagrange(1));
// Form heterogeneous list {{vx, vy, vz}, q}
Expr state = List(v, q);
```

### Probing Lists

• Use Expr::size() to find the number of elements at the top level (i.e., first index).
```// Form vector {vx, vy, vz}
Expr v = List(vx, vy, vz);
Expr q = new TestFunction(new Lagrange(1));
// Form heterogeneous list {{vx, vy, vz}, q}
Expr state = List(v, q);
// Check top-level size of state list {{vx, vy, vz}, q}
int stateSize = state.size(); // returns 2
```
• Use Expr::totalSize() to find the total number of elements in a list
```// Check total size of state list
int totalSize = state.totalSize(); // returns 4
```

## Arithmetic Operations

Arithmetic operations are implemented using overloaded operators

```Expr sum = x + y;
```
The operands must have identical list structures; otherwise, a runtime error is thrown.

• Subtraction
```Expr diff = x - y;
```
The operands must have identical list structures; otherwise, a runtime error is thrown.

• Multiplication
```Expr product = x * y;
```
The operands must have list structures such that the product can be interpreted as a scalar-vector product or as an inner product between vectors or tensors; otherwise, a runtime error is thrown. The multiplication operator is also used to represent the application of a differential operator.

• Division
```Expr quotient = x / y;
```
The denominator must be scalar-valued; otherwise, a runtime error is thrown.

## Arithmetic Operations

Arithmetic operations are implemented using overloaded operators

## Expression Subtypes

The user-level expression subtypes are listed below, along with examples of their use.
• UnknownFunction - Represents an unknown function in a finite-element problem. Unknown function ctors have one mandatory and one optional argument: a BasisFamily (mandatory) and a string name (optional). The name is used only for making diagnostic output more readable and has no effect on calculations. If the name argument is not given, an arbitrary function name will be picked.

Unknown functions can be scalar-valued or vector valued. To create a vector-valued unknown function, construct with a vector-valued BasisFamily.

Example of creation of a scalar-valued unknown function:

```Expr u = new UnknownFunction(new Lagrange(1));
```

Example of creation of a vector-valued unknown function:

```Expr u = new UnknownFunction(List(new Lagrange(1), new Lagrange(1)));
```

• TestFunction - Represents a test function in a finite-element problem. Constructor arguments are identical to those for UnknownFunction.

Example of creation of a scalar-valued test function:

```Expr v = new TestFunction(new Lagrange(1));
```

Example of creation of a vector-valued test function:

```Expr u = new TestFunction(List(new Lagrange(1), new Lagrange(1)));
```

• Derivative - Represents a spatial derivative operator. The constructor takes an integer argument to specify the direction of partial differentiation. Spatial derivatives are applied using the multiplication operator.
```// Create a differential operator in the zeroth (x) direction.
Expr dx = new Derivative(0);
Expr convect = (u*dx)*u;
```
Derivative expressions are scalar valued. However, vector differential operators can be created using the List operator. For example,
```Expr dx = new Derivative(0);
Expr dy = new Derivative(1);
```

• CoordExpr - Represents a coordinate functions.
```Expr x = new CoordExpr(0);
Expr y = new CoordExpr(1);
Expr r = sqrt(x*x + y*y);
```
Coordinate expressions are scalar valued.

• CellDiameterExpr - Represents the diameter of a cell. Cell diameters are often used in stabilized methods.
```Expr h = new CellDiameterExpr();