<commit_before>package fpinscala.monads

import java.util.concurrent.ExecutorService
import java.util.concurrent.Executors
import org.junit.runner.RunWith
import org.scalatest.BeforeAndAfterEach
import org.scalatest.FlatSpec
import org.scalatest.prop.PropertyChecks
import Monad.idMonad
import Monad.listMonad
import Monad.optionMonad
import Monad.parMonad
import Monad.parserMonad
import Monad.streamMonad
import fpinscala.parsing.ParserImpl
import fpinscala.parsing.ParserTypes
import org.scalacheck.Arbitrary
import org.scalacheck.Gen

@RunWith(classOf[org.scalatest.junit.JUnitRunner])
class MonadSpec extends FlatSpec with PropertyChecks with BeforeAndAfterEach {

  // tests w/ Int are simplest
  private type T = Int

  private implicit def arbitraryMonad[M[_] <: Monad[M]](m: M[T]): Arbitrary[M[T]] =
    Arbitrary(Gen.choose(-100, 100) map(m.unit(_)))

  var executorService: ExecutorService = _

  override def beforeEach = executorService = Executors.newCachedThreadPool
  override def afterEach = executorService.shutdown

  private[MonadSpec] case class MonadTest[F[_]](M: Monad[F],
      mEq: (F[Int], F[Int]) => Boolean = ((_:F[Int]) == (_:F[Int])))
  {
    import M._
    def kleisli[B](f: T => B) = (a: T) => unit[B](f(a))
    val f = kleisli[T](_ + 1)
    val g = kleisli(_ + 2)
    val h = kleisli(_ + 4)
    val fg = kleisli(_ + 3)
    val fgh = kleisli(_ + 7)

    def testMonad = {
      testFlatMap
      testUnit
      mapPreservesStructure
    }

    def testFlatMap =
      forAll("n") { n: T =>
        assertEq(flatMap(f(n))(g), fg(n))
      }
    def testUnit =
      forAll("n") { n: T =>
        assertEq(flatMap(unit(n))(x => unit(x)), unit(n))
      }
    def mapPreservesStructure =
      forAll("n") { n: T =>
        val m = unit[T](n)
        val idM = map(m)(identity[T])
        assertEq(idM, m)
      }
    private def assertEq(m1: F[T], m2: F[T]) =
      assert(mEq(m1, m2), s"""eq($m1, $m2)""")

    def testSequence =
      forAll("l") { l: List[T] =>
        val lma = l map(M.unit(_))
        assert(sequence(lma) == M.unit(l))
      }

    def testTraverse =
      forAll("l") { l: List[T] =>
        val lma = l map(M.unit(_))
        assert(traverse(l)(f) == M.unit(l map(_ + 1)))
      }

    def testCompose =
      forAll("n") { n: T =>
        assert(compose(f, g)(n) == fg(n))
        assert(compose(compose(f, g), h)(n) == fgh(n))
      }

    def associativeLaw =
      forAll("n") { n: T =>
        assert(compose(compose(f, g), h)(n) == compose(f, compose(g, h))(n))
      }

    def identityLaws = {
      val strictUnit = (t: T) => unit(t)
      val leftIdentity = compose(f, strictUnit)
      val rightIdentity = compose(unit[T], f)

      forAll("n") { n: T =>
        assert(leftIdentity(n) == f(n), "leftIdentity")
        assert(rightIdentity(n) == f(n), "rightIdentity")
        assert(flatMap(f(n))(strictUnit) == f(n), "leftIdentity (flatMap)")
        assert(flatMap(strictUnit(n))(f) == f(n), "rightIdentity (flatMap)")
      }
    }
  }

  val listMonadTest = MonadTest(listMonad)
  val optionMonadTest = MonadTest(optionMonad)
  val streamMonadTest = MonadTest(streamMonad)
  val parMonadTest = {
    import fpinscala.parallelism.Par.{run => prun, Par}
    MonadTest(parMonad,
        (p1: Par[Int], p2: Par[Int]) => prun(executorService)(p1) == prun(executorService)(p2))
  }
  val parserMonadTest =
    MonadTest(parserMonad(ParserImpl),
      (p1: ParserTypes.Parser[Int], p2: ParserTypes.Parser[Int]) => {
        ParserImpl.run(p1)("") == ParserImpl.run(p1)("")
      })
  val idMonadTest = MonadTest(idMonad)

  behavior of "11.1.1 parMonad"
  it should "work" in  parMonadTest.testMonad

  behavior of "11.1.2 ParserMonad"
  it should "work" in parserMonadTest.testMonad

  behavior of "11.1.3 optionMonad"
  it should "work" in optionMonadTest.testMonad

  behavior of "11.1.4 streamMonad"
  it should "work" in streamMonadTest.testMonad

  behavior of "11.1.5 listMonad"
  it should "work" in listMonadTest.testMonad

  behavior of "11.3.1 sequence"
  it should "work in ListMonad" in listMonadTest.testSequence
  it should "work in OptionMonad" in optionMonadTest.testSequence

  behavior of "11.3.2 traverse"
  it should "work in ListMonad" in listMonadTest.testTraverse
  it should "work in OptionMonad" in optionMonadTest.testTraverse

  behavior of "11.4 replicateM"

  behavior of "11.5.1 replicateM in ListMonad"
  it should "work" in {
    val tests =
      Table(
        ("n", "ma: List[Int]", "replicateM(n, ma)"),
        (0, List[Int](), List(List[Int]())),
        (1, List[Int](), List()),
        (2, List[Int](), List()),
        (3, List[Int](), List()),

        (0, List(1), List(List[Int]())),
        (1, List(1), List(List(1))),
        (2, List(1), List(List(1, 1))),
        (3, List(1), List(List(1, 1, 1))),

        (0, List(1, 2), List(List[Int]())),
        (1, List(1, 2), List(List(1), List(2))),
        (2, List(1, 2), List(List(1, 1), List(1, 2), List(2, 1), List(2, 2))),
        (3, List(1, 2), List(List(1, 1, 1), List(1, 1, 2), List(1, 2, 1), List(1, 2, 2), List(2, 1, 1),
          List(2, 1, 2), List(2, 2, 1), List(2, 2, 2))))
    forAll(tests) { (n: Int, ma: List[Int], expected: List[List[Int]]) =>
      assert(listMonad.replicateM(n, ma) == expected)
    }
  }

  behavior of "11.5.2 replicateM in OptionMonad"
  it should "work" in {
    val tests =
      Table[Int, Option[Int], Option[List[Int]]](<commit_msg>chapter 11 exercises and tests<commit_after>package fpinscala.monads

import java.util.concurrent.ExecutorService
import java.util.concurrent.Executors
import org.junit.runner.RunWith
import org.scalatest.BeforeAndAfterEach
import org.scalatest.FlatSpec
import org.scalatest.prop.PropertyChecks
import Monad.idMonad
import Monad.listMonad
import Monad.optionMonad
import Monad.parMonad
import Monad.parserMonad
import Monad.streamMonad
import fpinscala.parsing.ParserImpl
import fpinscala.parsing.ParserTypes
import org.scalacheck.Arbitrary
import org.scalacheck.Gen

@RunWith(classOf[org.scalatest.junit.JUnitRunner])
class MonadSpec extends FlatSpec with PropertyChecks with BeforeAndAfterEach {

  // tests w/ Int are simplest
  private type T = Int

  private implicit def arbitraryMonad[M[_] <: Monad[M]](m: M[T]): Arbitrary[M[T]] =
    Arbitrary(Gen.choose(-100, 100) map(m.unit(_)))

  var executorService: ExecutorService = _

  override def beforeEach = executorService = Executors.newCachedThreadPool
  override def afterEach = executorService.shutdown

  private[MonadSpec] case class MonadTest[F[_]](M: Monad[F],
      mEq: (F[Int], F[Int]) => Boolean = ((_:F[Int]) == (_:F[Int])))
  {
    import M._
    def kleisli[B](f: T => B) = (a: T) => unit[B](f(a))
    val f = kleisli[T](_ + 1)
    val g = kleisli(_ + 2)
    val h = kleisli(_ + 4)
    val fg = kleisli(_ + 3)
    val fgh = kleisli(_ + 7)

    def testMonad = {
      testFlatMap
      testUnit
      mapPreservesStructure
    }

    def testFlatMap =
      forAll("n") { n: T =>
        assertEq(flatMap(f(n))(g), fg(n))
      }
    def testUnit =
      forAll("n") { n: T =>
        assertEq(flatMap(unit(n))(x => unit(x)), unit(n))
      }
    def mapPreservesStructure =
      forAll("n") { n: T =>
        val m = unit(n)
        val idM = map(m)(identity[T])
        assertEq(idM, m)
      }
    private def assertEq(m1: F[T], m2: F[T]) =
      assert(mEq(m1, m2), s"""eq($m1, $m2)""")

    def testSequence =
      forAll("l") { l: List[T] =>
        val lma = l map(M.unit(_))
        assert(sequence(lma) == unit(l))
      }

    def testTraverse =
      forAll("l") { l: List[T] =>
        assert(traverse(l)(f) == unit(l map(_ + 1)))
      }

    def testReplicateM =
      forAll(Gen.choose(0, 100) label "n") { n: Int =>
        assert(replicateM(n, unit(1)) == unit(List.fill(n)(1)))
      }

    def testCompose =
      forAll("n") { n: T =>
        assert(compose(f, g)(n) == fg(n))
        assert(compose(compose(f, g), h)(n) == fgh(n))
      }

    def associativeLaw =
      forAll("n") { n: T =>
        assert(compose(compose(f, g), h)(n) == compose(f, compose(g, h))(n))
      }

    def identityLaws = {
      val strictUnit = (t: T) => unit(t)
      val leftIdentity = compose(f, strictUnit)
      val rightIdentity = compose(unit[T], f)

      forAll("n") { n: T =>
        assert(leftIdentity(n) == f(n), "leftIdentity")
        assert(rightIdentity(n) == f(n), "rightIdentity")
        assert(flatMap(f(n))(strictUnit) == f(n), "leftIdentity (flatMap)")
        assert(flatMap(strictUnit(n))(f) == f(n), "rightIdentity (flatMap)")
      }
    }
  }

  val listMonadTest = MonadTest(listMonad)
  val optionMonadTest = MonadTest(optionMonad)
  val streamMonadTest = MonadTest(streamMonad)
  val parMonadTest = {
    import fpinscala.parallelism.Par.{run => prun, Par}
    MonadTest(parMonad,
        (p1: Par[Int], p2: Par[Int]) => prun(executorService)(p1) == prun(executorService)(p2))
  }
  val parserMonadTest =
    MonadTest(parserMonad(ParserImpl),
      (p1: ParserTypes.Parser[Int], p2: ParserTypes.Parser[Int]) => {
        ParserImpl.run(p1)("") == ParserImpl.run(p1)("")
      })
  val idMonadTest = MonadTest(idMonad)

  behavior of "11.1.1 parMonad"
  it should "work" in  parMonadTest.testMonad

  behavior of "11.1.2 ParserMonad"
  it should "work" in parserMonadTest.testMonad

  behavior of "11.1.3 optionMonad"
  it should "work" in optionMonadTest.testMonad

  behavior of "11.1.4 streamMonad"
  it should "work" in streamMonadTest.testMonad

  behavior of "11.1.5 listMonad"
  it should "work" in listMonadTest.testMonad

  behavior of "11.3.1 sequence"
  it should "work in ListMonad" in listMonadTest.testSequence
  it should "work in OptionMonad" in optionMonadTest.testSequence

  behavior of "11.3.2 traverse"
  it should "work in ListMonad" in listMonadTest.testTraverse
  it should "work in OptionMonad" in optionMonadTest.testTraverse

  behavior of "11.4 replicateM"
  it should "work in ListMonad" in listMonadTest.testReplicateM
  it should "work in OptionMonad" in optionMonadTest.testReplicateM

  behavior of "11.5.1 replicateM in ListMonad"
  it should "work" in {
    val tests =
      Table(
        ("n", "ma: List[Int]", "replicateM(n, ma)"),
        (0, List[Int](), List(List[Int]())),
        (1, List[Int](), List()),
        (2, List[Int](), List()),
        (3, List[Int](), List()),

        (0, List(1), List(List[Int]())),
        (1, List(1), List(List(1))),
        (2, List(1), List(List(1, 1))),
        (3, List(1), List(List(1, 1, 1))),

        (0, List(1, 2), List(List[Int]())),
        (1, List(1, 2), List(List(1), List(2))),
        (2, List(1, 2), List(List(1, 1), List(1, 2), List(2, 1), List(2, 2))),
        (3, List(1, 2), List(List(1, 1, 1), List(1, 1, 2), List(1, 2, 1), List(1, 2, 2), List(2, 1, 1),
          List(2, 1, 2), List(2, 2, 1), List(2, 2, 2))))
    forAll(tests) { (n: Int, ma: List[Int], expected: List[List[Int]]) =>
      assert(listMonad.replicateM(n, ma) == expected)
    }
  }

  behavior of "11.5.2 replicateM in OptionMonad"
  it should "work" in {
    val tests =
      Table[Int, Option[Int], Option[List[Int]]](
{'fraction_non_alphanumeric': 0.1625359195402299, 'fraction_numerical': 0.023886494252873564, 'mean_word_length': 3.174287856071964, 'pattern_counts': {'":': 0, '<': 5, '<?xml version=': 0, '>': 38, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '2011210', 'n_tokens_mistral': 4339, 'n_tokens_neox': 4162, 'n_words': 968}