<commit_before>/*
 * Copyright (C) 2019 <NAME>
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

package org.openmole.core.workflow

/**
 * This package articulates workflows, by providing <code>Puzzle</code> and the bricks to construct them such as transitions.
 *
 * It also contains a large part of the DSL.
 */
package composition {

  import org.openmole.core.context.{ Context, Val }
  import org.openmole.core.expansion.Condition
  import org.openmole.core.outputmanager.OutputManager
  import org.openmole.core.workflow.builder.DefinitionScope
  import org.openmole.core.workflow.execution.{ EnvironmentProvider, LocalEnvironmentProvider }
  import org.openmole.core.workflow.mole.{ Grouping, Hook, MasterCapsule, Mole, MoleCapsule, MoleExecution, MoleExecutionContext, MoleServices, Source }
  import org.openmole.core.workflow.sampling.Sampling
  import org.openmole.core.workflow.task.{ EmptyTask, ExplorationTask, Task }
  import org.openmole.core.workflow.tools.OptionalArgument
  import org.openmole.core.workflow.transition._
  import org.openmole.core.workflow.validation.TypeUtil
  import shapeless.{ ::, HList }

  object Puzzle {

    def apply(s: TransitionSlot): Puzzle = Puzzle(s, lasts = Vector(s.capsule))

    /**
     * Merge two puzzles. The entry slot is taken as the entry slot of p1.
     * It concatenates transitions, data channels, environments and groupings; concatenates and reduce to distinct elements sources and hooks.
     * Last capsules are either merged, or taken as p1 depending on the argument mergeLast.
     *
     * @param p1
     * @param p2
     * @param mergeLasts
     * @return
     */
    def merge(p1: Puzzle, p2: Puzzle, mergeLasts: Boolean = false) = {
      new Puzzle(
        p1.firstSlot,
        if (!mergeLasts) p1.lasts else p1.lasts ++ p2.lasts,
        p1.transitions.toList ::: p2.transitions.toList,
        p1.dataChannels.toList ::: p2.dataChannels.toList,
        (p1.sources.toList ::: p2.sources.toList).distinct,
        (p1.hooks.toList ::: p2.hooks.toList).distinct,
        p1.environments ++ p2.environments,
        p1.grouping ++ p2.grouping
      )
    }

    def add(
      p:            Puzzle,
      transitions:  Iterable[ITransition]                 = Iterable.empty,
      dataChannels: Iterable[DataChannel]                 = Iterable.empty,
      sources:      Iterable[(MoleCapsule, Source)]       = Iterable.empty,
      hooks:        Iterable[(MoleCapsule, Hook)]         = Iterable.empty,
      environments: Map[MoleCapsule, EnvironmentProvider] = Map.empty,
      grouping:     Map[MoleCapsule, Grouping]            = Map.empty) =
      p.copy(
        transitions = p.transitions ++ transitions,
        dataChannels = p.dataChannels ++ dataChannels,
        sources = p.sources ++ sources,
        hooks = p.hooks ++ hooks,
        environments = p.environments ++ environments,
        grouping = p.grouping ++ grouping
      )

  }

  /**
   * A Puzzle is the overall wrapper of the different components of an experiment: it articulates the different <code>MoleCapsule</code> in the script
   * through transitions between slots, and incorporates data, sources, hooks, environments.
   *
   * @param firstSlot the first slot to enter the puzzle
   * @param lasts the set of MoleCapsule on which the puzzle ends
   * @param transitions the transitions from which the workflow is reconstructed
   * @param dataChannels
   * @param sources sources (mapped as MoleCapsule -> Hook)
   * @param hooks hooks (mapped as MoleCapsule -> Hook)
   * @param environments
   * @param grouping
   */
  case class Puzzle(
    firstSlot:    TransitionSlot,
    lasts:        Iterable[MoleCapsule],
    transitions:  Iterable[ITransition]                 = Iterable.empty,
    dataChannels: Iterable[DataChannel]                 = Iterable.empty,
    sources:      Iterable[(MoleCapsule, Source)]       = Iterable.empty,
    hooks:        Iterable[(MoleCapsule, Hook)]         = Iterable.empty,
    environments: Map[MoleCapsule, EnvironmentProvider] = Map.empty,
    grouping:     Map[MoleCapsule, Grouping]            = Map.empty) {

    def toMole = new Mole(firstSlot.capsule, transitions, dataChannels)

    def toExecution(implicit moleServices: MoleServices): MoleExecution =
      MoleExecution(toMole, sources, hooks, environments, grouping)

    def toExecution(
      implicits:          Context                                    = Context.empty,
      seed:               OptionalArgument[Long]                     = None,
      executionContext:   OptionalArgument[MoleExecutionContext]     = None,
      defaultEnvironment: OptionalArgument[LocalEnvironmentProvider] = None)(implicit moleServices: MoleServices): MoleExecution =
      MoleExecution(
        mole = toMole,
        sources = sources,
        hooks = hooks,
        environments = environments,
        grouping = grouping,
        implicits = implicits,
        defaultEnvironment = defaultEnvironment,
        executionContext = executionContext
      )

    def slots: Set[TransitionSlot] = (firstSlot :: transitions.map(_.end).toList).toSet
    def first = firstSlot.capsule
    def inputs = first.inputs(toMole, sources, hooks).toSeq
    def defaults = first.task.defaults
  }

  case class TaskNode(task: Task, strain: Boolean = false, master: Boolean = false, persist: Seq[Val[_]] = Seq.empty, environment: Option[EnvironmentProvider] = None, grouping: Option[Grouping] = None, hooks: Vector[Hook] = Vector.empty, sources: Vector[Source] = Vector.empty) {
    def on(environment: EnvironmentProvider) = copy(environment = Some(environment))
    def by(strategy: Grouping) = copy(grouping = Some(strategy))
    def hook(hooks: Hook*) = copy(hooks = this.hooks ++ hooks)
    def source(sources: Source*) = copy(sources = this.sources ++ sources)
  }

  object TransitionOrigin {

    def tasks(d: TransitionOrigin) =
      d match {
        case TaskOrigin(n)          ⇒ Vector(n)
        case TransitionDSLOrigin(o) ⇒ DSL.tasks(o)
        case _                      ⇒ Vector.empty
      }

    def dsl(o: TransitionOrigin) =
      o match {
        case TransitionDSLOrigin(d) ⇒ Some(d)
        case _                      ⇒ None
      }
  }

  sealed trait TransitionOrigin {
    def --(d: Seq[TransitionDestination]) = new --(this, d.toVector)
    def --(d1: TransitionDestination, d: TransitionDestination*) = new --(this, (Seq(d1) ++ d).toVector)
    def -<(d: TransitionDestination*) = new -<(this, d.toVector)
    def >-(d: TransitionDestination*) = new >-(this, d.toVector)
    def >|(d: TransitionDestination*) = new >|(this, d.toVector)
    def -<-(d: TransitionDestination*) = new -<-(this, d.toVector)
    def oo(d: TransitionDestination*) = new oo(this, d.toVector)
  }

  case class TaskOrigin(node: TaskNode) extends TransitionOrigin
  case class TransitionDSLOrigin(t: DSL) extends TransitionOrigin
<commit_msg>[Core] enh: code formating<commit_after>/*
 * Copyright (C) 2019 <NAME>
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

package org.openmole.core.workflow

/**
 * This package articulates workflows, by providing <code>Puzzle</code> and the bricks to construct them such as transitions.
 *
 * It also contains a large part of the DSL.
 */
package composition {

  import org.openmole.core.context.{ Context, Val }
  import org.openmole.core.expansion.Condition
  import org.openmole.core.outputmanager.OutputManager
  import org.openmole.core.workflow.builder.DefinitionScope
  import org.openmole.core.workflow.execution.{ EnvironmentProvider, LocalEnvironmentProvider }
  import org.openmole.core.workflow.mole.{ Grouping, Hook, MasterCapsule, Mole, MoleCapsule, MoleExecution, MoleExecutionContext, MoleServices, Source }
  import org.openmole.core.workflow.sampling.Sampling
  import org.openmole.core.workflow.task.{ EmptyTask, ExplorationTask, Task }
  import org.openmole.core.workflow.tools.OptionalArgument
  import org.openmole.core.workflow.transition._
  import org.openmole.core.workflow.validation.TypeUtil
  import shapeless.{ ::, HList }

  object Puzzle {

    def apply(s: TransitionSlot): Puzzle = Puzzle(s, lasts = Vector(s.capsule))

    /**
     * Merge two puzzles. The entry slot is taken as the entry slot of p1.
     * It concatenates transitions, data channels, environments and groupings; concatenates and reduce to distinct elements sources and hooks.
     * Last capsules are either merged, or taken as p1 depending on the argument mergeLast.
     *
     * @param p1
     * @param p2
     * @param mergeLasts
     * @return
     */
    def merge(p1: Puzzle, p2: Puzzle, mergeLasts: Boolean = false) = {
      new Puzzle(
        p1.firstSlot,
        if (!mergeLasts) p1.lasts else p1.lasts ++ p2.lasts,
        p1.transitions.toList ::: p2.transitions.toList,
        p1.dataChannels.toList ::: p2.dataChannels.toList,
        (p1.sources.toList ::: p2.sources.toList).distinct,
        (p1.hooks.toList ::: p2.hooks.toList).distinct,
        p1.environments ++ p2.environments,
        p1.grouping ++ p2.grouping
      )
    }

    def add(
      p:            Puzzle,
      transitions:  Iterable[ITransition]                 = Iterable.empty,
      dataChannels: Iterable[DataChannel]                 = Iterable.empty,
      sources:      Iterable[(MoleCapsule, Source)]       = Iterable.empty,
      hooks:        Iterable[(MoleCapsule, Hook)]         = Iterable.empty,
      environments: Map[MoleCapsule, EnvironmentProvider] = Map.empty,
      grouping:     Map[MoleCapsule, Grouping]            = Map.empty) =
      p.copy(
        transitions = p.transitions ++ transitions,
        dataChannels = p.dataChannels ++ dataChannels,
        sources = p.sources ++ sources,
        hooks = p.hooks ++ hooks,
        environments = p.environments ++ environments,
        grouping = p.grouping ++ grouping
      )

  }

  /**
   * A Puzzle is the overall wrapper of the different components of an experiment: it articulates the different <code>MoleCapsule</code> in the script
   * through transitions between slots, and incorporates data, sources, hooks, environments.
   *
   * @param firstSlot the first slot to enter the puzzle
   * @param lasts the set of MoleCapsule on which the puzzle ends
   * @param transitions the transitions from which the workflow is reconstructed
   * @param dataChannels
   * @param sources sources (mapped as MoleCapsule -> Hook)
   * @param hooks hooks (mapped as MoleCapsule -> Hook)
   * @param environments
   * @param grouping
   */
  case class Puzzle(
    firstSlot:    TransitionSlot,
    lasts:        Iterable[MoleCapsule],
    transitions:  Iterable[ITransition]                 = Iterable.empty,
    dataChannels: Iterable[DataChannel]                 = Iterable.empty,
    sources:      Iterable[(MoleCapsule, Source)]       = Iterable.empty,
    hooks:        Iterable[(MoleCapsule, Hook)]         = Iterable.empty,
    environments: Map[MoleCapsule, EnvironmentProvider] = Map.empty,
    grouping:     Map[MoleCapsule, Grouping]            = Map.empty) {

    def toMole = new Mole(firstSlot.capsule, transitions, dataChannels)

    def toExecution(implicit moleServices: MoleServices): MoleExecution =
      MoleExecution(toMole, sources, hooks, environments, grouping)

    def toExecution(
      implicits:          Context                                    = Context.empty,
      seed:               OptionalArgument[Long]                     = None,
      executionContext:   OptionalArgument[MoleExecutionContext]     = None,
      defaultEnvironment: OptionalArgument[LocalEnvironmentProvider] = None)(implicit moleServices: MoleServices): MoleExecution =
      MoleExecution(
        mole = toMole,
        sources = sources,
        hooks = hooks,
        environments = environments,
        grouping = grouping,
        implicits = implicits,
        defaultEnvironment = defaultEnvironment,
        executionContext = executionContext
      )

    def slots: Set[TransitionSlot] = (firstSlot :: transitions.map(_.end).toList).toSet
    def first = firstSlot.capsule
    def inputs = first.inputs(toMole, sources, hooks).toSeq
    def defaults = first.task.defaults
  }

  case class TaskNode(task: Task, strain: Boolean = false, master: Boolean = false, persist: Seq[Val[_]] = Seq.empty, environment: Option[EnvironmentProvider] = None, grouping: Option[Grouping] = None, hooks: Vector[Hook] = Vector.empty, sources: Vector[Source] = Vector.empty) {
    def on(environment: EnvironmentProvider) = copy(environment = Some(environment))
    def by(strategy: Grouping) = copy(grouping = Some(strategy))
    def hook(hooks: Hook*) = copy(hooks = this.hooks ++ hooks)
    def source(sources: Source*) = copy(sources = this.sources ++ sources)
  }

  object TransitionOrigin {

    def tasks(d: TransitionOrigin) =
      d match {
        case TaskOrigin(n)          ⇒ Vector(n)
        case TransitionDSLOrigin(o) ⇒ DSL.tasks(o)
        case _                      ⇒ Vector.empty
      }

    def dsl(o: TransitionOrigin) =
      o match {
        case TransitionDSLOrigin(d) ⇒ Some(d)
        case _                      ⇒ None
      }
  }

  sealed trait TransitionOrigin {
    def --(d1: TransitionDestination, d: TransitionDestination*) = new --(this, (Seq(d1) ++ d).toVector)
    def --(d: Seq[TransitionDestination]) = new --(this, d.toVector)

    def -<(d: TransitionDestination*) = new -<(this, d.toVector)
    def >-(d: TransitionDestination*) = new >-(this, d.toVector)
    def >|(d: TransitionDestination*) = new >|(this, d.toVector)
    def -<-(d: TransitionDestination*) = new -<-(this, d.toVector)

    def oo(d: TransitionDestination*) = new oo(this, d.toVector)
  }

  case class TaskOrigin(node: TaskNode) extends TransitionOrigin
  case class TransitionDSLOrigin(t: DSL) extends TransitionOrigin

{'fraction_non_alphanumeric': 0.09813301441764667, 'fraction_numerical': 0.0038535645472061657, 'mean_word_length': 2.934134866701516, 'pattern_counts': {'":': 0, '<': 23, '<?xml version=': 0, '>': 27, 'https://': 0, 'lorem ipsum': 0, 'www.': 2, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 2, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '2659815', 'n_tokens_mistral': 4444, 'n_tokens_neox': 4356, 'n_words': 1253}