Skip to content

Instantly share code, notes, and snippets.

@waynejo
Last active October 23, 2020 13:04
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save waynejo/0d22e5e022f18f898ba4bdae5c6cbab7 to your computer and use it in GitHub Desktop.
Save waynejo/0d22e5e022f18f898ba4bdae5c6cbab7 to your computer and use it in GitHub Desktop.
import scala.collection.mutable.HashSet
import java.util.Objects
import scala.collection.mutable
object Main extends App {
// var container1 = new Container()
// container1.addWater(10)
// println(container1)
// var container2 = new Container()
// container2.addWater(20)
// println(container2)
// container1.connectTo(container2)
// println(container1)
var container1 = new Container2()
container1.addWater(10)
println(container1)
var container2 = new Container2()
container2.addWater(20)
println(container2)
container1.connectTo(container2)
println(container1)
}
class Container {
private var amount: Double = 0
private var group = mutable.HashSet[Container](this)
def getAmount: Double = {
amount
}
def connectTo(other: Container): Unit = { // Pre-condition
Objects.requireNonNull(other, "Cannot connect to a null container.")
if (group eq other.group) return
val size1 = group.size
val size2 = other.group.size
val tot1 = amount * size1
val tot2 = other.amount * size2
val newAmount = (tot1 + tot2) / (size1 + size2)
group.addAll(other.group)
for (x <- other.group) {
x.group = group
}
for (x <- group) {
x.amount = newAmount
}
assert(invariantsArePreservedByConnectTo(other), "connectTo broke an invariant!")
}
private def invariantsArePreservedByConnectTo(other: Container) = {
group == other.group &&
isGroupNonNegative &&
isGroupBalanced &&
isGroupConsistent
}
def addWater(amount: Double) = {
val amountPerContainer = amount / group.size
// Pre-condition
if (this.amount + amountPerContainer < 0)
throw new IllegalArgumentException(
"Not enough water to match the addWater request.");
for (element <- group) {
element.amount += amountPerContainer
}
assert(invariantsArePreservedByAddWater, "addWater broke an invariant!")
}
def invariantsArePreservedByAddWater: Boolean = {
return isGroupNonNegative && isGroupBalanced
}
def isGroupNonNegative: Boolean = { // Invariant I1
!group.exists { _.amount < 0}
}
def isGroupConsistent: Boolean = { // Invariants I2, I3
!group.exists { _.group != group}
}
def isGroupBalanced: Boolean = { // Invariant I4
!group.exists { _.amount != amount}
}
def toString(histories: Set[Container]): String = {
val groupText = group.map { element =>
if (element eq this) {
"self"
} else if (histories.contains(this)) {
"..."
} else {
element.toString(histories + this).split("\n").map(" " + _).mkString("\n")
}
}.mkString("\n ")
s"""Container {
| amount = $amount,
| group = $groupText
|}""".stripMargin
}
override def toString: String = {
toString(Set())
}
}
import java.util.Objects
import scala.collection.mutable
private class ConnectPostData {
var group1: mutable.HashSet[Container2] = mutable.HashSet[Container2]()
var group2: mutable.HashSet[Container2] = mutable.HashSet[Container2]()
var amount1 = .0
var amount2 = .0
}
class Container2 {
private var amount: Double = 0
private var group = mutable.HashSet[Container2](this)
def getAmount: Double = {
amount
}
def connectTo(other: Container2): Unit = { // Pre-condition check
Objects.requireNonNull(other, "Cannot connect to a null container.")
if (group eq other.group) {
return
}
val postData = saveConnectPostData(other)
assert(postData != null)
val size1 = group.size
val size2 = other.group.size
val tot1 = amount * size1
val tot2 = other.amount * size2
val newAmount = (tot1 + tot2) / (size1 + size2)
group.addAll(other.group)
for (x <- other.group) {
x.group = group
}
for (x <- group) {
x.amount = newAmount
}
assert(postConnect(postData), "connectTo failed its post-condition!")
}
private def saveConnectPostData(other: Container2) = {
val data = new ConnectPostData
data.group1 = group.clone()
data.group2 = other.group.clone()
data.amount1 = amount
data.amount2 = other.amount
data
}
private def postConnect(postData: ConnectPostData): Boolean = {
areGroupMembersCorrect(postData) &&
isGroupAmountCorrect(postData) &&
isGroupBalanced &&
isGroupConsistent
}
private def areGroupMembersCorrect(postData: ConnectPostData): Boolean = {
postData.group1.diff(group).isEmpty &&
postData.group2.diff(group).isEmpty &&
group.size == postData.group1.size + postData.group2.size
}
private def isGroupAmountCorrect(postData: ConnectPostData) = {
val size1 = postData.group1.size
val size2 = postData.group2.size
val tot1 = postData.amount1 * size1
val tot2 = postData.amount2 * size2
val newAmount = (tot1 + tot2) / (size1 + size2)
almostEqual(amount, newAmount)
}
def addWater(amount: Double): Unit = {
val amountPerContainer = amount / group.size
if (this.amount + amountPerContainer < 0) {
throw new IllegalArgumentException("Not enough water to match the addWater request.")
}
val oldTotal = groupAmount // for the post-condition
assert(oldTotal >= 0)
for (c <- group) {
c.amount += amountPerContainer
}
assert(postAddWater(oldTotal, amount), "addWater failed its post-condition!")
}
private def postAddWater(oldTotal: Double, addedAmount: Double) = {
isGroupBalanced && almostEqual(groupAmount, oldTotal + addedAmount)
}
private def almostEqual(x: Double, y: Double) = {
val EPSILON = 1E-4
Math.abs(x - y) < EPSILON
}
def isGroupConsistent: Boolean = { // Invariants I2, I3
!group.exists { _.group != group}
}
def isGroupBalanced: Boolean = { // Invariant I4
!group.exists { _.amount != amount}
}
def groupAmount: Int = {
group.map(_.amount).sum.toInt
}
def toString(histories: Set[Container2]): String = {
val groupText = group.map { element =>
if (element eq this) {
"self"
} else if (histories.contains(this)) {
"..."
} else {
element.toString(histories + this).split("\n").map(" " + _).mkString("\n")
}
}.mkString("\n ")
s"""Container {
| amount = $amount,
| group = $groupText
|}""".stripMargin
}
override def toString: String = {
toString(Set())
}
}
Container {
amount = 10.0,
group = self
}
Container {
amount = 20.0,
group = self
}
Container {
amount = 15.0,
group = self
Container {
amount = 15.0,
group = self
Container {
amount = 15.0,
group = self
...
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment