> For the complete documentation index, see [llms.txt](https://thias-organization.gitbook.io/p256-documentation/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://thias-organization.gitbook.io/p256-documentation/risc-zero-p256-accelerator/field-element-standard-form.md).

# Field Element (Standard Form)

The other way we can optimise field arithmetic in `FieldElement` is to completely remove any Montgomery form operations. This is because Montgomery form no longer provides any speed ups compared to using the RISC Zero accelerator. This optimisation allows us to achieve a significant 7x speedup.

## Remove functions and their references

Firstly we have 2 functions `to_canonical` and `to_montgomery` that converts montgomery form to standard form and vice versa.

```rust
/// Translate a field element out of the Montgomery domain.
#[inline]
pub(crate) const fn to_canonical(self) -> Self {
    let w = u256_to_u64x4(self.0);
    FieldElement::montgomery_reduce(w[0], w[1], w[2], w[3], 0, 0, 0, 0)
}

/// Translate a field element into the Montgomery domain.
#[inline]
pub(crate) const fn to_montgomery(self) -> Self {
    Self::multiply(&self, &R2)
}
```

`to_canonical` is referenced by the function `to_bytes` and to remove this reference is easy. The main reference to `to_montgomery` comes from the function `from_uint_unchecked` which is also called by many other functions to initialise either hexadecimals or integers into a `FieldElement` in Montgomery form. To resolve this issue, we can simply replace `from_uint_unchecked` by initialising hexadecimals or integers straight using functions like `U256::from_u64` or `U256::from_be_hex`.

For example, the original implementation with Montgomery form

```rust
/// Convert a `u64` into a [`FieldElement`].
pub const fn from_u64(w: u64) -> Self {
    Self::from_uint_unchecked(U256::from_u64(w))
}

/// Parse a [`FieldElement`] from big endian hex-encoded bytes.
///
/// Does *not* perform a check that the field element does not overflow the order.
///
/// This method is primarily intended for defining internal constants.
pub(crate) const fn from_hex(hex: &str) -> Self {
    Self::from_uint_unchecked(U256::from_be_hex(hex))
}
```

will be modified into the standard from

```rust
/// Convert a `u64` into a [`FieldElement`].
pub fn from_u64(w: u64) -> Self {
    Self(U256::from_u64(w))
}

/// Parse a [`FieldElement`] from big endian hex-encoded bytes.
///
/// Does *not* perform a check that the field element does not overflow the order.
///
/// This method is primarily intended for defining internal constants.
#[allow(dead_code)]
pub(crate) fn from_hex(hex: &str) -> Self {
    Self(U256::from_be_hex(hex))
}
```

## Doubling Operation

The `double` and `mul_single` operations are still [optimised the same way](/p256-documentation/risc-zero-p256-accelerator/field-element-montgomery-form.md#double-operation) as they did not reference any Montgomery functions.

## Multiply Operation

The difficulty in [optimising the multiplication operation](/p256-documentation/risc-zero-p256-accelerator/field-element-montgomery-form.md#multiply-operation) in Montgomery form was to find a way to use `mul_wide_u128` and then perform the Montgomery reduction, but removing Montgomery form allows us to simply use the accelerated `modmul_u256_denormalized` straight in `field_risc0.rs`'s `mul` function just like how the k256 curve did it

```rust
/// Returns self * rhs mod p
pub(super) fn mul(a: U256, b: U256) -> U256 {
    risc0::modmul_u256_denormalized(&a, &b, &MODULUS_256)
}
```

Now instead, we need to modify `mul` function of `field32.rs` and `field64.rs` because the optimisation previously uses `montgomery_reduce()`

```rust
/// Returns self * rhs mod p
pub(super) fn mul(a: U256, b: U256) -> U256 {
    let (lo, hi) = a.mul_wide(&b);
    montgomery_reduce(lo, hi)
}
```

To do so, we can just make use of [`const_rem_wide`](https://docs.rs/crypto-bigint/latest/crypto_bigint/type.U256.html#method.const_rem_wide) to get the result of $$a \mod b$$

```rust
/// Returns self * rhs mod p
pub(super) fn mul(a: U256, b: U256) -> U256 {
    let (lo, hi) = a.mul_wide(&b);
    let (rem, _) = U256::const_rem_wide((lo, hi), &U256::from_be_hex(MODULUS_HEX));
    rem
}
```

Consequently, we can remove `montgomery_reduce` and `to_canonical` in `field64.rs`, `field32.rs` and `field_risc0.rs` as we will not be using them anymore.

### Downstream functions and constants

According to the [same logic as before](/p256-documentation/risc-zero-p256-accelerator/field-element-montgomery-form.md#downstream-functions-and-constants), these changes will affect any downstream functions and constants.&#x20;

Field elements in Montgomery form can be represented as

$$
FieldElement(a) = aR \mod p
$$

where the element $$a$$ is multiplied by a constant $$R$$. However, because we are now out of the Montgomery form, the value of the affected constants such as `TWO_INV`, `ROOT_OF_UNITY`, `ROOT_OF_UNITY_INV`, etc. are different and have to be changed.

The process of finding out the hexadecimal values is still the same whereby we can make use of test functions to get the expected result.
