Optimizing toSeq inside of BinaryTree

This commit is contained in:
Chris Stewart 2016-02-05 14:16:09 -06:00
parent 372ec28a16
commit a03d40f8d8
7 changed files with 87 additions and 8 deletions

View file

@ -11,6 +11,7 @@ trait ScriptToken {
def hex : String
def bytes = ScalacoinUtil.decodeHex(hex)
def bytesSize = bytes.size
def toInt = Integer.parseInt(hex,16)
}
trait ScriptOperation extends ScriptToken {

View file

@ -2,6 +2,7 @@ package org.scalacoin.script.interpreter
import org.scalacoin.protocol.script.{ScriptSignature, ScriptPubKey}
import org.scalacoin.protocol.transaction.Transaction
import org.scalacoin.script.splice.{SpliceInterpreter, OP_SIZE}
import org.scalacoin.script.{ScriptProgramImpl, ScriptProgram}
import org.scalacoin.script.arithmetic.{ArithmeticInterpreter, OP_ADD}
import org.scalacoin.script.bitwise.{OP_EQUAL, BitwiseInterpreter, OP_EQUALVERIFY}
@ -18,7 +19,7 @@ import scala.annotation.tailrec
* Created by chris on 1/6/16.
*/
trait ScriptInterpreter extends CryptoInterpreter with StackInterpreter with ControlOperationsInterpreter
with BitwiseInterpreter with ConstantInterpreter with ArithmeticInterpreter {
with BitwiseInterpreter with ConstantInterpreter with ArithmeticInterpreter with SpliceInterpreter {
private def logger = LoggerFactory.getLogger(this.getClass().toString)
@ -105,6 +106,8 @@ trait ScriptInterpreter extends CryptoInterpreter with StackInterpreter with Con
//reserved operations
case (nop : NOP) :: t => loop(ScriptProgramImpl(program.stack,t,program.transaction,program.altStack))
//splice operations
case OP_SIZE :: t => loop(opSize(program))
//no more script operations to run, True is represented by any representation of non-zero
case Nil => program.stack.head != ScriptFalse
case h :: t => throw new RuntimeException(h + " was unmatched")

View file

@ -0,0 +1,27 @@
package org.scalacoin.script.splice
import org.scalacoin.script.{ScriptOperationFactory, ScriptProgramImpl, ScriptProgram}
import org.scalacoin.script.constant.ScriptConstantImpl
import org.scalacoin.util.ScalacoinUtil
/**
* Created by chris on 2/4/16.
*/
trait SpliceInterpreter {
/**
* Pushes the string length of the top element of the stack (without popping it).
* @param program
* @return
*/
def opSize(program : ScriptProgram) : ScriptProgram = {
require(program.script.headOption.isDefined && program.script.head == OP_SIZE, "Script top must be OP_SIZE")
require(program.stack.size > 0, "Must have at least 1 element on the stack for OP_SIZE")
val stringSize = program.stack.head.bytes.size
val hex = ScalacoinUtil.encodeHex(stringSize.toByte)
val scriptOperation = ScriptOperationFactory.fromHex(hex)
val size = if (scriptOperation.isDefined) scriptOperation.get else ScriptConstantImpl(hex)
ScriptProgramImpl(size :: program.stack, program.script.tail, program.transaction,program.altStack)
}
}

View file

@ -141,12 +141,14 @@ trait BinaryTree[+T] {
def toSeq : Seq[T] = {
//TODO: Optimize this into a tailrec function
//@tailrec
def loop(tree : BinaryTree[T],accum : List[T]) : List[T] = tree match {
case Leaf(x) => x :: accum
case Leaf(x) => accum ++ List(x)
case Empty => accum
case Node(v,l,r) => v :: loop(l,List()) ++ loop(r,List())
case Node(v,l,r) => loop(r,loop(l,accum ++ List(v)))
}
loop(this,List())
}

View file

@ -54,13 +54,13 @@ class ScriptInterpreterTest extends FlatSpec with MustMatchers with ScriptInterp
val source = scala.io.Source.fromFile("src/test/scala/org/scalacoin/script/interpreter/script_valid.json")
//use this to represent a single test case from script_valid.json
/* val lines =
val lines =
"""
|
|[["10 0 11 TOALTSTACK DROP FROMALTSTACK", "ADD 21 EQUAL", "P2SH,STRICTENC"]]
""".stripMargin*/
|[["128", "SIZE 2 EQUAL", "P2SH,STRICTENC"]]
""".stripMargin
val lines = try source.getLines.filterNot(_.isEmpty).map(_.trim) mkString "\n" finally source.close()
//val lines = try source.getLines.filterNot(_.isEmpty).map(_.trim) mkString "\n" finally source.close()
val json = lines.parseJson
val testCasesOpt : Seq[Option[CoreTestCase]] = json.convertTo[Seq[Option[CoreTestCase]]]
val testCases : Seq[CoreTestCase] = testCasesOpt.flatten

View file

@ -0,0 +1,32 @@
package org.scalacoin.script.splice
import org.scalacoin.script.ScriptProgramImpl
import org.scalacoin.script.bitwise.OP_EQUAL
import org.scalacoin.script.constant.{OP_2, ScriptConstantImpl, OP_0}
import org.scalacoin.util.TestUtil
import org.scalatest.{MustMatchers, FlatSpec}
/**
* Created by chris on 2/4/16.
*/
class SpliceInterpreterTest extends FlatSpec with MustMatchers with SpliceInterpreter {
"SpliceInterpreter" must "evaluate an OP_SIZE correctly" in {
val stack = List(OP_0)
val script = List(OP_SIZE)
val program = ScriptProgramImpl(stack,script,TestUtil.transaction,List())
val newProgram = opSize(program)
newProgram.stack must be (List(OP_0,OP_0))
newProgram.script.isEmpty must be (true)
}
it must "evaluate an OP_SIZE correctly with something of size 2 bytes" in {
val stack = List(ScriptConstantImpl("80"))
val script = List(OP_SIZE)
val program = ScriptProgramImpl(stack,script,TestUtil.transaction,List())
val newProgram = opSize(program)
newProgram.stack must be (List(OP_2),ScriptConstantImpl("80"))
newProgram.script.isEmpty must be (true)
}
}

View file

@ -17,16 +17,30 @@ class BinaryTreeTest extends FlatSpec with MustMatchers {
}
it must "convert a binary tree to to a list with node values" in {
//val script = List(OP_IF, OP_IF, OP_1, OP_ELSE, OP_0, OP_ENDIF, OP_ELSE, OP_IF, OP_0, OP_ELSE, OP_1, OP_ENDIF, OP_ENDIF)
val bTree : BinaryTree[ScriptToken] =
Node[ScriptToken](OP_IF,Node(OP_IF,Leaf(OP_1),Node(OP_ELSE,Leaf(OP_0),Leaf(OP_ENDIF))),
Node(OP_ELSE,Node(OP_IF,Leaf(OP_0), Node(OP_ELSE,Leaf(OP_1),Leaf(OP_ENDIF))),Leaf(OP_ENDIF)))
val script = List(OP_IF, OP_IF, OP_1, OP_ELSE, OP_0, OP_ENDIF, OP_ELSE, OP_IF, OP_0, OP_ELSE, OP_1, OP_ENDIF, OP_ENDIF)
bTree.toSeq.size must be (script.size)
bTree.toSeq must be (script)
}
it must "convert a simple binary tree to a sequence" in {
val bTree = Node(1,Leaf(2), Leaf(3))
val seq = bTree.toSeq
seq must be (Seq(1,2,3))
val bTree1 = Node(1,Node(2,Empty,Empty), Node(3,Empty,Empty))
val seq1 = bTree1.toSeq
seq1 must be (List(1,2,3))
val bTree2 = Node(OP_IF,Node(OP_1,Empty,Empty), Node(OP_ELSE,Node(OP_2,Empty,Empty),Leaf(OP_ENDIF)))
val seq2 = bTree2.toSeq
seq2 must be (Seq(OP_IF,OP_1,OP_ELSE, OP_2,OP_ENDIF))
}
it must "find the first occurrence of a element in the tree" in {
val tree = Node[Int](0,Node(1,Leaf(2),Leaf(3)), Node(1,Leaf(4), Leaf(5)))