Skip to content

Commit

Permalink
more logic tests
Browse files Browse the repository at this point in the history
  • Loading branch information
rssh committed Jan 21, 2024
1 parent 4cb2efb commit e7aaac4
Show file tree
Hide file tree
Showing 3 changed files with 202 additions and 3 deletions.
3 changes: 3 additions & 0 deletions build.sbt
Original file line number Diff line number Diff line change
Expand Up @@ -198,6 +198,9 @@ lazy val logic = crossProject(JSPlatform, JVMPlatform, NativePlatform)
.settings(
name := "dotty-cps-async-logic",
libraryDependencies += "com.github.sbt" % "junit-interface" % "0.13.3" % "test",
).jsSettings(
scalaJSUseMainModuleInitializer := true,
libraryDependencies += ("org.scala-js" %% "scalajs-junit-test-runtime" % "1.8.0" % Test).cross(CrossVersion.for3Use2_13),
)


158 changes: 158 additions & 0 deletions logic/shared/src/test/scala/cps/logic/AgadaTest.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,158 @@
package cps.logic

import org.junit.{Test,Ignore}

import cps.*
import cps.monads.logic.*

/**
* Someone in Dreadsbury Mansion killed Aunt Agatha.
*Agatha
*, the butler
*, and Charles live in Dreadsbury Mansion
*, and
* are the only ones to live there.
* (+)A killer always hates and is
* (+) no richer than his victim.
* (+)Charles hates noone that Agatha hates
*.
*(+)Agatha hates everybody except the butler
*.(+) The butler hates everyone
*not richer than Aunt Agatha.
* (+) The butler hates everyone whom Agatha hates.
*(+) Noone hates everyone.
* Who killed Agatha ?
**/

class AgadaTest {

@Test def testAgada(): Unit = {
val worlds = AgadaTest.allWorlds()
val result = worlds.toLazyList.toIndexedSeq
val killer = result.map(_.killer).toSet
//for { r <- result } {
// println(s"result.killer=${r.killer}")
// println(s"result.richer=${r.richer}")
// println(s"result.hates=${r.hates}")
// println("------------------")
//}
assert(killer.size == 1)
assert(killer.head == AgadaTest.agata)
}

}

object AgadaTest {

case class Person(name: String)
case class Richer(p1: Person, p2: Person)
case class Hates(p1: Person, p2: Person)

val agata = Person("Agatha")
val butler = Person("Butler")
val charles = Person("Charles")

val citizents = List(agata,butler,charles)

case class World(
richer: Set[Richer],
hates: Set[Hates],
killer: Person
)

def allWorlds(): LogicStream[World] = reify[LogicStream]{
val richer = reflect(allRichers())
val hates = reflect(allHates())
val killer = reflect(LogicStream.fromCollection(citizents))

// a killer alwasy hates. ... victim

guard {
hates.contains(Hates(killer,agata))
}

guard {
// a killer is no richer than his victim
!richer.contains(Richer(killer,agata))
}


//The butler hates everyone
// * not richer than Aunt Agatha
guard {
citizents.forall(p =>
if (richer.contains(Richer(agata,p))) {
hates.contains(Hates(butler,p))
} else {
true
}
)
}

World(richer, hates, killer)
}


def allRichers(): LogicStream[Set[Richer]] = reify[LogicStream]{
val allRicher = for(p1 <- citizents; p2 <- citizents if p1!=p2) yield Richer(p1,p2)
val candidate = reflect(allSubsets(allRicher.toIndexedSeq)).toSet
guard(candidate.forall{ case Richer(p1,p2) => !candidate.contains(Richer(p2,p1)) })
guard{
// note, that if we assume, that all wealth are different.
// if we don't assume this, other solutions become possible
val pairs = for{ p1 <- citizents; p2 <- citizents if p1!=p2 } yield (p1,p2)
pairs.forall{ case (p1,p2) =>
candidate.contains(Richer(p1,p2)) || candidate.contains(Richer(p2,p1))
}
}
candidate
}

def allHates(): LogicStream[Set[Hates]] = reify[LogicStream]{
def allPairs = for(p1 <- citizents; p2 <- citizents) yield Hates(p1,p2)
val candidate = reflect(allSubsets(allPairs.toIndexedSeq)).toSet

// Agatha hates everybody except the butler
guard(
!candidate.contains(Hates(agata,butler)) &&
candidate.contains(Hates(agata,agata)) &&
candidate.contains(Hates(agata,charles))
)

// Charles hates noone that Agatha hates
// The butler hates everyone whom Agatha hates.
guard(
citizents.forall{ p =>
if (candidate.contains(Hates(agata,p))) {
!candidate.contains(Hates(charles,p))
&&
candidate.contains(Hates(butler,p))
} else {
true
}
}
)



//Noone hates everyone.
guard{
citizents.forall(p => !citizents.forall(p1 => candidate.contains(Hates(p,p1))))
}

candidate
}

def allSubsets[T](xs: IndexedSeq[T], from:Int=0): LogicStream[List[T]] = reify[LogicStream]{
if (xs.size == from) {
List.empty
} else {
val head = xs(from)
val tail = reflect(allSubsets(xs,from+1))
val next = LogicStream.pure(head::tail) |+| LogicStream.pure(tail)
reflect(next)
}
}


}
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,47 @@ import cps.monads.logic.*
class DefaultLogicMonadBasicTest {

@Test
def logicMonadFromCollection() = {
val m = LogicStream.fromCollection(List(1,2,3))

def basicInterleaveFromCollection() = {
val m1 = LogicStream.fromCollection(List(1,2,3))
val m2 = LogicStream.fromCollection(List(4,5,6))
assert(m1.toLazyList.toSeq == Seq(1,2,3))
val m3 = m1 |+| m2
assert(m3.toLazyList.toSeq == Seq(1,2,3,4,5,6))
val m4 = m2 |+| m1
assert(m4.toLazyList.toSeq == Seq(4,5,6,1,2,3))
val m5 = m1 | m2
//println(s"m5=${m5.toLazyList.toIndexedSeq}")
assert(m5.toLazyList.toSeq == Seq(1,4,2,5,3,6))
}

@Test
def basicFairFlatMap() = {
val m1 = LogicStream.fromCollection(List(1, 2))
val m2 = m1 &>> { x =>
if (x == 2) {
LogicStream.fromCollection(List(4, 5, 6))
} else {
LogicStream.fromCollection(List(7, 8, 9))
}
}
assert(m2.toLazyList.toSeq == Seq(7,4,8,5,9,6))
}

@Test
def testBaiscOnce(): Unit = {
val m1 = LogicStream.fromCollection(List(1, 2))
val m2 = once(m1)
assert(m2.toLazyList.toSeq == Seq(1))
}

@Test
def testEmpty() = {
val m1 = LogicStream.fromCollection(List.empty[Int])
val m2 = LogicStream.fromCollection(List(1, 2))
val m3 = m1 |+| m2
assert(m1.toLazyList.toSeq == Seq.empty)
assert(m2.toLazyList.toSeq == Seq(1,2))
assert(m3.toLazyList.toSeq == Seq(1,2))
}


Expand Down

0 comments on commit e7aaac4

Please sign in to comment.