// SPDX-License-Identifier: Apache-2.0

// Copyright 2023 Consensys Software Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

// Code generated by gnark DO NOT EDIT
pragma solidity 0.8.25;

/**
 * @title Library to perform MiMC hashing
 * @author ConsenSys Software Inc.
 * @custom:security-contact security-report@linea.build
 */
library Mimc {
  /**
   * Thrown when the data is not provided
   */
  error DataMissing();

  /**
   * Thrown when the data is not purely in 32 byte chunks
   */
  error DataIsNotMod32();

  uint256 constant FR_FIELD = 8444461749428370424248824938781546531375899335154063827935233455917409239041;
  /**
   * @notice Performs a MiMC hash on the data provided
   * @param _msg The data to be hashed
   * @dev Only data that has length modulus 32 is hashed, reverts otherwise
   * @return mimcHash The computed MiMC hash
   */
  function hash(bytes calldata _msg) external pure returns (bytes32 mimcHash) {
    if (_msg.length == 0) {
      revert DataMissing();
    }

    if (_msg.length % 0x20 != 0) {
      revert DataIsNotMod32();
    }

    assembly {
      let chunks := div(add(_msg.length, 0x1f), 0x20)

      for {
        let i := 0
      } lt(i, chunks) {
        i := add(i, 1)
      } {
        let offset := add(_msg.offset, mul(i, 0x20))
        let chunk := calldataload(offset)

        let r := encrypt(mimcHash, chunk)
        mimcHash := addmod(addmod(mimcHash, r, FR_FIELD), chunk, FR_FIELD)
      }

      function encrypt(h, chunk) -> output {
        let frField := FR_FIELD
        let tmpSum := 0

        tmpSum := addmod(
          addmod(chunk, h, frField),
          {{ index . 0 }},
          frField
        )
        output := mulmod(tmpSum, tmpSum, frField)
        output := mulmod(output, output, frField)
        output := mulmod(output, output, frField)
        output := mulmod(mulmod(output, output, frField), tmpSum, frField)
{{ range slice . 1 (sub (len .) 1) }}
        tmpSum := addmod(
          addmod(output, h, frField),
          {{ . }},
          frField
        )
        output := mulmod(tmpSum, tmpSum, frField)
        output := mulmod(output, output, frField)
        output := mulmod(output, output, frField)
        output := mulmod(mulmod(output, output, frField), tmpSum, frField)
{{ end }}
        tmpSum := addmod(
          addmod(output, h, frField),
          {{ index . (sub (len .) 1) }},
          frField
        )
        output := mulmod(tmpSum, tmpSum, frField)
        output := mulmod(output, output, frField)
        output := mulmod(output, output, frField)
        output := mulmod(mulmod(output, output, frField), tmpSum, frField)

        output := addmod(output, h, frField)
      }
    }
  }
}
