Skip to content

Commit

Permalink
Update README and rename Tree
Browse files Browse the repository at this point in the history
  • Loading branch information
Dnomyar committed Dec 3, 2018
1 parent 51bf641 commit 0349d77
Show file tree
Hide file tree
Showing 5 changed files with 56 additions and 93 deletions.
30 changes: 24 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,29 @@ This project following the hexagonal architecture :
- the package `domain` contains the business logic and the business objects.


## Efficiency of operations
- `N`ew order in the book : `O(log(book_depth))`
- `U`pdate an order in the book : `O(log(book_depth))`
- `D`elete an order in the book : `O(log(book_depth))`

Overall, the complexity of the algorithm is `O(n * log(book_depth))` where `n` is the number of operations.


### Data structure: immutable indexed AVL Tree
#### Description
To have this this complexity, the data structure used is an immutable AVL tree (self-balanced tree). The tree is transformed to be able to have this interface :
```scala
sealed trait AVLIndexedTree[+T] {
def insert[U >: T](index: Int, elementToInsert: U): AVLIndexedTree[U]
def delete[U >: T](index: Int): AVLIndexedTree[U]
def updated[U >: T](index: Int, elementToInsert: U): AVLIndexedTree[U]
}
```
This interface allows to insert, delete and update nodes of the tree using indexes instead of values.

#### How is works ?
Every node know the number of children in the left and the right side. This enable to know the which side to choose for a given index.

## Usage

### Run
Expand Down Expand Up @@ -38,8 +61,6 @@ Where n is the book depth and price is in $.
sbt test
```

## Complexity
The complexity of the algorithm itself is `O(|updateOrderBookCommands| + book_depth)`. The parser part could probably be improved to be quicker.


## Example
Expand All @@ -54,13 +75,10 @@ U B 1 5 40
```

```
sbt "run lot-of-updates.txt 10.0 100"
sbt "run lot-of-updates.txt 10.0 2"
```
- Output
```
50.0,40,60.0,10
40.0,40,70.0,20
```
- Visually

![Visual example](./images/example.png)
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ import scala.util.Try

class OrderBookComputerImplementation extends OrderBookComputer {

// O(|updateOrderBookCommands| + book_depth)
def compute(updateOrderBookCommands: Iterator[UpdateOrderBookCommand], tickSize: TickSize, bookDepth: Int): Try[List[OrderBookProjection]] =
updateOrderBookCommands
.filter(_.priceLevelIndex <= bookDepth)
Expand Down
6 changes: 3 additions & 3 deletions src/main/scala/order/book/domain/OrderBookSide.scala
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,11 @@ package order.book.domain

import order.book.domain.commands.OrderBookInstruction
import order.book.domain.commands.OrderBookInstruction.{Delete, New, Update}
import order.book.domain.datastructure.BinaryTree
import order.book.domain.datastructure.AVLIndexedTree

import scala.util.{Success, Try}

class OrderBookSide private[OrderBookSide] (orders: BinaryTree[OrderBookOrder]) {
class OrderBookSide private[OrderBookSide] (orders: AVLIndexedTree[OrderBookOrder]) {

def applyOrderChange(instruction: OrderBookInstruction,
priceLevelIndex: Int,
Expand All @@ -25,5 +25,5 @@ class OrderBookSide private[OrderBookSide] (orders: BinaryTree[OrderBookOrder])
}

object OrderBookSide {
def empty: OrderBookSide = new OrderBookSide(BinaryTree.empty)
def empty: OrderBookSide = new OrderBookSide(AVLIndexedTree.empty)
}
Original file line number Diff line number Diff line change
@@ -1,54 +1,53 @@
package order.book.domain.datastructure


sealed trait BinaryTree[+T] {
sealed trait AVLIndexedTree[+T] {
val depth: Int
val level: Int
val balanceScore: Int
def insert[U >: T](index: Int, elementToInsert: U): BinaryTree[U]
def delete[U >: T](index: Int): BinaryTree[U]
def updated[U >: T](index: Int, elementToInsert: U): BinaryTree[U]
def balance: BinaryTree[T]
def insert[U >: T](index: Int, elementToInsert: U): AVLIndexedTree[U]
def delete[U >: T](index: Int): AVLIndexedTree[U]
def updated[U >: T](index: Int, elementToInsert: U): AVLIndexedTree[U]
def balance: AVLIndexedTree[T]
def toList: List[T]
}

object BinaryTree {
def empty[T]: BinaryTree[T] = EmptyTree
object AVLIndexedTree {
def empty[T]: AVLIndexedTree[T] = EmptyTree
}


case object EmptyTree extends BinaryTree[Nothing] {
case object EmptyTree extends AVLIndexedTree[Nothing] {
val depth: Int = 0
val level: Int = 0
val balanceScore: Int = 0

def insert[T](index: Int, elementToInsert: T): BinaryTree[T] =
def insert[T](index: Int, elementToInsert: T): AVLIndexedTree[T] =
Node(EmptyTree, 0, elementToInsert, 0, EmptyTree)

def delete[T](index: Int): BinaryTree[T] = EmptyTree
def delete[T](index: Int): AVLIndexedTree[T] = EmptyTree

def updated[T](index: Int, elementToInsert: T): BinaryTree[T] = EmptyTree
def updated[T](index: Int, elementToInsert: T): AVLIndexedTree[T] = EmptyTree

override def toList: List[Nothing] = List.empty

override def balance: BinaryTree[Nothing] = this
override def balance: AVLIndexedTree[Nothing] = this
}

case class Node[T](left: BinaryTree[T], leftNumberOfElement: Int,
case class Node[T](left: AVLIndexedTree[T], leftNumberOfElement: Int,
element: T,
rightNumberOfElement: Int, right: BinaryTree[T]) extends BinaryTree[T] {
rightNumberOfElement: Int, right: AVLIndexedTree[T]) extends AVLIndexedTree[T] {


private def isCurrentNode(index: Int): Boolean = leftNumberOfElement == index
def isCurrentNode(index: Int): Boolean = leftNumberOfElement == index
def isInTheLeftSide(index: Int): Boolean = index < leftNumberOfElement
def isInTheRightSide(index: Int): Boolean = index > leftNumberOfElement

def computeLeftIndex(index: Int): Int = index
def computeRightIndex(index: Int): Int = index - leftNumberOfElement - 1

override def balance: BinaryTree[T] = {
override def balance: AVLIndexedTree[T] = {

def leftRotation(tree: BinaryTree[T]): BinaryTree[T] = this match {
def leftRotation(tree: AVLIndexedTree[T]): AVLIndexedTree[T] = this match {
case Node(_, _, _, _, Node(rightLeft, rightLeftNumberOfElement, rightElement, rightRightNumberOfElement, rightRight)) =>
Node(
Node(
Expand All @@ -63,9 +62,10 @@ case class Node[T](left: BinaryTree[T], leftNumberOfElement: Int,
rightRightNumberOfElement,
rightRight
)
case _ => this
}

def rightLeftRotation(tree: BinaryTree[T]): BinaryTree[T] = this match {
def rightLeftRotation(tree: AVLIndexedTree[T]): AVLIndexedTree[T] = this match {
case Node(_, _, _, _,
Node(
Node(
Expand Down Expand Up @@ -100,9 +100,10 @@ case class Node[T](left: BinaryTree[T], leftNumberOfElement: Int,
rightRight
)
)
case _ => this
}

def rightRotation(tree: BinaryTree[T]): BinaryTree[T] = this match {
def rightRotation(tree: AVLIndexedTree[T]): AVLIndexedTree[T] = this match {
case Node(Node(leftLeft, leftLeftNumberOfElement, leftElement, leftRightNumberOfElement, leftRight), _, _, _, _) =>
Node(
leftLeft,
Expand All @@ -117,9 +118,10 @@ case class Node[T](left: BinaryTree[T], leftNumberOfElement: Int,
right
)
)
case _ => this
}

def leftRightRotation(tree: BinaryTree[T]): BinaryTree[T] = this match {
def leftRightRotation(tree: AVLIndexedTree[T]): AVLIndexedTree[T] = this match {
case Node(
Node(
leftLeft,
Expand Down Expand Up @@ -153,6 +155,7 @@ case class Node[T](left: BinaryTree[T], leftNumberOfElement: Int,
right
)
)
case _ => this
}

if(balanceScore == -2){
Expand All @@ -166,7 +169,7 @@ case class Node[T](left: BinaryTree[T], leftNumberOfElement: Int,
}
}

override def insert[U >: T](index: Int, elementToInsert: U): BinaryTree[U] =
override def insert[U >: T](index: Int, elementToInsert: U): AVLIndexedTree[U] =
if(isInTheLeftSide(index) || isCurrentNode(index)){
copy(
left = left.insert(computeLeftIndex(index), elementToInsert).balance,
Expand All @@ -179,14 +182,14 @@ case class Node[T](left: BinaryTree[T], leftNumberOfElement: Int,
).balance
}

def deleteMin: (T, BinaryTree[T]) = this match {
def deleteMin: (T, AVLIndexedTree[T]) = this match {
case Node(EmptyTree, _, _, _, _) => (element, EmptyTree)
case Node(leftNode @ Node(_, _, _, _, _), _, _, _, _) => leftNode.deleteMin match {
case (minElement, leftTree) => (minElement, copy(left = leftTree, leftNumberOfElement = leftNumberOfElement - 1))
}
}

override def delete[U >: T](index: Int): BinaryTree[U] = this match {
override def delete[U >: T](index: Int): AVLIndexedTree[U] = this match {
case _ if isInTheLeftSide(index) =>
copy(
left = left.delete(computeLeftIndex(index)).balance,
Expand Down Expand Up @@ -214,7 +217,7 @@ case class Node[T](left: BinaryTree[T], leftNumberOfElement: Int,



override def updated[U >: T](index: Int, elementToInsert: U): BinaryTree[U] =
override def updated[U >: T](index: Int, elementToInsert: U): AVLIndexedTree[U] =
if(isCurrentNode(index)) copy(element = elementToInsert)
else if (isInTheLeftSide(index)) copy(left = left.updated(computeLeftIndex(index), elementToInsert))
else copy(right = right.updated(computeRightIndex(index), elementToInsert))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,18 +3,7 @@ package order.book.domain.datastructure
import org.scalatest.{Matchers, WordSpec}


class BinaryTreeSpec extends WordSpec with Matchers {
//
// val tree: BinaryTree[Char] =
// EmptyTree
// .insert(1, '1')
// .insert(2, '2')
// .insert(3, '3')
// .insert(4, '4')
// .insert(5, '5')
// .insert(6, '6')


class AVLIndexedTreeSpec extends WordSpec with Matchers {

"It" should {
"be possible to know if a given index is in the left or the right side of the tree" in {
Expand Down Expand Up @@ -252,51 +241,5 @@ class BinaryTreeSpec extends WordSpec with Matchers {
}
}

// "The minimum element of the tree" should {
// "is the element of the node if no children" in {
// EmptyTree.insert(0, 5).deleteMin should be (5, EmptyTree)
// }
//
// "is the element at the left of the tree" in {
// Node(Node(Node(1), 2, Node(3)), 4, EmptyTree).deleteMin should be (
// 1,
// Node(Node(EmptyTree, 2, Node(3)), 4, EmptyTree)
// )
// }
// }



//
// "An value" should {
// "be addable to an empty tree" in {
// EmptyTree.insert(1) should be (Node(1))
// }
// "be addable in the left side of the tree" in {
// tree.insert(-1) should be (Node(
// Node(Node(-1), 1, EmptyTree),
// 2,
// Node(Node(3), 4, Node(EmptyTree, 5, Node(6)))
// ))
// }
// "be addable in the right side of the tree" in {
// tree.insert(7) should be (Node(
// Node(1),
// 2,
// Node(Node(3), 4, Node(EmptyTree, 5, Node(EmptyTree, 6, Node(7))))
// ))
// }
// "be addable in the middle of the tree" in {
// Node(
// Node(1),
// 2,
// Node(Node(4), 5, Node(EmptyTree, 6, Node(7)))
// ).insert(3) should be (Node(
// Node(1),
// 2,
// Node(Node(Node(3), 4, EmptyTree), 5, Node(EmptyTree, 6, Node(7)))
// ))
// }
// }

}

0 comments on commit 0349d77

Please sign in to comment.