diff --git a/src/main/res/raw/collections.forth b/src/main/res/raw/collections.forth index d781bde..40032bc 100644 --- a/src/main/res/raw/collections.forth +++ b/src/main/res/raw/collections.forth @@ -85,6 +85,7 @@ : remove ( l o -- l ) swap :remove/O jvm-call-method ; : keys ( d -- l ) :keys jvm-call-method ; : values ( d -- l ) :values jvm-call-method ; +: sum ( c -- n ) 0 swap { + } each ; : ... ( lower upper step -- lst ) :com.vectron.fcl.types.Range/create/NNN jvm-call-static ; : .. ( lower upper -- lst ) 1 ... ; diff --git a/src/main/res/raw/core.forth b/src/main/res/raw/core.forth index d018bc3..de7b0e0 100644 --- a/src/main/res/raw/core.forth +++ b/src/main/res/raw/core.forth @@ -24,6 +24,7 @@ : else immediate ['] jmp , (dummy) swap resolve ; : then immediate resolve ; : begin immediate here ; +: again immediate ['] jmp , (offset) ; : while immediate ['] jmp#f , (dummy) ; : repeat immediate swap ['] jmp , (offset) resolve ; : until immediate ['] jmp#f , (offset) ; diff --git a/src/main/res/raw/misc.forth b/src/main/res/raw/misc.forth index 45cd330..c5fcda6 100644 --- a/src/main/res/raw/misc.forth +++ b/src/main/res/raw/misc.forth @@ -17,4 +17,34 @@ : tone ( hz ms -- ) swap :com.vectron.forthcalc.support.Tone/play/di jvm-call-static ; : torch ( n -- ) :com.vectron.forthcalc.support.Torch/toggle/O jvm-call-static ; -: match: immediate ` lastword set-predicate ; \ No newline at end of file +: match: immediate ` lastword set-predicate ; + +: npv ( cashflow rate -- n ) + -> rate 0 => year + { rate year @ dis year inc } map sum ; + +: npv* ( .. rate -- n ) >r list* r> npv ; + +: npv/npv' ( cashflow rate -- npv/npv' ) + 1+ -> rate 0 => n + 0 0 rot { + dup ( each ) n @ neg * rate n @ 1+ neg pow * ( npv' ) rot + + swap ( each ) rate n @ pow / ( npv ) rot + + swap + n inc + } each / ; + +var: irr-guess 0 irr-guess ! + +: irr ( cashflow -- n/nil ) + -> cashflow irr-guess @ 100 / => guess + 1000 0 do + guess @ cashflow guess @ npv/npv' - ( new guess ) + dup guess @ - abs 0.01 < if + 100 * unloop exit + then + guess ! + loop + nil ; + +: irr* ( cashflow -- n ) list* irr ; \ No newline at end of file diff --git a/src/main/res/raw/ops.forth b/src/main/res/raw/ops.forth index 09c9830..5ca91cb 100644 --- a/src/main/res/raw/ops.forth +++ b/src/main/res/raw/ops.forth @@ -18,7 +18,7 @@ ( years ) 1 - repeat drop ( years ) r> r> 2drop ; -: dis ( b i n -- t ) swap 100 / neg 1+ swap pow * ; +: dis ( b i n -- t ) swap 100 / 1+ swap pow / ; : tip1 ( n -- n ) 15 percent ; : tip2 ( bill split -- total tip ) / dup 115 percent swap 15 percent ; ( trigonometry ) diff --git a/src/test/java/com/vectron/fcl/FclTest.java b/src/test/java/com/vectron/fcl/FclTest.java index 734d7ef..26a86b1 100644 --- a/src/test/java/com/vectron/fcl/FclTest.java +++ b/src/test/java/com/vectron/fcl/FclTest.java @@ -67,6 +67,7 @@ public class FclTest { @After public void tearDown() throws Exception { + System.out.println("Transcript: " + transcript.content()); assertEquals(0, fcl.stackSize()); assertEquals(0, fcl.rStackSize()); assertEquals(0, evalPop("psp @").longValue()); @@ -210,12 +211,19 @@ public class FclTest { @Test public void testFinance() { assertEquals(1216.65, evalPop("1000 4 5 cin1").doubleValue(), 0.01); - assertEquals(815.37, evalPop("1000 4 5 dis").doubleValue(), 0.01); + assertEquals(821.93, evalPop("1000 4 5 dis").doubleValue(), 0.01); + assertEquals(558.39, evalPop("1000 6 10 dis").doubleValue(), 0.01); evalDoubles("1000.0 4 5 100 cin2", asList(1000.0, 2240.0, 3529.6, 4870.784, 6265.61536, 7716.2399744)); assertEquals(15, evalPop("100 tip1").doubleValue(), 0.01); evalDoubles("100 1 tip2", asList(115.0, 15.0)); evalDoubles("100 3 tip2", asList(38.33, 5)); evalDoubles("3421 5 tip2", asList(786.83, 102.63)); + assertEquals(-371.68, evalPop("[ -1000 50 100 150 200 250 ] 5 npv").doubleValue(), 0.01); + assertEquals(-371.68, evalPop("-1000 50 100 150 200 250 5 npv*").doubleValue(), 0.01); + assertEquals(-250, evalPop("[ -1000 50 100 150 200 250 ] 0 npv").doubleValue(), 0.01); + assertEquals(12.006, evalPop("[ -500 50 100 150 200 250 ] irr").doubleValue(), 0.01); + assertEquals(-7.431, evalPop("[ -1000 50 100 150 200 250 ] irr").doubleValue(), 0.01); + assertEquals(-28.482, evalPop("-10 irr-guess ! -5000 200 230 400 202 450 irr*").doubleValue(), 0.01); } private void evalDoubles(String script, List expected) { @@ -809,6 +817,8 @@ public class FclTest { assertEquals(true, evalPop("m 2 at").boolValue()); assertEquals(1, evalPop("m 2 index-of").intValue()); assertEquals(0, evalPop("m clear m size").intValue()); + assertEquals(6, evalPop("[ 4 -1 3 ] sum").intValue()); + assertEquals(1+2+3+4+5, evalPop("1 5 .. sum").intValue()); } @Test