<commit_before>package ir.ast

import ir._

import scala.language.implicitConversions

/**
 * Instances of this class represent declarations (and definition) of anonymous functions (a.k.a., lambda expressions).
 * This class allows for an arbitrary number of parameters.
 * Use one of the provided subclasses to get a (runtime) checking of the number of arguments when constructing an
 * instance of such a subclass.
 *
 * @param params The parameters of the lambda expression.
 * @param body The body of the lambda expression.
 */
abstract class Lambda private[ast] (val params: Array[Param],
                                    val body: Expr) extends FunDecl(params.length) {

  /**
   * Debug string representation
   */
  override def toString = "(\\" + params.map(_.toString).reduce(_ + ", " + _) +
      " -> \n" + body.toString.split("\n").map("  " + _ + "\n").mkString + ")"

  override def checkType(argType: Type,
                         setType: Boolean): Type = {
    TypeChecker.checkAndSetTypeForParams(params, argType)
    TypeChecker.check(body, setType)
  }

  override def apply(args : Expr*) : Expr = {
    assert (args.length == arity)

    val paramUsedMultipleTimes =
      params.map(p => Expr.visitWithState(0)(body, (e, c) => {
        e match {
          case p2: Param => if (p.eq(p2)) c + 1 else c
          case _ => c
        }})).exists(_ > 1)

    // don't inline if one param is used multiple times
    val inline = !paramUsedMultipleTimes

    if (!inline) {
      super.apply(args:_*)
    } else {

      // 1. pair up parameters and arguments
      // 2. replace each param with the corresponding argument
      (params, args).zipped.foldLeft(body) {
        case (e, (p, a)) => Expr.replace(e, p, a)
      }
    }
  }

  override lazy val isGenerable: Boolean = {
    Expr.visitWithState(true)(body, (e, s) => {
      e match {
        case call: FunCall if !call.f.isGenerable => false
        case _ => s
      }
    })
  }
}

object Lambda {

  def unapply(l: Lambda): Option[(Array[Param], Expr)] = {
    Some(l.params, l.body)
  }

  def apply(params: Array[Param],
            body: Expr): Lambda = {
    body match {
      case FunCall(funDecl, FunCall(Lambda(lambdaParams,lambdaBody), args@_*))
        if lambdaParams.length == params.length && (lambdaParams, args).zipped.forall((a, b) => a eq b)
      =>

        val a = (lambdaParams, params).zipped.foldLeft(lambdaBody)(
            (e: Expr, pair: (Param, Param)) => Expr.replace(e, pair._1, pair._2))

        new Lambda(params, FunCall(funDecl, a)) {}
      case _ => new Lambda(params, body) {}
    }
  }


  /**
   * Implicitly wrap a given function declaration `f` into a lambda.
   *
   * @param f A given function declaration
   * @return A lambda with the same arity as `f` which immediately calls `f` in its body.
   */
  implicit def FunDefToLambda(f: FunDecl): Lambda = {
    val params = Array.fill(f.arity)(Param(UndefType))
    f match {<commit_msg>Restoring lambda as a case class to bring simplification back to more reasonable speeds<commit_after>package ir.ast

import ir._

import scala.language.implicitConversions

/**
 * Instances of this class represent declarations (and definition) of anonymous functions (a.k.a., lambda expressions).
 * This class allows for an arbitrary number of parameters.
 * Use one of the provided subclasses to get a (runtime) checking of the number of arguments when constructing an
 * instance of such a subclass.
 *
 * @param params The parameters of the lambda expression.
 * @param body The body of the lambda expression.
 */
abstract case class Lambda private[ast] (params: Array[Param],
                                         body: Expr) extends FunDecl(params.length) {

  /**
   * Debug string representation
   */
  override def toString = "(\\" + params.map(_.toString).reduce(_ + ", " + _) +
      " -> \n" + body.toString.split("\n").map("  " + _ + "\n").mkString + ")"

  override def checkType(argType: Type,
                         setType: Boolean): Type = {
    TypeChecker.checkAndSetTypeForParams(params, argType)
    TypeChecker.check(body, setType)
  }

  override def apply(args : Expr*) : Expr = {
    assert (args.length == arity)

    val paramUsedMultipleTimes =
      params.map(p => Expr.visitWithState(0)(body, (e, c) => {
        e match {
          case p2: Param => if (p.eq(p2)) c + 1 else c
          case _ => c
        }})).exists(_ > 1)

    // don't inline if one param is used multiple times
    val inline = !paramUsedMultipleTimes

    if (!inline) {
      super.apply(args:_*)
    } else {

      // 1. pair up parameters and arguments
      // 2. replace each param with the corresponding argument
      (params, args).zipped.foldLeft(body) {
        case (e, (p, a)) => Expr.replace(e, p, a)
      }
    }
  }

  override lazy val isGenerable: Boolean = {
    Expr.visitWithState(true)(body, (e, s) => {
      e match {
        case call: FunCall if !call.f.isGenerable => false
        case _ => s
      }
    })
  }
}

object Lambda {

  def apply(params: Array[Param],
            body: Expr): Lambda = {
    body match {
      case FunCall(funDecl, FunCall(Lambda(lambdaParams,lambdaBody), args@_*))
        if lambdaParams.length == params.length && (lambdaParams, args).zipped.forall((a, b) => a eq b)
      =>

        val a = (lambdaParams, params).zipped.foldLeft(lambdaBody)(
            (e: Expr, pair: (Param, Param)) => Expr.replace(e, pair._1, pair._2))

        new Lambda(params, FunCall(funDecl, a)) {}
      case _ => new Lambda(params, body) {}
    }
  }


  /**
   * Implicitly wrap a given function declaration `f` into a lambda.
   *
   * @param f A given function declaration
   * @return A lambda with the same arity as `f` which immediately calls `f` in its body.
   */
  implicit def FunDefToLambda(f: FunDecl): Lambda = {
    val params = Array.fill(f.arity)(Param(UndefType))
    f match {
{'fraction_non_alphanumeric': 0.12620375063355296, 'fraction_numerical': 0.0030410542321338066, 'mean_word_length': 2.568414707655214, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 31, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 3, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '4200575', 'n_tokens_mistral': 1822, 'n_tokens_neox': 1783, 'n_words': 565}