Merge branch 'master' of github.com:jboner/akka
This commit is contained in:
commit
7cff6e2ae8
3 changed files with 314 additions and 975 deletions
|
|
@ -0,0 +1,139 @@
|
|||
package sample.fsm.dining.become
|
||||
|
||||
//Akka adaptation of
|
||||
//http://www.dalnefre.com/wp/2010/08/dining-philosophers-in-humus/
|
||||
|
||||
import akka.actor.{Scheduler, ActorRef, Actor}
|
||||
import akka.actor.Actor._
|
||||
import java.util.concurrent.TimeUnit
|
||||
|
||||
/*
|
||||
* First we define our messages, they basically speak for themselves
|
||||
*/
|
||||
sealed trait DiningHakkerMessage
|
||||
case class Busy(chopstick: ActorRef) extends DiningHakkerMessage
|
||||
case class Put(hakker: ActorRef) extends DiningHakkerMessage
|
||||
case class Take(hakker: ActorRef) extends DiningHakkerMessage
|
||||
case class Taken(chopstick: ActorRef) extends DiningHakkerMessage
|
||||
object Eat extends DiningHakkerMessage
|
||||
object Think extends DiningHakkerMessage
|
||||
|
||||
/*
|
||||
* A Chopstick is an actor, it can be taken, and put back
|
||||
*/
|
||||
class Chopstick(name: String) extends Actor {
|
||||
self.id = name
|
||||
|
||||
//When a Chopstick is taken by a hakker
|
||||
//It will refuse to be taken by other hakkers
|
||||
//But the owning hakker can put it back
|
||||
def takenBy(hakker: ActorRef): Receive = {
|
||||
case Take(otherHakker) =>
|
||||
otherHakker ! Busy(self)
|
||||
case Put(`hakker`) =>
|
||||
become(available)
|
||||
}
|
||||
|
||||
//When a Chopstick is available, it can be taken by a hakker
|
||||
def available: Receive = {
|
||||
case Take(hakker) =>
|
||||
become(takenBy(hakker))
|
||||
hakker ! Taken(self)
|
||||
}
|
||||
|
||||
//A Chopstick begins its existence as available
|
||||
def receive = available
|
||||
}
|
||||
|
||||
/*
|
||||
* A hakker is an awesome dude or dudett who either thinks about hacking or has to eat ;-)
|
||||
*/
|
||||
class Hakker(name: String,left: ActorRef, right: ActorRef) extends Actor {
|
||||
self.id = name
|
||||
|
||||
//When a hakker is thinking it can become hungry
|
||||
//and try to pick up its chopsticks and eat
|
||||
def thinking: Receive = {
|
||||
case Eat =>
|
||||
become(hungry)
|
||||
left ! Take(self)
|
||||
right ! Take(self)
|
||||
}
|
||||
|
||||
//When a hakker is hungry it tries to pick up its chopsticks and eat
|
||||
//When it picks one up, it goes into wait for the other
|
||||
//If the hakkers first attempt at grabbing a chopstick fails,
|
||||
//it starts to wait for the response of the other grab
|
||||
def hungry: Receive = {
|
||||
case Taken(`left`) =>
|
||||
become(waiting_for(right,left))
|
||||
case Taken(`right`) =>
|
||||
become(waiting_for(left,right))
|
||||
case Busy(chopstick) =>
|
||||
become(denied_a_chopstick)
|
||||
}
|
||||
|
||||
//When a hakker is waiting for the last chopstick it can either obtain it
|
||||
//and start eating, or the other chopstick was busy, and the hakker goes
|
||||
//back to think about how he should obtain his chopsticks :-)
|
||||
def waiting_for(chopstickToWaitFor: ActorRef, otherChopstick: ActorRef): Receive = {
|
||||
case Taken(`chopstickToWaitFor`) =>
|
||||
log.info("%s has picked up %s and %s, and starts to eat",name,left.id,right.id)
|
||||
become(eating)
|
||||
Scheduler.scheduleOnce(self,Think,5,TimeUnit.SECONDS)
|
||||
|
||||
case Busy(chopstick) =>
|
||||
become(thinking)
|
||||
otherChopstick ! Put(self)
|
||||
self ! Eat
|
||||
}
|
||||
|
||||
//When the results of the other grab comes back,
|
||||
//he needs to put it back if he got the other one.
|
||||
//Then go back and think and try to grab the chopsticks again
|
||||
def denied_a_chopstick: Receive = {
|
||||
case Taken(chopstick) =>
|
||||
become(thinking)
|
||||
chopstick ! Put(self)
|
||||
self ! Eat
|
||||
case Busy(chopstick) =>
|
||||
become(thinking)
|
||||
self ! Eat
|
||||
}
|
||||
|
||||
//When a hakker is eating, he can decide to start to think,
|
||||
//then he puts down his chopsticks and starts to think
|
||||
def eating: Receive = {
|
||||
case Think =>
|
||||
become(thinking)
|
||||
left ! Put(self)
|
||||
right ! Put(self)
|
||||
log.info("%s puts down his chopsticks and starts to think",name)
|
||||
Scheduler.scheduleOnce(self,Eat,5,TimeUnit.SECONDS)
|
||||
}
|
||||
|
||||
//All hakkers start in a non-eating state
|
||||
def receive = {
|
||||
case Think =>
|
||||
log.info("%s starts to think",name)
|
||||
become(thinking)
|
||||
Scheduler.scheduleOnce(self,Eat,5,TimeUnit.SECONDS)
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Alright, here's our test-harness
|
||||
*/
|
||||
object DiningHakkers {
|
||||
def run {
|
||||
//Create 5 chopsticks
|
||||
val chopsticks = for(i <- 1 to 5) yield actorOf(new Chopstick("Chopstick "+i)).start
|
||||
//Create 5 awesome hakkers and assign them their left and right chopstick
|
||||
val hakkers = for {
|
||||
(name,i) <- List("Ghosh","Bonér","Klang","Krasser","Manie").zipWithIndex
|
||||
} yield actorOf(new Hakker(name,chopsticks(i),chopsticks((i+1) % 5))).start
|
||||
|
||||
//Signal all hakkers that they should start thinking, and watch the show
|
||||
hakkers.foreach(_ ! Think)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,172 @@
|
|||
package sample.fsm.dining.fsm
|
||||
|
||||
import akka.actor.{ActorRef, Actor, FSM}
|
||||
import Actor._
|
||||
import java.util.concurrent.TimeUnit
|
||||
import TimeUnit._
|
||||
|
||||
/*
|
||||
* Some messages for the chopstick
|
||||
*/
|
||||
sealed trait ChopstickMessage
|
||||
object Take extends ChopstickMessage
|
||||
object Put extends ChopstickMessage
|
||||
case class Taken(chopstick: ActorRef) extends ChopstickMessage
|
||||
case class Busy(chopstick: ActorRef) extends ChopstickMessage
|
||||
|
||||
/**
|
||||
* Some states the chopstick can be in
|
||||
*/
|
||||
sealed trait ChopstickState
|
||||
case object Available extends ChopstickState
|
||||
case object Taken extends ChopstickState
|
||||
|
||||
/**
|
||||
* Some state container for the chopstick
|
||||
*/
|
||||
case class TakenBy(hakker: Option[ActorRef])
|
||||
|
||||
/*
|
||||
* A chopstick is an actor, it can be taken, and put back
|
||||
*/
|
||||
class Chopstick(name: String) extends Actor with FSM[ChopstickState, TakenBy] {
|
||||
self.id = name
|
||||
|
||||
// When a chopstick is available, it can be taken by a some hakker
|
||||
when(Available) {
|
||||
case Event(Take, _) =>
|
||||
goto(Taken) using TakenBy(self.sender) replying Taken(self)
|
||||
}
|
||||
|
||||
// When a chopstick is taken by a hakker
|
||||
// It will refuse to be taken by other hakkers
|
||||
// But the owning hakker can put it back
|
||||
when(Taken) {
|
||||
case Event(Take, currentState) =>
|
||||
stay replying Busy(self)
|
||||
case Event(Put, TakenBy(hakker)) if self.sender == hakker =>
|
||||
goto(Available) using TakenBy(None)
|
||||
}
|
||||
|
||||
// A chopstick begins its existence as available and taken by no one
|
||||
startWith(Available, TakenBy(None))
|
||||
}
|
||||
|
||||
/**
|
||||
* Some fsm hakker messages
|
||||
*/
|
||||
sealed trait FSMHakkerMessage
|
||||
object Think extends FSMHakkerMessage
|
||||
|
||||
/**
|
||||
* Some fsm hakker states
|
||||
*/
|
||||
sealed trait FSMHakkerState
|
||||
case object Waiting extends FSMHakkerState
|
||||
case object Thinking extends FSMHakkerState
|
||||
case object Hungry extends FSMHakkerState
|
||||
case object WaitForOtherChopstick extends FSMHakkerState
|
||||
case object FirstChopstickDenied extends FSMHakkerState
|
||||
case object Eating extends FSMHakkerState
|
||||
|
||||
/**
|
||||
* Some state container to keep track of which chopsticks we have
|
||||
*/
|
||||
case class TakenChopsticks(left: Option[ActorRef], right: Option[ActorRef])
|
||||
|
||||
/*
|
||||
* A fsm hakker is an awesome dude or dudette who either thinks about hacking or has to eat ;-)
|
||||
*/
|
||||
class FSMHakker(name: String, left: ActorRef, right: ActorRef) extends Actor with FSM[FSMHakkerState, TakenChopsticks] {
|
||||
self.id = name
|
||||
|
||||
when(Waiting) {
|
||||
case Event(Think, _) =>
|
||||
log.info("%s starts to think", name)
|
||||
startThinking(5, SECONDS)
|
||||
}
|
||||
|
||||
//When a hakker is thinking it can become hungry
|
||||
//and try to pick up its chopsticks and eat
|
||||
when(Thinking) {
|
||||
case Event(StateTimeout, _) =>
|
||||
left ! Take
|
||||
right ! Take
|
||||
goto(Hungry)
|
||||
}
|
||||
|
||||
// When a hakker is hungry it tries to pick up its chopsticks and eat
|
||||
// When it picks one up, it goes into wait for the other
|
||||
// If the hakkers first attempt at grabbing a chopstick fails,
|
||||
// it starts to wait for the response of the other grab
|
||||
when(Hungry) {
|
||||
case Event(Taken(`left`), _) =>
|
||||
goto(WaitForOtherChopstick) using TakenChopsticks(Some(left), None)
|
||||
case Event(Taken(`right`), _) =>
|
||||
goto(WaitForOtherChopstick) using TakenChopsticks(None, Some(right))
|
||||
case Event(Busy(_), _) =>
|
||||
goto(FirstChopstickDenied)
|
||||
}
|
||||
|
||||
// When a hakker is waiting for the last chopstick it can either obtain it
|
||||
// and start eating, or the other chopstick was busy, and the hakker goes
|
||||
// back to think about how he should obtain his chopsticks :-)
|
||||
when(WaitForOtherChopstick) {
|
||||
case Event(Taken(`left`), TakenChopsticks(None, Some(right))) => startEating(left, right)
|
||||
case Event(Taken(`right`), TakenChopsticks(Some(left), None)) => startEating(left, right)
|
||||
case Event(Busy(chopstick), TakenChopsticks(leftOption, rightOption)) =>
|
||||
leftOption.foreach(_ ! Put)
|
||||
rightOption.foreach(_ ! Put)
|
||||
startThinking(10, MILLISECONDS)
|
||||
}
|
||||
|
||||
private def startEating(left: ActorRef, right: ActorRef): State = {
|
||||
log.info("%s has picked up %s and %s, and starts to eat", name, left.id, right.id)
|
||||
goto(Eating) using TakenChopsticks(Some(left), Some(right)) forMax (5, SECONDS)
|
||||
}
|
||||
|
||||
// When the results of the other grab comes back,
|
||||
// he needs to put it back if he got the other one.
|
||||
// Then go back and think and try to grab the chopsticks again
|
||||
when(FirstChopstickDenied) {
|
||||
case Event(Taken(secondChopstick), _) =>
|
||||
secondChopstick ! Put
|
||||
startThinking(10, MILLISECONDS)
|
||||
case Event(Busy(chopstick), _) =>
|
||||
startThinking(10, MILLISECONDS)
|
||||
}
|
||||
|
||||
// When a hakker is eating, he can decide to start to think,
|
||||
// then he puts down his chopsticks and starts to think
|
||||
when(Eating) {
|
||||
case Event(StateTimeout, _) =>
|
||||
log.info("%s puts down his chopsticks and starts to think", name)
|
||||
left ! Put
|
||||
right ! Put
|
||||
startThinking(5, SECONDS)
|
||||
}
|
||||
|
||||
private def startThinking(period: Int, timeUnit: TimeUnit): State = {
|
||||
goto(Thinking) using TakenChopsticks(None, None) forMax (period, timeUnit)
|
||||
}
|
||||
|
||||
//All hakkers start waiting
|
||||
startWith(Waiting, TakenChopsticks(None, None))
|
||||
}
|
||||
|
||||
/*
|
||||
* Alright, here's our test-harness
|
||||
*/
|
||||
object DiningHakkersOnFsm {
|
||||
|
||||
def run = {
|
||||
// Create 5 chopsticks
|
||||
val chopsticks = for (i <- 1 to 5) yield actorOf(new Chopstick("Chopstick " + i)).start
|
||||
// Create 5 awesome fsm hakkers and assign them their left and right chopstick
|
||||
val hakkers = for{
|
||||
(name, i) <- List("Ghosh", "Bonér", "Klang", "Krasser", "Manie").zipWithIndex
|
||||
} yield actorOf(new FSMHakker(name, chopsticks(i), chopsticks((i + 1) % 5))).start
|
||||
|
||||
hakkers.foreach(_ ! Think)
|
||||
}
|
||||
}
|
||||
|
|
@ -406,12 +406,15 @@ class AkkaParentProject(info: ProjectInfo) extends DefaultProject(info) {
|
|||
}
|
||||
|
||||
class AkkaSampleRemoteProject(info: ProjectInfo) extends AkkaDefaultProject(info, deployPath)
|
||||
class AkkaSampleFSMProject(info: ProjectInfo) extends AkkaDefaultProject(info, deployPath)
|
||||
|
||||
class AkkaSamplesParentProject(info: ProjectInfo) extends ParentProject(info) {
|
||||
override def disableCrossPaths = true
|
||||
|
||||
lazy val akka_sample_ants = project("akka-sample-ants", "akka-sample-ants",
|
||||
new AkkaSampleAntsProject(_), akka_stm)
|
||||
lazy val akka_sample_fsm = project("akka-sample-fsm", "akka-sample-fsm",
|
||||
new AkkaSampleFSMProject(_), akka_actor)
|
||||
lazy val akka_sample_remote = project("akka-sample-remote", "akka-sample-remote",
|
||||
new AkkaSampleRemoteProject(_), akka_remote)
|
||||
}
|
||||
|
|
@ -499,978 +502,3 @@ trait DeployProject { self: BasicScalaProject =>
|
|||
trait OSGiProject extends BNDPlugin { self: DefaultProject =>
|
||||
override def bndExportPackage = Seq("akka.*;version=%s".format(projectVersion.value))
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue