<!-- Autogenerated by mlir-tblgen; don't manually edit -->
# 'FHELinalg' Dialect

High Level Fully Homomorphic Encryption Linalg dialect
A dialect for representation of high level linalg operations on fully homomorphic ciphertexts.



## Operation definition

### `FHELinalg.add_eint_int` (::mlir::concretelang::FHELinalg::AddEintIntOp)

Returns a tensor that contains the addition of a tensor of encrypted integers and a tensor of clear integers.

Performs an addition following the broadcasting rules between a tensor of encrypted integers and a tensor of clear integers.
The width of the clear integers must be less than or equal to the width of encrypted integers.

Examples:
```mlir
// Returns the term-by-term addition of `%a0` with `%a1`
"FHELinalg.add_eint_int"(%a0, %a1) : (tensor<4x!FHE.eint<4>>, tensor<4xi5>) -> tensor<4x!FHE.eint<4>>

// Returns the term-by-term addition of `%a0` with `%a1`, where dimensions equal to one are stretched.
"FHELinalg.add_eint_int"(%a0, %a1) : (tensor<4x1x4x!FHE.eint<4>>, tensor<1x4x4xi5>) -> tensor<4x4x4x!FHE.eint<4>>

// Returns the addition of a 3x3 matrix of encrypted integers and a 3x1 matrix (a column) of integers.
//
// [1,2,3]   [1]   [2,3,4]
// [4,5,6] + [2] = [6,7,8]
// [7,8,9]   [3]   [10,11,12]
//
// The dimension #1 of operand #2 is stretched as it is equal to 1.
"FHELinalg.add_eint_int"(%a0, %a1) : (tensor<3x3x!FHE.eint<4>>, tensor<3x1xi5>) -> tensor<3x3x!FHE.eint<4>>

// Returns the addition of a 3x3 matrix of encrypted integers and a 1x3 matrix (a line) of integers.
//
// [1,2,3]             [2,4,6]
// [4,5,6] + [1,2,3] = [5,7,9]
// [7,8,9]             [8,10,12]
//
// The dimension #2 of operand #2 is stretched as it is equal to 1.
"FHELinalg.add_eint_int"(%a0, %a1) : (tensor<3x3x!FHE.eint<4>>, tensor<1x3xi5>) -> tensor<3x3x!FHE.eint<4>>

// Same behavior as the previous one, but as the dimension #2 is missing of operand #2.
"FHELinalg.add_eint_int(%a0, %a1)" : (tensor<3x4x!FHE.eint<4>>, tensor<3xi5>) -> tensor<4x4x4x!FHE.eint<4>>

```

Traits: AlwaysSpeculatableImplTrait, TensorBinaryEintInt, TensorBroadcastingRules

Interfaces: Binary, BinaryEintInt, ConditionallySpeculatable, NoMemoryEffect (MemoryEffectOpInterface)

Effects: MemoryEffects::Effect{}

#### Operands:

| Operand | Description |
| :-----: | ----------- |
| `lhs` | 
| `rhs` | 

#### Results:

| Result | Description |
| :----: | ----------- |
&laquo;unnamed&raquo; | 

### `FHELinalg.add_eint` (::mlir::concretelang::FHELinalg::AddEintOp)

Returns a tensor that contains the addition of two tensor of encrypted integers.

Performs an addition following the broadcasting rules between two tensors of encrypted integers.
The width of the encrypted integers must be equal.

Examples:
```mlir
// Returns the term-by-term addition of `%a0` with `%a1`
"FHELinalg.add_eint"(%a0, %a1) : (tensor<4x!FHE.eint<4>>, tensor<4x!FHE.eint<4>>) -> tensor<4x!FHE.eint<4>>

// Returns the term-by-term addition of `%a0` with `%a1`, where dimensions equal to one are stretched.
"FHELinalg.add_eint"(%a0, %a1) : (tensor<4x1x4x!FHE.eint<4>>, tensor<1x4x4x!FHE.eint<4>>) -> tensor<4x4x4x!FHE.eint<4>>

// Returns the addition of a 3x3 matrix of encrypted integers and a 3x1 matrix (a column) of encrypted integers.
//
// [1,2,3]   [1]   [2,3,4]
// [4,5,6] + [2] = [6,7,8]
// [7,8,9]   [3]   [10,11,12]
//
// The dimension #1 of operand #2 is stretched as it is equal to 1.
"FHELinalg.add_eint"(%a0, %a1) : (tensor<3x3x!FHE.eint<4>>, tensor<3x1x!FHE.eint<4>>) -> tensor<3x3x!FHE.eint<4>>

// Returns the addition of a 3x3 matrix of encrypted integers and a 1x3 matrix (a line) of encrypted integers.
//
// [1,2,3]             [2,4,6]
// [4,5,6] + [1,2,3] = [5,7,9]
// [7,8,9]             [8,10,12]
//
// The dimension #2 of operand #2 is stretched as it is equal to 1.
"FHELinalg.add_eint"(%a0, %a1) : (tensor<3x3x!FHE.eint<4>>, tensor<1x3x!FHE.eint<4>>) -> tensor<3x3x!FHE.eint<4>>

// Same behavior as the previous one, but as the dimension #2 of operand #2 is missing.
"FHELinalg.add_eint"(%a0, %a1) : (tensor<3x3x!FHE.eint<4>>, tensor<3x!FHE.eint<4>>) -> tensor<3x3x!FHE.eint<4>>
```

Traits: AlwaysSpeculatableImplTrait, TensorBinaryEint, TensorBroadcastingRules

Interfaces: BinaryEint, ConditionallySpeculatable, NoMemoryEffect (MemoryEffectOpInterface)

Effects: MemoryEffects::Effect{}

#### Operands:

| Operand | Description |
| :-----: | ----------- |
| `lhs` | 
| `rhs` | 

#### Results:

| Result | Description |
| :----: | ----------- |
&laquo;unnamed&raquo; | 

### `FHELinalg.apply_lookup_table` (::mlir::concretelang::FHELinalg::ApplyLookupTableEintOp)

Returns a tensor that contains the result of the lookup on a table.

For each encrypted index, performs a lookup table of clear integers.

```mlir
// The result of this operation, is a tensor that contains the result of a lookup table.
// i.e. %res[i, ..., k] = %lut[%t[i, ..., k]]
%res = FHELinalg.apply_lookup_table(%t, %lut): tensor<DNx...xD1x!FHE.eint<$p>>, tensor<D2^$pxi64> -> tensor<DNx...xD1x!FHE.eint<$p>>
```

The `%lut` argument must be a tensor with one dimension, where its dimension is `2^p` where `p` is the width of the encrypted integers.

Examples:
```mlir

// Returns the lookup of 3x3 matrix of encrypted indices of with 2 on a table of size 4=2² of clear integers.
//
// [0,1,2]                 [1,3,5]
// [3,0,1] lut [1,3,5,7] = [7,1,3]
// [2,3,0]                 [5,7,1]
"FHELinalg.apply_lookup_table"(%t, %lut) : (tensor<3x3x!FHE.eint<2>>, tensor<4xi64>) -> tensor<3x3x!FHE.eint<3>>
```

Traits: AlwaysSpeculatableImplTrait

Interfaces: ConditionallySpeculatable, ConstantNoise, NoMemoryEffect (MemoryEffectOpInterface)

Effects: MemoryEffects::Effect{}

#### Operands:

| Operand | Description |
| :-----: | ----------- |
| `t` | 
| `lut` | 

#### Results:

| Result | Description |
| :----: | ----------- |
&laquo;unnamed&raquo; | 

### `FHELinalg.apply_mapped_lookup_table` (::mlir::concretelang::FHELinalg::ApplyMappedLookupTableEintOp)

Returns a tensor that contains the result of the lookup on a table, using a different lookup table for each element, specified by a map.

Performs for each encrypted index a lookup table of clear integers. Multiple lookup tables are passed, and the application of lookup tables
is performed following the broadcasting rules. The precise lookup is specified by a map.

```mlir
// The result of this operation, is a tensor that contains the result of the lookup on different tables.
// i.e. %res[i, ..., k] = %luts[ %map[i, ..., k] ][ %t[i, ..., k] ]
%res = FHELinalg.apply_mapped_lookup_table(%t, %luts, %map): tensor<DNx...xD1x!FHE.eint<$p>>, tensor<DM x ^$p>, tensor<DNx...xD1xindex> -> tensor<DNx...xD1x!FHE.eint<$p>>
```

Examples:
```mlir

// Returns the lookup of 3x2 matrix of encrypted indices of width 2 on a vector of 2 tables of size 4=2^2 of clear integers.
//
// [0,1]                                 [0, 1] = [1,2]
// [3,0] lut [[1,3,5,7], [0,2,4,6]] with [0, 1] = [7,0]
// [2,3]                                 [0, 1] = [5,6]
"FHELinalg.apply_mapped_lookup_table"(%t, %luts, %map) : (tensor<3x2x!FHE.eint<2>>, tensor<2x4xi64>, tensor<3x2xindex>) -> tensor<3x2x!FHE.eint<3>>
```

Others examples:
// [0,1]                                 [1, 0] = [3,2]
// [3,0] lut [[1,3,5,7], [0,2,4,6]] with [0, 1] = [7,0]
// [2,3]                                 [1, 0] = [4,7]

// [0,1]                                 [0, 0] = [1,3]
// [3,0] lut [[1,3,5,7], [0,2,4,6]] with [1, 1] = [6,0]
// [2,3]                                 [1, 0] = [4,7]

// [0,1]                                 [0]    = [1,3]
// [3,0] lut [[1,3,5,7], [0,2,4,6]] with [1]    = [6,0]
// [2,3]                                 [0]    = [5,7]

// [0,1]                                        = [1,2]
// [3,0] lut [[1,3,5,7], [0,2,4,6]] with [0, 1] = [7,0]
// [2,3]                                        = [5,6]


Traits: AlwaysSpeculatableImplTrait

Interfaces: ConditionallySpeculatable, ConstantNoise, NoMemoryEffect (MemoryEffectOpInterface)

Effects: MemoryEffects::Effect{}

#### Operands:

| Operand | Description |
| :-----: | ----------- |
| `t` | 
| `luts` | 
| `map` | 

#### Results:

| Result | Description |
| :----: | ----------- |
&laquo;unnamed&raquo; | 

### `FHELinalg.apply_multi_lookup_table` (::mlir::concretelang::FHELinalg::ApplyMultiLookupTableEintOp)

Returns a tensor that contains the result of the lookup on a table, using a different lookup table for each element.

Performs for each encrypted index a lookup table of clear integers. Multiple lookup tables are passed, and the application of lookup tables
is performed following the broadcasting rules.

```mlir
// The result of this operation, is a tensor that contains the result of the lookup on different tables.
// i.e. %res[i, ..., k] = [ %luts[i][%t[i]], ..., %luts[k][%t[k]] ]
%res = FHELinalg.apply_multi_lookup_table(%t, %lut): tensor<DNx...xD1x!FHE.eint<$p>>, tensor<DMx...xD1xD2^$pxi64> -> tensor<DNx...xD1x!FHE.eint<$p>>
```

The `%luts` argument should be a tensor with M dimension, where the first M-1 dimensions are broadcastable with the N dimensions of the encrypted tensor,
and where the last dimension dimension is equal to `2^p` where `p` is the width of the encrypted integers.

Examples:
```mlir

// Returns the lookup of 3x2 matrix of encrypted indices of width 2 on a vector of 2 tables of size 4=2² of clear integers.
// The tables are broadcasted along the first dimension of the tensor.
//
// [0,1]                            = [1,2]
// [3,0] lut [[1,3,5,7], [0,2,4,6]] = [7,0]
// [2,3]                            = [5,6]
"FHELinalg.apply_multi_lookup_table"(%t, %luts) : (tensor<3x2x!FHE.eint<2>>, tensor<2x4xi64>) -> tensor<3x2x!FHE.eint<3>>
```

```mlir

// Returns the lookup of a vector of 3 encrypted indices of width 2 on a vector of 3 tables of size 4=2² of clear integers.
//
// [3,0,1] lut [[1,3,5,7], [0,2,4,6], [1,2,3,4]] = [7,0,2]
"FHELinalg.apply_multi_lookup_table"(%t, %luts) : (tensor<3x!FHE.eint<2>>, tensor<3x4xi64>) -> tensor<3x!FHE.eint<3>>
```

Traits: AlwaysSpeculatableImplTrait

Interfaces: ConditionallySpeculatable, ConstantNoise, NoMemoryEffect (MemoryEffectOpInterface)

Effects: MemoryEffects::Effect{}

#### Operands:

| Operand | Description |
| :-----: | ----------- |
| `t` | 
| `luts` | 

#### Results:

| Result | Description |
| :----: | ----------- |
&laquo;unnamed&raquo; | 

### `FHELinalg.concat` (::mlir::concretelang::FHELinalg::ConcatOp)

Concatenates a sequence of tensors along an existing axis.

Concatenates several tensors along a given axis.

Examples:

```mlir
"FHELinalg.concat"(%a, %b) { axis = 0 } : (tensor<3x3x!FHE.eint<4>>, tensor<3x3x!FHE.eint<4>>) -> tensor<6x3x!FHE.eint<4>>
//
//        ( [1,2,3]  [1,2,3] )   [1,2,3]
// concat ( [4,5,6], [4,5,6] ) = [4,5,6]
//        ( [7,8,9]  [7,8,9] )   [7,8,9]
//                               [1,2,3]
//                               [4,5,6]
//                               [7,8,9]
//
```

```mlir
"FHELinalg.concat"(%a, %b) { axis = 1 } : (tensor<3x3x!FHE.eint<4>>, tensor<3x3x!FHE.eint<4>>) -> tensor<3x6x!FHE.eint<4>>
//
//        ( [1,2,3]  [1,2,3] )   [1,2,3,1,2,3]
// concat ( [4,5,6], [4,5,6] ) = [4,5,6,4,5,6]
//        ( [7,8,9]  [7,8,9] )   [7,8,9,7,8,9]
//
```

Traits: AlwaysSpeculatableImplTrait

Interfaces: ConditionallySpeculatable, NoMemoryEffect (MemoryEffectOpInterface)

Effects: MemoryEffects::Effect{}

#### Attributes:

| Attribute | MLIR Type | Description |
| :-------: | :-------: | ----------- |
| `axis` | ::mlir::IntegerAttr | 64-bit signless integer attribute

#### Operands:

| Operand | Description |
| :-----: | ----------- |
| `ins` | 

#### Results:

| Result | Description |
| :----: | ----------- |
| `out` | 

### `FHELinalg.conv2d` (::mlir::concretelang::FHELinalg::Conv2dOp)

Returns the 2D convolution of a tensor in the form NCHW with weights in the form FCHW


Traits: AlwaysSpeculatableImplTrait

Interfaces: Binary, BinaryEintInt, ConditionallySpeculatable, NoMemoryEffect (MemoryEffectOpInterface)

Effects: MemoryEffects::Effect{}

#### Attributes:

| Attribute | MLIR Type | Description |
| :-------: | :-------: | ----------- |
| `padding` | ::mlir::DenseIntElementsAttr | 64-bit signless integer elements attribute
| `strides` | ::mlir::DenseIntElementsAttr | 64-bit signless integer elements attribute
| `dilations` | ::mlir::DenseIntElementsAttr | 64-bit signless integer elements attribute
| `group` | ::mlir::IntegerAttr | 64-bit signless integer attribute

#### Operands:

| Operand | Description |
| :-----: | ----------- |
| `input` | 
| `weight` | 
| `bias` | 

#### Results:

| Result | Description |
| :----: | ----------- |
&laquo;unnamed&raquo; | 

### `FHELinalg.dot_eint_int` (::mlir::concretelang::FHELinalg::Dot)

Returns the encrypted dot product between a vector of encrypted integers and a vector of clean integers.

Performs a dot product between a vector of encrypted integers and a vector of clear integers.

Examples:
```mlir
// Returns the dot product of `%a0` with `%a1`
"FHELinalg.dot_eint_int"(%a0, %a1) : (tensor<4x!FHE.eint<4>>, tensor<4xi5>) -> !FHE.eint<4>

```

Traits: AlwaysSpeculatableImplTrait

Interfaces: Binary, BinaryEintInt, ConditionallySpeculatable, NoMemoryEffect (MemoryEffectOpInterface)

Effects: MemoryEffects::Effect{}

#### Operands:

| Operand | Description |
| :-----: | ----------- |
| `lhs` | 
| `rhs` | 

#### Results:

| Result | Description |
| :----: | ----------- |
| `out` | 

### `FHELinalg.dot_eint_eint` (::mlir::concretelang::FHELinalg::DotEint)

Returns the encrypted dot product between two vectors of encrypted integers.

Performs a dot product between two vectors of encrypted integers.

Examples:
```mlir
// Returns the dot product of `%a0` with `%a1`
"FHELinalg.dot_eint_eint"(%a0, %a1) : (tensor<4x!FHE.eint<4>>, tensor<4x!FHE.eint<4>>) -> !FHE.eint<4>

```

Traits: AlwaysSpeculatableImplTrait

Interfaces: ConditionallySpeculatable, NoMemoryEffect (MemoryEffectOpInterface)

Effects: MemoryEffects::Effect{}

#### Operands:

| Operand | Description |
| :-----: | ----------- |
| `lhs` | 
| `rhs` | 

#### Results:

| Result | Description |
| :----: | ----------- |
| `out` | 

### `FHELinalg.from_element` (::mlir::concretelang::FHELinalg::FromElementOp)

Creates a tensor with a single element.

Creates a tensor with a single element.

```mlir
"FHELinalg.from_element"(%a) : (Type) -> tensor<1xType>
```

Traits: AlwaysSpeculatableImplTrait

Interfaces: ConditionallySpeculatable, NoMemoryEffect (MemoryEffectOpInterface)

Effects: MemoryEffects::Effect{}

#### Operands:

| Operand | Description |
| :-----: | ----------- |
&laquo;unnamed&raquo; | any type

#### Results:

| Result | Description |
| :----: | ----------- |
&laquo;unnamed&raquo; | 

### `FHELinalg.lsb` (::mlir::concretelang::FHELinalg::LsbEintOp)

Extract the lowest significant bit at a given precision.

  This operation extract the lsb of a ciphertext tensor in a specific precision.

  Extracting only 1 bit:
  ```mlir
   // ok
   %lsb = "FHE.lsb"(%a): (tensor<1x!FHE.eint<4>)) -> (tensor<1x!FHE.eint<1>>)

  If you need to clear the lsb of the original ciphertext, you should extract to the same precision as the ciphertext.
  If you need to extract several bits, you can extract sequentially using explicit bitwidth change and bit clearing.

  Example:
  ```mlir
   // ok
   %a_lsb = "FHELinalg.lsb"(%a): (tensor<1x!FHE.eint<4>)) -> (tensor<1x!FHE.eint<4>))
   %a_lsb_cleared = "FHELinalg.sub_eint"(%a, %lsb) : (tensor<1x!FHE.eint<4>), tensor<1x!FHE.eint<4>)) -> (tensor<1x!FHE.eint<4>))
   %b = %a : tensor<1x!FHE.eint<3>>
   // now you can extract the next lsb from %b
   %b_lsb = "FHELinalg.lsb"(%b): (tensor<1x!FHE.eint<3>>) -> (tensor<1x!FHE.eint<3>>)
   // later if you need %b_lsb at the original position
   %b_lsb_as_in_a = %b_lsb : tensor<1x!FHE.eint<3>>
```

Traits: AlwaysSpeculatableImplTrait, TensorUnaryEint

Interfaces: ConditionallySpeculatable, ConstantNoise, NoMemoryEffect (MemoryEffectOpInterface), UnaryEint

Effects: MemoryEffects::Effect{}

#### Operands:

| Operand | Description |
| :-----: | ----------- |
| `input` | 

#### Results:

| Result | Description |
| :----: | ----------- |
| `output` | 

### `FHELinalg.matmul_eint_eint` (::mlir::concretelang::FHELinalg::MatMulEintEintOp)

Returns a tensor that contains the result of the matrix multiplication of a matrix of encrypted integers and a second matrix of encrypted integers.

Performs a matrix multiplication of a matrix of encrypted integers and a second matrix of encrypted integers.

The behavior depends on the arguments in the following way:

    - If both arguments are 2-D,
      they are multiplied like conventional matrices.

      e.g.,

      arg0: tensor<MxN> = [...]
      arg1: tensor<NxP> = [...]

      result: tensor<MxP> = [...]

    - If the first argument is a vector (1-D),
      it is treated as a matrix with a single row and standard matrix multiplication is performed.

      After standard matrix multiplication,
      the first dimension is removed from the result.

      e.g.,

      arg0: tensor<3> = [x, y, z]
      arg1: tensor<3xM> = [
          [_, _, ..., _, _],
          [_, _, ..., _, _],
          [_, _, ..., _, _],
      ]

      is treated as

      arg0: tensor<1x3> = [
          [x, y, z]
      ]
      arg1: tensor<3xM> = [
          [_, _, ..., _, _],
          [_, _, ..., _, _],
          [_, _, ..., _, _],
      ]

      and matrix multiplication is performed with the following form (1x3 @ 3xM -> 1xM)

      result: tensor<1xM> = [[_, _, ..., _, _]]

      finally, the first dimension is removed by definition so the result has the following form

      result: tensor<M>  = [_, _, ..., _, _]

    - If the second argument is 1-D,
      it is treated as a matrix with a single column and standard matrix multiplication is performed.

      After standard matrix multiplication,
      the last dimension is removed from the result.

      e.g.,

      arg0: tensor<Mx3> = [
          [_, _, _],
          [_, _, _],
          ...,
          [_, _, _],
          [_, _, _],
      ]
      arg1: tensor<3> = [x, y, z]

      is treated as

      arg0: tensor<Mx3> = [
          [_, _, _],
          [_, _, _],
          ...,
          [_, _, _],
          [_, _, _],
      ]
      arg1: tensor<3x1> = [
        [x],
        [y],
        [z],
      ]

      and matrix multiplication is performed with the following form (Mx3 @ 3x1 -> Mx1)

      result: tensor<Mx1> = [
        [_],
        [_],
          ...,
        [_],
        [_],
      ]

      finally, the last dimension is removed by definition so the result has the following form

      result: tensor<M> = [_, _, _]

    - If either argument is N-D where N > 2,
      the operation is treated as a collection of matrices residing in the last two indices and broadcasted accordingly.

      arg0: tensor<Kx1MxN> = [...]
      arg1: tensor<LxNxP> = [...]

      result: tensor<KxLxMxP> = [...]

```mlir
"FHELinalg.matmul_eint_eint(%a, %b) : (tensor<MxNx!FHE.eint<p>>, tensor<NxPx!FHE.eint<p>'>) -> tensor<MxPx!FHE.eint<p>>"
"FHELinalg.matmul_eint_eint(%a, %b) : (tensor<KxLxMxNx!FHE.eint<p>>, tensor<KxLxNxPx!FHE.eint<p>'>) -> tensor<KxLxMxPx!FHE.eint<p>>"
"FHELinalg.matmul_eint_eint(%a, %b) : (tensor<MxNx!FHE.eint<p>>, tensor<Nx!FHE.eint<p>'>) -> tensor<Mx!FHE.eint<p>>"
"FHELinalg.matmul_eint_eint(%a, %b) : (tensor<Nx!FHE.eint<p>>, tensor<NxPx!FHE.eint<p>'>) -> tensor<Px!FHE.eint<p>>"
```

Examples:
```mlir
// Returns the matrix multiplication of a 3x2 matrix of encrypted integers and a 2x3 matrix of integers.
//         [ 1, 2, 3]
//         [ 2, 3, 4]
//       *
// [1,2]   [ 5, 8,11]
// [3,4] = [11,18,25]
// [5,6]   [17,28,39]
//
"FHELinalg.matmul_eint_eint"(%a, %b) : (tensor<3x2x!FHE.eint<6>>, tensor<2x3x!FHE.eint<6>>) -> tensor<3x3x!FHE.eint<12>>
```

Traits: AlwaysSpeculatableImplTrait, TensorBinaryEint

Interfaces: ConditionallySpeculatable, NoMemoryEffect (MemoryEffectOpInterface)

Effects: MemoryEffects::Effect{}

#### Operands:

| Operand | Description |
| :-----: | ----------- |
| `lhs` | 
| `rhs` | 

#### Results:

| Result | Description |
| :----: | ----------- |
&laquo;unnamed&raquo; | 

### `FHELinalg.matmul_eint_int` (::mlir::concretelang::FHELinalg::MatMulEintIntOp)

Returns a tensor that contains the result of the matrix multiplication of a matrix of encrypted integers and a matrix of clear integers.

Performs a matrix multiplication of a matrix of encrypted integers and a matrix of clear integers.
The width of the clear integers must be less than or equal to the width of encrypted integers.

The behavior depends on the arguments in the following way:

    - If both arguments are 2-D,
      they are multiplied like conventional matrices.

      e.g.,

      arg0: tensor<MxN> = [...]
      arg1: tensor<NxP> = [...]

      result: tensor<MxP> = [...]

    - If the first argument is a vector (1-D),
      it is treated as a matrix with a single row and standard matrix multiplication is performed.

      After standard matrix multiplication,
      the first dimension is removed from the result.

      e.g.,

      arg0: tensor<3> = [x, y, z]
      arg1: tensor<3xM> = [
          [_, _, ..., _, _],
          [_, _, ..., _, _],
          [_, _, ..., _, _],
      ]

      is treated as

      arg0: tensor<1x3> = [
          [x, y, z]
      ]
      arg1: tensor<3xM> = [
          [_, _, ..., _, _],
          [_, _, ..., _, _],
          [_, _, ..., _, _],
      ]

      and matrix multiplication is performed with the following form (1x3 @ 3xM -> 1xM)

      result: tensor<1xM> = [[_, _, ..., _, _]]

      finally, the first dimension is removed by definition so the result has the following form

      result: tensor<M>  = [_, _, ..., _, _]

    - If the second argument is 1-D,
      it is treated as a matrix with a single column and standard matrix multiplication is performed.

      After standard matrix multiplication,
      the last dimension is removed from the result.

      e.g.,

      arg0: tensor<Mx3> = [
          [_, _, _],
          [_, _, _],
          ...,
          [_, _, _],
          [_, _, _],
      ]
      arg1: tensor<3> = [x, y, z]

      is treated as

      arg0: tensor<Mx3> = [
          [_, _, _],
          [_, _, _],
          ...,
          [_, _, _],
          [_, _, _],
      ]
      arg1: tensor<3x1> = [
        [x],
        [y],
        [z],
      ]

      and matrix multiplication is performed with the following form (Mx3 @ 3x1 -> Mx1)

      result: tensor<Mx1> = [
        [_],
        [_],
          ...,
        [_],
        [_],
      ]

      finally, the last dimension is removed by definition so the result has the following form

      result: tensor<M> = [_, _, _]

    - If either argument is N-D where N > 2,
      the operation is treated as a collection of matrices residing in the last two indices and broadcasted accordingly.

      arg0: tensor<Kx1MxN> = [...]
      arg1: tensor<LxNxP> = [...]

      result: tensor<KxLxMxP> = [...]

```mlir
"FHELinalg.matmul_eint_int(%a, %b) : (tensor<MxNx!FHE.eint<p>>, tensor<NxPxip'>) -> tensor<MxPx!FHE.eint<p>>"
"FHELinalg.matmul_eint_int(%a, %b) : (tensor<KxLxMxNx!FHE.eint<p>>, tensor<KxLxNxPxip'>) -> tensor<KxLxMxPx!FHE.eint<p>>"
"FHELinalg.matmul_eint_int(%a, %b) : (tensor<MxNx!FHE.eint<p>>, tensor<Nxip'>) -> tensor<Mx!FHE.eint<p>>"
"FHELinalg.matmul_eint_int(%a, %b) : (tensor<Nx!FHE.eint<p>>, tensor<NxPxip'>) -> tensor<Px!FHE.eint<p>>"
```

Examples:
```mlir
// Returns the matrix multiplication of a 3x2 matrix of encrypted integers and a 2x3 matrix of integers.
//         [ 1, 2, 3]
//         [ 2, 3, 4]
//       *
// [1,2]   [ 5, 8,11]
// [3,4] = [11,18,25]
// [5,6]   [17,28,39]
//
"FHELinalg.matmul_eint_int"(%a, %b) : (tensor<3x2x!FHE.eint<6>>, tensor<2x3xi7>) -> tensor<3x3x!FHE.eint<6>>
```

Traits: AlwaysSpeculatableImplTrait, TensorBinaryEintInt

Interfaces: Binary, BinaryEintInt, ConditionallySpeculatable, NoMemoryEffect (MemoryEffectOpInterface)

Effects: MemoryEffects::Effect{}

#### Operands:

| Operand | Description |
| :-----: | ----------- |
| `lhs` | 
| `rhs` | 

#### Results:

| Result | Description |
| :----: | ----------- |
&laquo;unnamed&raquo; | 

### `FHELinalg.matmul_int_eint` (::mlir::concretelang::FHELinalg::MatMulIntEintOp)

Returns a tensor that contains the result of the matrix multiplication of a matrix of clear integers and a matrix of encrypted integers.

Performs a matrix multiplication of a matrix of clear integers and a matrix of encrypted integers.
The width of the clear integers must be less than or equal to the width of encrypted integers.

The behavior depends on the arguments in the following way:

    - If both arguments are 2-D,
      they are multiplied like conventional matrices.

      e.g.,

      arg0: tensor<MxN> = [...]
      arg1: tensor<NxP> = [...]

      result: tensor<MxP> = [...]

    - If the first argument is a vector (1-D),
      it is treated as a matrix with a single row and standard matrix multiplication is performed.

      After standard matrix multiplication,
      the first dimension is removed from the result.

      e.g.,

      arg0: tensor<3> = [x, y, z]
      arg1: tensor<3xM> = [
          [_, _, ..., _, _],
          [_, _, ..., _, _],
          [_, _, ..., _, _],
      ]

      is treated as

      arg0: tensor<1x3> = [
          [x, y, z]
      ]
      arg1: tensor<3xM> = [
          [_, _, ..., _, _],
          [_, _, ..., _, _],
          [_, _, ..., _, _],
      ]

      and matrix multiplication is performed with the following form (1x3 @ 3xM -> 1xM)

      result: tensor<1xM> = [[_, _, ..., _, _]]

      finally, the first dimension is removed by definition so the result has the following form

      result: tensor<M>  = [_, _, ..., _, _]

    - If the second argument is 1-D,
      it is treated as a matrix with a single column and standard matrix multiplication is performed.

      After standard matrix multiplication,
      the last dimension is removed from the result.

      e.g.,

      arg0: tensor<Mx3> = [
          [_, _, _],
          [_, _, _],
          ...,
          [_, _, _],
          [_, _, _],
      ]
      arg1: tensor<3> = [x, y, z]

      is treated as

      arg0: tensor<Mx3> = [
          [_, _, _],
          [_, _, _],
          ...,
          [_, _, _],
          [_, _, _],
      ]
      arg1: tensor<3x1> = [
        [x],
        [y],
        [z],
      ]

      and matrix multiplication is performed with the following form (Mx3 @ 3x1 -> Mx1)

      result: tensor<Mx1> = [
        [_],
        [_],
          ...,
        [_],
        [_],
      ]

      finally, the last dimension is removed by definition so the result has the following form

      result: tensor<M> = [_, _, _]

    - If either argument is N-D where N > 2,
      the operation is treated as a collection of matrices residing in the last two indices and broadcasted accordingly.

      arg0: tensor<Kx1MxN> = [...]
      arg1: tensor<LxNxP> = [...]

      result: tensor<KxLxMxP> = [...]

```mlir
"FHELinalg.matmul_int_eint(%a, %b) : (tensor<MxNxip'>, tensor<NxPxFHE.eint<p>>) -> tensor<MxPx!FHE.eint<p>>"
"FHELinalg.matmul_int_eint(%a, %b) : (tensor<KxLxMxNxip'>, tensor<KxLxNxPxFHE.eint<p>>) -> tensor<KxLxMxPx!FHE.eint<p>>"
"FHELinalg.matmul_int_eint(%a, %b) : (tensor<MxNxip'>, tensor<NxFHE.eint<p>>) -> tensor<Mx!FHE.eint<p>>"
"FHELinalg.matmul_int_eint(%a, %b) : (tensor<Nxip'>, tensor<NxPxFHE.eint<p>>) -> tensor<Px!FHE.eint<p>>"
```

Examples:
```mlir
// Returns the matrix multiplication of a 3x2 matrix of clear integers and a 2x3 matrix of encrypted integers.
//         [ 1, 2, 3]
//         [ 2, 3, 4]
//       *
// [1,2]   [ 5, 8,11]
// [3,4] = [11,18,25]
// [5,6]   [17,28,39]
//
"FHELinalg.matmul_int_eint"(%a, %b) : (tensor<3x2xi7>, tensor<2x3x!FHE.eint<6>>) -> tensor<3x3x!FHE.eint<6>>
```

Traits: AlwaysSpeculatableImplTrait, TensorBinaryIntEint

Interfaces: Binary, BinaryIntEint, ConditionallySpeculatable, NoMemoryEffect (MemoryEffectOpInterface)

Effects: MemoryEffects::Effect{}

#### Operands:

| Operand | Description |
| :-----: | ----------- |
| `lhs` | 
| `rhs` | 

#### Results:

| Result | Description |
| :----: | ----------- |
&laquo;unnamed&raquo; | 

### `FHELinalg.maxpool2d` (::mlir::concretelang::FHELinalg::Maxpool2dOp)

Returns the 2D maxpool of a tensor in the form NCHW


Interfaces: UnaryEint

#### Attributes:

| Attribute | MLIR Type | Description |
| :-------: | :-------: | ----------- |
| `kernel_shape` | ::mlir::DenseIntElementsAttr | 64-bit signless integer elements attribute
| `strides` | ::mlir::DenseIntElementsAttr | 64-bit signless integer elements attribute
| `dilations` | ::mlir::DenseIntElementsAttr | 64-bit signless integer elements attribute

#### Operands:

| Operand | Description |
| :-----: | ----------- |
| `input` | 

#### Results:

| Result | Description |
| :----: | ----------- |
&laquo;unnamed&raquo; | 

### `FHELinalg.mul_eint_int` (::mlir::concretelang::FHELinalg::MulEintIntOp)

Returns a tensor that contains the multiplication of a tensor of encrypted integers and a tensor of clear integers.

Performs a multiplication following the broadcasting rules between a tensor of encrypted integers and a tensor of clear integers.
The width of the clear integers must be less than or equal to the width of encrypted integers.

Examples:
```mlir
// Returns the term-by-term multiplication of `%a0` with `%a1`
"FHELinalg.mul_eint_int"(%a0, %a1) : (tensor<4x!FHE.eint<4>>, tensor<4xi5>) -> tensor<4x!FHE.eint<4>>

// Returns the term-by-term multiplication of `%a0` with `%a1`, where dimensions equal to one are stretched.
"FHELinalg.mul_eint_int"(%a0, %a1) : (tensor<4x1x4x!FHE.eint<4>>, tensor<1x4x4xi5>) -> tensor<4x4x4x!FHE.eint<4>>

// Returns the multiplication of a 3x3 matrix of encrypted integers and a 3x1 matrix (a column) of integers.
//
// [1,2,3]   [1]   [1,2,3]
// [4,5,6] * [2] = [8,10,18]
// [7,8,9]   [3]   [21,24,27]
//
// The dimension #1 of operand #2 is stretched as it is equal to 1.
"FHELinalg.mul_eint_int"(%a0, %a1) : (tensor<3x3x!FHE.eint<4>>, tensor<3x1xi5>) -> tensor<3x3x!FHE.eint<4>>

// Returns the multiplication of a 3x3 matrix of encrypted integers and a 1x3 matrix (a line) of integers.
//
// [1,2,3]             [2,4,6]
// [4,5,6] * [1,2,3] = [5,7,9]
// [7,8,9]             [8,10,12]
//
// The dimension #2 of operand #2 is stretched as it is equal to 1.
"FHELinalg.mul_eint_int"(%a0, %a1) : (tensor<3x3x!FHE.eint<4>>, tensor<1x3xi5>) -> tensor<3x3x!FHE.eint<4>>

// Same behavior as the previous one, but as the dimension #2 is missing of operand #2.
"FHELinalg.mul_eint_int"(%a0, %a1) : (tensor<3x3x!FHE.eint<4>>, tensor<3xi5>) -> tensor<3x3x!FHE.eint<4>>

```

Traits: AlwaysSpeculatableImplTrait, TensorBinaryEintInt, TensorBroadcastingRules

Interfaces: Binary, BinaryEintInt, ConditionallySpeculatable, NoMemoryEffect (MemoryEffectOpInterface)

Effects: MemoryEffects::Effect{}

#### Operands:

| Operand | Description |
| :-----: | ----------- |
| `lhs` | 
| `rhs` | 

#### Results:

| Result | Description |
| :----: | ----------- |
&laquo;unnamed&raquo; | 

### `FHELinalg.mul_eint` (::mlir::concretelang::FHELinalg::MulEintOp)

Returns a tensor that contains the multiplication of two tensor of encrypted integers.

Performs an addition following the broadcasting rules between two tensors of encrypted integers.
The width of the encrypted integers must be equal.

Examples:
```mlir
// Returns the term-by-term multiplication of `%a0` with `%a1`
"FHELinalg.mul_eint"(%a0, %a1) : (tensor<4x!FHE.eint<8>>, tensor<4x!FHE.eint<8>>) -> tensor<4x!FHE.eint<8>>

// Returns the term-by-term multiplication of `%a0` with `%a1`, where dimensions equal to one are stretched.
"FHELinalg.mul_eint"(%a0, %a1) : (tensor<4x1x4x!FHE.eint<8>>, tensor<1x4x4x!FHE.eint<8>>) -> tensor<4x4x4x!FHE.eint<8>>

// Returns the multiplication of a 3x3 matrix of encrypted integers and a 3x1 matrix (a column) of encrypted integers.
//
// [1,2,3]   [1]   [1,2,3]
// [4,5,6] * [2] = [8,10,12]
// [7,8,9]   [3]   [21,24,27]
//
// The dimension #1 of operand #2 is stretched as it is equal to 1.
"FHELinalg.mul_eint"(%a0, %a1) : (tensor<3x3x!FHE.eint<8>>, tensor<3x1x!FHE.eint<8>>) -> tensor<3x3x!FHE.eint<8>>

// Returns the multiplication of a 3x3 matrix of encrypted integers and a 1x3 matrix (a line) of encrypted integers.
//
// [1,2,3]             [1,4,9]
// [4,5,6] * [1,2,3] = [4,10,18]
// [7,8,9]             [7,16,27]
//
// The dimension #2 of operand #2 is stretched as it is equal to 1.
"FHELinalg.mul_eint"(%a0, %a1) : (tensor<3x3x!FHE.eint<8>>, tensor<1x3x!FHE.eint<8>>) -> tensor<3x3x!FHE.eint<8>>

// Same behavior as the previous one, but as the dimension #2 of operand #2 is missing.
"FHELinalg.mul_eint"(%a0, %a1) : (tensor<3x3x!FHE.eint<8>>, tensor<3x!FHE.eint<8>>) -> tensor<3x3x!FHE.eint<8>>
```

Traits: AlwaysSpeculatableImplTrait, TensorBinaryEint, TensorBroadcastingRules

Interfaces: BinaryEint, ConditionallySpeculatable, NoMemoryEffect (MemoryEffectOpInterface)

Effects: MemoryEffects::Effect{}

#### Operands:

| Operand | Description |
| :-----: | ----------- |
| `lhs` | 
| `rhs` | 

#### Results:

| Result | Description |
| :----: | ----------- |
&laquo;unnamed&raquo; | 

### `FHELinalg.neg_eint` (::mlir::concretelang::FHELinalg::NegEintOp)

Returns a tensor that contains the negation of a tensor of encrypted integers.

Performs a negation to a tensor of encrypted integers.

Examples:
```mlir
// Returns the term-by-term negation of `%a0`
"FHELinalg.neg_eint"(%a0) : (tensor<3x3x!FHE.eint<4>>) -> tensor<3x3x!FHE.eint<4>>
//
//        ( [1,2,3] )   [31,30,29]
// negate ( [4,5,6] ) = [28,27,26]
//        ( [7,8,9] )   [25,24,23]
//
// The negation is computed as `2**(p+1) - a` where p=4 here.
```

Traits: AlwaysSpeculatableImplTrait, TensorUnaryEint

Interfaces: ConditionallySpeculatable, NoMemoryEffect (MemoryEffectOpInterface), UnaryEint

Effects: MemoryEffects::Effect{}

#### Operands:

| Operand | Description |
| :-----: | ----------- |
| `input` | 

#### Results:

| Result | Description |
| :----: | ----------- |
&laquo;unnamed&raquo; | 

### `FHELinalg.reinterpret_precision` (::mlir::concretelang::FHELinalg::ReinterpretPrecisionEintOp)

Reinterpret the ciphertext tensor with a different precision.

  It's a reinterpret cast changing only the precision.
  On CRT represention, it does nothing.
  On Native representation, it moves the message/noise frontier, effectively changing the precision.
  Changing to
    - a bigger precision is safe, as the crypto-parameters are chosen such that only zeros will comes from the noise part.
      This is equivalent to a shift left for the value
    - a smaller precision is only safe if you clear the message lowests bits first.
      If not, you can assume small errors with high probability and frequent bigger errors, which can be contained to smalls errors using margins.
      This is equivalent to a shift right for the value

  Example:
  ```mlir
   // assuming a is encoded as 4bits but can be stored in 2bits
   // we can obtain a to a smaller 2 bits precision
   %shifted_a = "FHELinalg.mul_eint_intlsb"(%a, %c_4): (tensor<1x!FHE.eint<4>>) -> (tensor<1x!FHE.eint<2>>)
   %a_small_precision = "FHELinalg.reinterpret_precision"(%shifted_a, %lsb) : (tensor<1x!FHE.eint<4>>) -> (tensor<1x!FHE.eint<2>>)
```

Traits: AlwaysSpeculatableImplTrait, TensorUnaryEint

Interfaces: ConditionallySpeculatable, NoMemoryEffect (MemoryEffectOpInterface), UnaryEint

Effects: MemoryEffects::Effect{}

#### Operands:

| Operand | Description |
| :-----: | ----------- |
| `input` | 

#### Results:

| Result | Description |
| :----: | ----------- |
| `output` | 

### `FHELinalg.round` (::mlir::concretelang::FHELinalg::RoundOp)

Rounds a tensor of ciphertexts into a smaller precision.

      Assuming a ciphertext whose message is implemented over `p` bits, this
      operation rounds it to fit to `q` bits where `p>q`.

      Example:
      ```mlir
      // ok
      "FHELinalg.round"(%a): (tensor<3x!FHE.eint<6>>) -> (tensor<3x!FHE.eint<5>>)
      "FHELinalg.round"(%a): (tensor<3x!FHE.eint<5>>) -> (tensor<3x!FHE.eint<3>>)
      "FHELinalg.round"(%a): (tensor<3x!FHE.eint<3>>) -> (tensor<3x!FHE.eint<2>>)
      "FHELinalg.round"(%a): (tensor<3x!FHE.esint<3>>) -> (tensor<3x!FHE.esint<2>>)

      // error
      "FHELinalg.round"(%a): (tensor<3x!FHE.eint<6>>) -> (tensor<3x!FHE.eint<6>>)
      "FHELinalg.round"(%a): (tensor<3x!FHE.eint<4>>) -> (tensor<3x!FHE.eint<5>>)
      "FHELinalg.round"(%a): (tensor<3x!FHE.eint<4>>) -> (tensor<3x!FHE.esint<2>>)
```

Traits: AlwaysSpeculatableImplTrait, TensorUnaryEint

Interfaces: ConditionallySpeculatable, NoMemoryEffect (MemoryEffectOpInterface), UnaryEint

Effects: MemoryEffects::Effect{}

#### Operands:

| Operand | Description |
| :-----: | ----------- |
| `input` | 

#### Results:

| Result | Description |
| :----: | ----------- |
| `output` | 

### `FHELinalg.sub_eint_int` (::mlir::concretelang::FHELinalg::SubEintIntOp)

Returns a tensor that contains the subtraction of a tensor of clear integers from a tensor of encrypted integers.

Performs a subtraction following the broadcasting rules between a tensor of clear integers from a tensor of encrypted integers.
The width of the clear integers must be less than or equal to the width of encrypted integers.

Examples:
```mlir
// Returns the term-by-term subtraction of `%a0` with `%a1`
"FHELinalg.sub_eint_int"(%a0, %a1) : (tensor<4x!FHE.eint<4>>, tensor<4xi5>) -> tensor<4x!FHE.eint<4>>

// Returns the term-by-term subtraction of `%a0` with `%a1`, where dimensions equal to one are stretched.
"FHELinalg.sub_eint_int"(%a0, %a1) : (tensor<1x4x4x!FHE.eint<4>>, tensor<4x1x4xi5>) -> tensor<4x4x4x!FHE.eint<4>>

// Returns the subtraction of a 3x3 matrix of integers and a 3x1 matrix (a column) of encrypted integers.
//
// [1,2,3]   [1]   [0,2,3]
// [4,5,6] - [2] = [2,3,4]
// [7,8,9]   [3]   [4,5,6]
//
// The dimension #1 of operand #2 is stretched as it is equal to 1.
"FHELinalg.sub_eint_int"(%a0, %a1) : (tensor<3x1x!FHE.eint<4>>, tensor<3x3xi5>) -> tensor<3x3x!FHE.eint<4>>

// Returns the subtraction of a 3x3 matrix of integers and a 1x3 matrix (a line) of encrypted integers.
//
// [1,2,3]             [0,0,0]
// [4,5,6] - [1,2,3] = [3,3,3]
// [7,8,9]             [6,6,6]
//
// The dimension #2 of operand #2 is stretched as it is equal to 1.
"FHELinalg.sub_eint_int"(%a0, %a1) : (tensor<1x3x!FHE.eint<4>>, tensor<3x3xi5>) -> tensor<3x3x!FHE.eint<4>>

// Same behavior as the previous one, but as the dimension #2 is missing of operand #2.
"FHELinalg.sub_eint_int"(%a0, %a1) : (tensor<3x!FHE.eint<4>>, tensor<3x3xi5>) -> tensor<3x3x!FHE.eint<4>>

```

Traits: AlwaysSpeculatableImplTrait, TensorBinaryEintInt, TensorBroadcastingRules

Interfaces: Binary, BinaryEintInt, ConditionallySpeculatable, NoMemoryEffect (MemoryEffectOpInterface)

Effects: MemoryEffects::Effect{}

#### Operands:

| Operand | Description |
| :-----: | ----------- |
| `lhs` | 
| `rhs` | 

#### Results:

| Result | Description |
| :----: | ----------- |
&laquo;unnamed&raquo; | 

### `FHELinalg.sub_eint` (::mlir::concretelang::FHELinalg::SubEintOp)

Returns a tensor that contains the subtraction of two tensor of encrypted integers.

Performs an subtraction following the broadcasting rules between two tensors of encrypted integers.
The width of the encrypted integers must be equal.

Examples:
```mlir
// Returns the term-by-term subtraction of `%a0` with `%a1`
"FHELinalg.sub_eint"(%a0, %a1) : (tensor<4x!FHE.eint<4>>, tensor<4x!FHE.eint<4>>) -> tensor<4x!FHE.eint<4>>

// Returns the term-by-term subtraction of `%a0` with `%a1`, where dimensions equal to one are stretched.
"FHELinalg.sub_eint"(%a0, %a1) : (tensor<4x1x4x!FHE.eint<4>>, tensor<1x4x4x!FHE.eint<4>>) -> tensor<4x4x4x!FHE.eint<4>>

// Returns the subtraction of a 3x3 matrix of integers and a 3x1 matrix (a column) of encrypted integers.
//
// [1,2,3]   [1]   [0,2,3]
// [4,5,6] - [2] = [2,3,4]
// [7,8,9]   [3]   [4,5,6]
//
// The dimension #1 of operand #2 is stretched as it is equal to 1.
"FHELinalg.sub_eint"(%a0, %a1) : (tensor<3x3x!FHE.eint<4>>, tensor<3x1x!FHE.eint<4>>) -> tensor<3x3x!FHE.eint<4>>

// Returns the subtraction of a 3x3 matrix of integers and a 1x3 matrix (a line) of encrypted integers.
//
// [1,2,3]             [0,0,0]
// [4,5,6] - [1,2,3] = [3,3,3]
// [7,8,9]             [6,6,6]
//
// The dimension #2 of operand #2 is stretched as it is equal to 1.
"FHELinalg.sub_eint"(%a0, %a1) : (tensor<3x3x!FHE.eint<4>>, tensor<1x3x!FHE.eint<4>>) -> tensor<3x3x!FHE.eint<4>>

// Same behavior as the previous one, but as the dimension #2 of operand #2 is missing.
"FHELinalg.sub_eint"(%a0, %a1) : (tensor<3x3x!FHE.eint<4>>, tensor<3x!FHE.eint<4>>) -> tensor<3x3x!FHE.eint<4>>
```

Traits: AlwaysSpeculatableImplTrait, TensorBinaryEint, TensorBroadcastingRules

Interfaces: BinaryEint, ConditionallySpeculatable, NoMemoryEffect (MemoryEffectOpInterface)

Effects: MemoryEffects::Effect{}

#### Operands:

| Operand | Description |
| :-----: | ----------- |
| `lhs` | 
| `rhs` | 

#### Results:

| Result | Description |
| :----: | ----------- |
&laquo;unnamed&raquo; | 

### `FHELinalg.sub_int_eint` (::mlir::concretelang::FHELinalg::SubIntEintOp)

Returns a tensor that contains the subtraction of a tensor of clear integers and a tensor of encrypted integers.

Performs a subtraction following the broadcasting rules between a tensor of clear integers and a tensor of encrypted integers.
The width of the clear integers must be less than or equal to the width of encrypted integers.

Examples:
```mlir
// Returns the term-by-term subtraction of `%a0` with `%a1`
"FHELinalg.sub_int_eint"(%a0, %a1) : (tensor<4xi5>, tensor<4x!FHE.eint<4>>) -> tensor<4x!FHE.eint<4>>

// Returns the term-by-term subtraction of `%a0` with `%a1`, where dimensions equal to one are stretched.
"FHELinalg.sub_int_eint"(%a0, %a1) : (tensor<4x1x4xi5>, tensor<1x4x4x!FHE.eint<4>>) -> tensor<4x4x4x!FHE.eint<4>>

// Returns the subtraction of a 3x3 matrix of integers and a 3x1 matrix (a column) of encrypted integers.
//
// [1,2,3]   [1]   [0,2,3]
// [4,5,6] - [2] = [2,3,4]
// [7,8,9]   [3]   [4,5,6]
//
// The dimension #1 of operand #2 is stretched as it is equal to 1.
"FHELinalg.sub_int_eint"(%a0, %a1) : (tensor<3x3xi5>, tensor<3x1x!FHE.eint<4>>) -> tensor<3x3x!FHE.eint<4>>

// Returns the subtraction of a 3x3 matrix of integers and a 1x3 matrix (a line) of encrypted integers.
//
// [1,2,3]             [0,0,0]
// [4,5,6] - [1,2,3] = [3,3,3]
// [7,8,9]             [6,6,6]
//
// The dimension #2 of operand #2 is stretched as it is equal to 1.
"FHELinalg.sub_int_eint"(%a0, %a1) : (tensor<3x3xi5>, tensor<1x3x!FHE.eint<4>>) -> tensor<3x3x!FHE.eint<4>>

// Same behavior as the previous one, but as the dimension #2 is missing of operand #2.
"FHELinalg.sub_int_eint"(%a0, %a1) : (tensor<3x3xi5>, tensor<3x!FHE.eint<4>>) -> tensor<3x3x!FHE.eint<4>>

```

Traits: AlwaysSpeculatableImplTrait, TensorBinaryIntEint, TensorBroadcastingRules

Interfaces: Binary, BinaryIntEint, ConditionallySpeculatable, NoMemoryEffect (MemoryEffectOpInterface)

Effects: MemoryEffects::Effect{}

#### Operands:

| Operand | Description |
| :-----: | ----------- |
| `lhs` | 
| `rhs` | 

#### Results:

| Result | Description |
| :----: | ----------- |
&laquo;unnamed&raquo; | 

### `FHELinalg.sum` (::mlir::concretelang::FHELinalg::SumOp)

Returns the sum of elements of a tensor of encrypted integers along specified axes.

Attributes:

- keep_dims: boolean = false
    whether to keep the rank of the tensor after the sum operation
    if true, reduced axes will have the size of 1

- axes: I64ArrayAttr = []
    list of dimension to perform the sum along
    think of it as the dimensions to reduce (see examples below to get an intuition)

Examples:

```mlir
// Returns the sum of all elements of `%a0`
"FHELinalg.sum"(%a0) : (tensor<3x3x!FHE.eint<4>>) -> !FHE.eint<4>
//
//     ( [1,2,3] )
// sum ( [4,5,6] ) = 45
//     ( [7,8,9] )
//
```

```mlir
// Returns the sum of all elements of `%a0` along columns
"FHELinalg.sum"(%a0) { axes = [0] } : (tensor<3x2x!FHE.eint<4>>) -> tensor<2x!FHE.eint<4>>
//
//     ( [1,2] )
// sum ( [3,4] ) = [9, 12]
//     ( [5,6] )
//
```

```mlir
// Returns the sum of all elements of `%a0` along columns while preserving dimensions
"FHELinalg.sum"(%a0) { axes = [0], keep_dims = true } : (tensor<3x2x!FHE.eint<4>>) -> tensor<1x2x!FHE.eint<4>>
//
//     ( [1,2] )
// sum ( [3,4] ) = [[9, 12]]
//     ( [5,6] )
//
```

```mlir
// Returns the sum of all elements of `%a0` along rows
"FHELinalg.sum"(%a0) { axes = [1] } : (tensor<3x2x!FHE.eint<4>>) -> tensor<3x!FHE.eint<4>>
//
//     ( [1,2] )
// sum ( [3,4] ) = [3, 7, 11]
//     ( [5,6] )
//
```

```mlir
// Returns the sum of all elements of `%a0` along rows while preserving dimensions
"FHELinalg.sum"(%a0) { axes = [1], keep_dims = true } : (tensor<3x2x!FHE.eint<4>>) -> tensor<3x1x!FHE.eint<4>>
//
//     ( [1,2] )   [3]
// sum ( [3,4] ) = [7]
//     ( [5,6] )   [11]
//
```

Traits: AlwaysSpeculatableImplTrait, TensorUnaryEint

Interfaces: ConditionallySpeculatable, NoMemoryEffect (MemoryEffectOpInterface)

Effects: MemoryEffects::Effect{}

#### Attributes:

| Attribute | MLIR Type | Description |
| :-------: | :-------: | ----------- |
| `axes` | ::mlir::ArrayAttr | 64-bit integer array attribute
| `keep_dims` | ::mlir::BoolAttr | bool attribute

#### Operands:

| Operand | Description |
| :-----: | ----------- |
| `tensor` | 

#### Results:

| Result | Description |
| :----: | ----------- |
| `out` | 

### `FHELinalg.to_signed` (::mlir::concretelang::FHELinalg::ToSignedOp)

Cast an unsigned integer tensor to a signed one

Cast an unsigned integer tensor to a signed one.
The result must have the same width and the same shape as the input.

The behavior is undefined on overflow/underflow.

Examples:
```mlir
// ok
"FHELinalg.to_signed"(%x) : (tensor<3x2x!FHE.eint<2>>) -> tensor<3x2x!FHE.esint<2>>

// error
"FHELinalg.to_signed"(%x) : (tensor<3x2x!FHE.eint<2>>) -> tensor<3x2x!FHE.esint<3>>
```

Traits: AlwaysSpeculatableImplTrait

Interfaces: ConditionallySpeculatable, NoMemoryEffect (MemoryEffectOpInterface), UnaryEint

Effects: MemoryEffects::Effect{}

#### Operands:

| Operand | Description |
| :-----: | ----------- |
| `input` | 

#### Results:

| Result | Description |
| :----: | ----------- |
| `output` | 

### `FHELinalg.to_unsigned` (::mlir::concretelang::FHELinalg::ToUnsignedOp)

Cast a signed integer tensor to an unsigned one

Cast a signed integer tensor to an unsigned one.
The result must have the same width and the same shape as the input.

The behavior is undefined on overflow/underflow.

Examples:
```mlir
// ok
"FHELinalg.to_unsigned"(%x) : (tensor<3x2x!FHE.esint<2>>) -> tensor<3x2x!FHE.eint<2>>

// error
"FHELinalg.to_unsigned"(%x) : (tensor<3x2x!FHE.esint<2>>) -> tensor<3x2x!FHE.eint<3>>
```

Traits: AlwaysSpeculatableImplTrait

Interfaces: ConditionallySpeculatable, NoMemoryEffect (MemoryEffectOpInterface), UnaryEint

Effects: MemoryEffects::Effect{}

#### Operands:

| Operand | Description |
| :-----: | ----------- |
| `input` | 

#### Results:

| Result | Description |
| :----: | ----------- |
| `output` | 

### `FHELinalg.transpose` (::mlir::concretelang::FHELinalg::TransposeOp)

Returns a tensor that contains the transposition of the input tensor.

Performs a transpose operation on an N-dimensional tensor.

Attributes:

- axes: I64ArrayAttr = []
    list of dimension to perform the transposition
    contains a permutation of [0,1,..,N-1] where N is the number of axes
    think of it as a way to rearrange axes (see the example below)

```mlir
"FHELinalg.transpose"(%a) : (tensor<n0xn1x...xnNxType>) -> tensor<nNx...xn1xn0xType>
```

Examples:
```mlir
// Transpose the input tensor
// [1,2]    [1, 3, 5]
// [3,4] => [2, 4, 6]
// [5,6]
//
"FHELinalg.transpose"(%a) : (tensor<3x2xi7>) -> tensor<2x3xi7>
```

```mlir
"FHELinalg.transpose"(%a) { axes = [1, 3, 0, 2] } : (tensor<2x3x4x5xi7>) -> tensor<3x5x2x4xi7>
```

Traits: AlwaysSpeculatableImplTrait

Interfaces: ConditionallySpeculatable, NoMemoryEffect (MemoryEffectOpInterface), UnaryEint

Effects: MemoryEffects::Effect{}

#### Attributes:

| Attribute | MLIR Type | Description |
| :-------: | :-------: | ----------- |
| `axes` | ::mlir::ArrayAttr | 64-bit integer array attribute

#### Operands:

| Operand | Description |
| :-----: | ----------- |
| `tensor` | any type

#### Results:

| Result | Description |
| :----: | ----------- |
&laquo;unnamed&raquo; | any type

