diff options
author | David Terei <davidterei@gmail.com> | 2011-07-20 11:09:03 -0700 |
---|---|---|
committer | David Terei <davidterei@gmail.com> | 2011-07-20 11:26:35 -0700 |
commit | 16514f272fb42af6e9c7674a9bd6c9dce369231f (patch) | |
tree | e4f332b45fe65e2a7a2451be5674f887b42bf199 /testsuite/tests/arrows/should_run | |
parent | ebd422aed41048476aa61dd4c520d43becd78682 (diff) | |
download | haskell-16514f272fb42af6e9c7674a9bd6c9dce369231f.tar.gz |
Move tests from tests/ghc-regress/* to just tests/*
Diffstat (limited to 'testsuite/tests/arrows/should_run')
-rw-r--r-- | testsuite/tests/arrows/should_run/Makefile | 3 | ||||
-rw-r--r-- | testsuite/tests/arrows/should_run/T3822.hs | 17 | ||||
-rw-r--r-- | testsuite/tests/arrows/should_run/T3822.stdout | 2 | ||||
-rw-r--r-- | testsuite/tests/arrows/should_run/all.T | 8 | ||||
-rw-r--r-- | testsuite/tests/arrows/should_run/arrowrun001.hs | 48 | ||||
-rw-r--r-- | testsuite/tests/arrows/should_run/arrowrun001.stdout | 2 | ||||
-rw-r--r-- | testsuite/tests/arrows/should_run/arrowrun002.hs | 225 | ||||
-rw-r--r-- | testsuite/tests/arrows/should_run/arrowrun002.stdout | 4 | ||||
-rw-r--r-- | testsuite/tests/arrows/should_run/arrowrun003.hs | 133 | ||||
-rw-r--r-- | testsuite/tests/arrows/should_run/arrowrun003.stdout | 6 | ||||
-rw-r--r-- | testsuite/tests/arrows/should_run/arrowrun004.hs | 128 | ||||
-rw-r--r-- | testsuite/tests/arrows/should_run/arrowrun004.stdout | 2 |
12 files changed, 578 insertions, 0 deletions
diff --git a/testsuite/tests/arrows/should_run/Makefile b/testsuite/tests/arrows/should_run/Makefile new file mode 100644 index 0000000000..9101fbd40a --- /dev/null +++ b/testsuite/tests/arrows/should_run/Makefile @@ -0,0 +1,3 @@ +TOP=../../.. +include $(TOP)/mk/boilerplate.mk +include $(TOP)/mk/test.mk diff --git a/testsuite/tests/arrows/should_run/T3822.hs b/testsuite/tests/arrows/should_run/T3822.hs new file mode 100644 index 0000000000..93e6dc5797 --- /dev/null +++ b/testsuite/tests/arrows/should_run/T3822.hs @@ -0,0 +1,17 @@ + +{-# LANGUAGE Arrows #-} + +import Control.Arrow +import qualified Control.Category as Cat + +test :: Int -> Int +test = proc x -> do + let neg = x < 0 + case x of + x | neg -> returnA -< 0 -- GHC panics + --x | x < 0 -> returnA -< 0 -- GHC doesn't panic + _ -> returnA -< 10 + +main = do + print $ test (-1) + print $ test 1 diff --git a/testsuite/tests/arrows/should_run/T3822.stdout b/testsuite/tests/arrows/should_run/T3822.stdout new file mode 100644 index 0000000000..25e7f55667 --- /dev/null +++ b/testsuite/tests/arrows/should_run/T3822.stdout @@ -0,0 +1,2 @@ +0 +10 diff --git a/testsuite/tests/arrows/should_run/all.T b/testsuite/tests/arrows/should_run/all.T new file mode 100644 index 0000000000..a9867dd395 --- /dev/null +++ b/testsuite/tests/arrows/should_run/all.T @@ -0,0 +1,8 @@ +setTestOpts(only_compiler_types(['ghc'])) + +test('arrowrun001', normal, compile_and_run, ['']) +test('arrowrun002', skip_if_fast, compile_and_run, ['']) +test('arrowrun003', normal, compile_and_run, ['']) +test('arrowrun004', skip_if_fast, compile_and_run, ['']) +test('T3822', normal, compile_and_run, ['']) + diff --git a/testsuite/tests/arrows/should_run/arrowrun001.hs b/testsuite/tests/arrows/should_run/arrowrun001.hs new file mode 100644 index 0000000000..c686b32546 --- /dev/null +++ b/testsuite/tests/arrows/should_run/arrowrun001.hs @@ -0,0 +1,48 @@ +{-# LANGUAGE Arrows #-} + +-- Toy lambda-calculus interpreter from John Hughes's arrows paper (s5) + +module Main(main) where + +import Data.Maybe(fromJust) +import Control.Arrow + +type Id = String +data Val a = Num Int | Bl Bool | Fun (a (Val a) (Val a)) +data Exp = Var Id | Add Exp Exp | If Exp Exp Exp | Lam Id Exp | App Exp Exp + +eval :: (ArrowChoice a, ArrowApply a) => Exp -> a [(Id, Val a)] (Val a) +eval (Var s) = proc env -> + returnA -< fromJust (lookup s env) +eval (Add e1 e2) = proc env -> do + ~(Num u) <- eval e1 -< env + ~(Num v) <- eval e2 -< env + returnA -< Num (u + v) +eval (If e1 e2 e3) = proc env -> do + ~(Bl b) <- eval e1 -< env + if b then eval e2 -< env + else eval e3 -< env +eval (Lam x e) = proc env -> + returnA -< Fun (proc v -> eval e -< (x,v):env) +eval (App e1 e2) = proc env -> do + ~(Fun f) <- eval e1 -< env + v <- eval e2 -< env + f -<< v + +-- some tests + +i = Lam "x" (Var "x") +k = Lam "x" (Lam "y" (Var "x")) +double = Lam "x" (Add (Var "x") (Var "x")) + +-- if b then k (double x) x else x + x + x + +text_exp = If (Var "b") + (App (App k (App double (Var "x"))) (Var "x")) + (Add (Var "x") (Add (Var "x") (Var "x"))) + +unNum (Num n) = n + +main = do + print (unNum (eval text_exp [("b", Bl True), ("x", Num 5)])) + print (unNum (eval text_exp [("b", Bl False), ("x", Num 5)])) diff --git a/testsuite/tests/arrows/should_run/arrowrun001.stdout b/testsuite/tests/arrows/should_run/arrowrun001.stdout new file mode 100644 index 0000000000..349103a876 --- /dev/null +++ b/testsuite/tests/arrows/should_run/arrowrun001.stdout @@ -0,0 +1,2 @@ +10 +15 diff --git a/testsuite/tests/arrows/should_run/arrowrun002.hs b/testsuite/tests/arrows/should_run/arrowrun002.hs new file mode 100644 index 0000000000..16f29806ac --- /dev/null +++ b/testsuite/tests/arrows/should_run/arrowrun002.hs @@ -0,0 +1,225 @@ +{-# LANGUAGE Arrows #-} + +-- Homogeneous (or depth-preserving) functions over perfectly balanced trees. + +module Main where + +import Control.Arrow +import Control.Category +import Data.Complex +import Prelude hiding (id, (.)) + +infixr 4 :&: + +-- Consider the following non-regular type of perfectly balanced trees, +-- or `powertrees' (cf Jayadev Misra's powerlists): + +data Pow a = Zero a | Succ (Pow (Pair a)) + deriving Show + +type Pair a = (a, a) + +-- Here are some example elements: + +tree0 = Zero 1 +tree1 = Succ (Zero (1, 2)) +tree2 = Succ (Succ (Zero ((1, 2), (3, 4)))) +tree3 = Succ (Succ (Succ (Zero (((1, 2), (3, 4)), ((5, 6), (7, 8)))))) + +-- The elements of this type have a string of constructors expressing +-- a depth n as a Peano numeral, enclosing a nested pair tree of 2^n +-- elements. The type definition ensures that all elements of this type +-- are perfectly balanced binary trees of this form. (Such things arise +-- in circuit design, eg Ruby, and descriptions of parallel algorithms.) +-- And the type system will ensure that all legal programs preserve +-- this structural invariant. +-- +-- The only problem is that the type constraint is too restrictive, rejecting +-- many of the standard operations on these trees. Typically you want to +-- split a tree into two subtrees, do some processing on the subtrees and +-- combine the results. But the type system cannot discover that the two +-- results are of the same depth (and thus combinable). We need a type +-- that says a function preserves depth. Here it is: + +data Hom a b = (a -> b) :&: Hom (Pair a) (Pair b) + +-- A homogeneous (or depth-preserving) function is an infinite sequence of +-- functions of type Pair^n a -> Pair^n b, one for each depth n. We can +-- apply a homogeneous function to a powertree by selecting the function +-- for the required depth: + +apply :: Hom a b -> Pow a -> Pow b +apply (f :&: fs) (Zero x) = Zero (f x) +apply (f :&: fs) (Succ t) = Succ (apply fs t) + +-- Having defined apply, we can forget about powertrees and do all our +-- programming with Hom's. Firstly, Hom is an arrow: + +instance Category Hom where + id = id :&: id + (f :&: fs) . (g :&: gs) = (f . g) :&: (fs . gs) + +instance Arrow Hom where + arr f = f :&: arr (f *** f) + first (f :&: fs) = + first f :&: (arr transpose >>> first fs >>> arr transpose) + +transpose :: ((a,b), (c,d)) -> ((a,c), (b,d)) +transpose ((a,b), (c,d)) = ((a,c), (b,d)) + +-- arr maps f over the leaves of a powertree. + +-- The composition >>> composes sequences of functions pairwise. +-- +-- The *** operator unriffles a powertree of pairs into a pair of powertrees, +-- applies the appropriate function to each and riffles the results. +-- It defines a categorical product for this arrow category. + +-- When describing algorithms, one often provides a pure function for the +-- base case (trees of one element) and a (usually recursive) expression +-- for trees of pairs. + +-- For example, a common divide-and-conquer pattern is the butterfly, where +-- one recursive call processes the odd-numbered elements and the other +-- processes the even ones (cf Geraint Jones and Mary Sheeran's Ruby papers): + +butterfly :: (Pair a -> Pair a) -> Hom a a +butterfly f = id :&: proc (x, y) -> do + x' <- butterfly f -< x + y' <- butterfly f -< y + returnA -< f (x', y') + +-- The recursive calls operate on halves of the original tree, so the +-- recursion is well-defined. + +-- Some examples of butterflies: + +rev :: Hom a a +rev = butterfly swap + where swap (x, y) = (y, x) + +unriffle :: Hom (Pair a) (Pair a) +unriffle = butterfly transpose + +-- Batcher's sorter for bitonic sequences: + +bisort :: Ord a => Hom a a +bisort = butterfly cmp + where cmp (x, y) = (min x y, max x y) + +-- This can be used (with rev) as the merge phase of a merge sort. +-- +sort :: Ord a => Hom a a +sort = id :&: proc (x, y) -> do + x' <- sort -< x + y' <- sort -< y + yr <- rev -< y' + p <- unriffle -< (x', yr) + bisort2 -< p + where _ :&: bisort2 = bisort + +-- Here is the scan operation, using the algorithm of Ladner and Fischer: + +scan :: (a -> a -> a) -> a -> Hom a a +scan op b = id :&: proc (x, y) -> do + y' <- scan op b -< op x y + l <- rsh b -< y' + returnA -< (op l x, y') + +-- The auxiliary function rsh b shifts each element in the tree one place to +-- the right, placing b in the now-vacant leftmost position, and discarding +-- the old rightmost element: + +rsh :: a -> Hom a a +rsh b = const b :&: proc (x, y) -> do + w <- rsh b -< y + returnA -< (w, x) + +-- Finally, here is the Fast Fourier Transform: + +type C = Complex Double + +fft :: Hom C C +fft = id :&: proc (x, y) -> do + x' <- fft -< x + y' <- fft -< y + r <- roots (-1) -< () + let z = r*y' + unriffle -< (x' + z, x' - z) + +-- The auxiliary function roots r (where r is typically a root of unity) +-- populates a tree of size n (necessarily a power of 2) with the values +-- 1, w, w^2, ..., w^(n-1), where w^n = r. + +roots :: C -> Hom () C +roots r = const 1 :&: proc _ -> do + x <- roots r' -< () + unriffle -< (x, x*r') + where r' = if imagPart s >= 0 then -s else s + s = sqrt r + +-- Miscellaneous functions: + +rrot :: Hom a a +rrot = id :&: proc (x, y) -> do + w <- rrot -< y + returnA -< (w, x) + +ilv :: Hom a a -> Hom (Pair a) (Pair a) +ilv f = proc (x, y) -> do + x' <- f -< x + y' <- f -< y + returnA -< (x', y') + +scan' :: (a -> a -> a) -> a -> Hom a a +scan' op b = proc x -> do + l <- rsh b -< x + (id :&: ilv (scan' op b)) -< op l x + +riffle :: Hom (Pair a) (Pair a) +riffle = id :&: proc ((x1, y1), (x2, y2)) -> do + x <- riffle -< (x1, x2) + y <- riffle -< (y1, y2) + returnA -< (x, y) + +invert :: Hom a a +invert = id :&: proc (x, y) -> do + x' <- invert -< x + y' <- invert -< y + unriffle -< (x', y') + +carryLookaheadAdder :: Hom (Bool, Bool) Bool +carryLookaheadAdder = proc (x, y) -> do + carryOut <- rsh (Just False) -< + if x == y then Just x else Nothing + Just carryIn <- scan plusMaybe Nothing -< carryOut + returnA -< x `xor` y `xor` carryIn + where plusMaybe x Nothing = x + plusMaybe x (Just y) = Just y + False `xor` b = b + True `xor` b = not b + +-- Global conditional for SIMD + +ifAll :: Hom a b -> Hom a b -> Hom (a, Bool) b +ifAll fs gs = ifAllAux snd (arr fst >>> fs) (arr fst >>> gs) + where ifAllAux :: (a -> Bool) -> Hom a b -> Hom a b -> Hom a b + ifAllAux p (f :&: fs) (g :&: gs) = + liftIf p f g :&: ifAllAux (liftAnd p) fs gs + liftIf p f g x = if p x then f x else g x + liftAnd p (x, y) = p x && p y + +maybeAll :: Hom a c -> Hom (a, b) c -> Hom (a, Maybe b) c +maybeAll (n :&: ns) (j :&: js) = + choose :&: (arr dist >>> maybeAll ns (arr transpose >>> js)) + where choose (a, Nothing) = n a + choose (a, Just b) = j (a, b) + dist ((a1, b1), (a2, b2)) = ((a1, a2), zipMaybe b1 b2) + zipMaybe (Just x) (Just y) = Just (x, y) + zipMaybe _ _ = Nothing + +main = do + print (apply rev tree3) + print (apply invert tree3) + print (apply (invert >>> sort) tree3) + print (apply (scan (+) 0) tree3) diff --git a/testsuite/tests/arrows/should_run/arrowrun002.stdout b/testsuite/tests/arrows/should_run/arrowrun002.stdout new file mode 100644 index 0000000000..b13ce3b37a --- /dev/null +++ b/testsuite/tests/arrows/should_run/arrowrun002.stdout @@ -0,0 +1,4 @@ +Succ (Succ (Succ (Zero (((8,7),(6,5)),((4,3),(2,1)))))) +Succ (Succ (Succ (Zero (((1,5),(3,7)),((2,6),(4,8)))))) +Succ (Succ (Succ (Zero (((1,2),(3,4)),((5,6),(7,8)))))) +Succ (Succ (Succ (Zero (((1,3),(6,10)),((15,21),(28,36)))))) diff --git a/testsuite/tests/arrows/should_run/arrowrun003.hs b/testsuite/tests/arrows/should_run/arrowrun003.hs new file mode 100644 index 0000000000..5f4580ab87 --- /dev/null +++ b/testsuite/tests/arrows/should_run/arrowrun003.hs @@ -0,0 +1,133 @@ +{-# LANGUAGE Arrows #-} + +module Main(main) where + +import Control.Arrow +import Control.Category +import Prelude hiding (id, (.)) + +class ArrowLoop a => ArrowCircuit a where + delay :: b -> a b b + +-- stream map instance + +data Stream a = Cons a (Stream a) + +instance Functor Stream where + fmap f ~(Cons a as) = Cons (f a) (fmap f as) + +zipStream :: Stream a -> Stream b -> Stream (a,b) +zipStream ~(Cons a as) ~(Cons b bs) = Cons (a,b) (zipStream as bs) + +unzipStream :: Stream (a,b) -> (Stream a, Stream b) +unzipStream abs = (fmap fst abs, fmap snd abs) + +newtype StreamMap a b = StreamMap (Stream a -> Stream b) +unStreamMap (StreamMap f) = f + +instance Category StreamMap where + id = StreamMap id + StreamMap f . StreamMap g = StreamMap (f . g) + +instance Arrow StreamMap where + arr f = StreamMap (fmap f) + first (StreamMap f) = + StreamMap (uncurry zipStream . first f . unzipStream) + +instance ArrowLoop StreamMap where + loop (StreamMap f) = + StreamMap (loop (unzipStream . f . uncurry zipStream)) + +instance ArrowCircuit StreamMap where + delay a = StreamMap (Cons a) + +listToStream :: [a] -> Stream a +listToStream = foldr Cons undefined + +streamToList :: Stream a -> [a] +streamToList (Cons a as) = a:streamToList as + +runStreamMap :: StreamMap a b -> [a] -> [b] +runStreamMap (StreamMap f) as = + take (length as) (streamToList (f (listToStream as))) + +-- simple automaton instance + +data Auto a b = Auto (a -> (b, Auto a b)) + +instance Category Auto where + id = Auto $ \a -> (a, id) + Auto f . Auto g = Auto $ \b -> + let (c, g') = g b + (d, f') = f c + in (d, f' . g') + +instance Arrow Auto where + arr f = Auto $ \a -> (f a, arr f) + first (Auto f) = Auto $ \(b,d) -> let (c,f') = f b in ((c,d), first f') + +instance ArrowLoop Auto where + loop (Auto f) = Auto $ \b -> + let (~(c,d), f') = f (b,d) + in (c, loop f') + +instance ArrowCircuit Auto where + delay a = Auto $ \a' -> (a, delay a') + +runAuto :: Auto a b -> [a] -> [b] +runAuto (Auto f) [] = [] +runAuto (Auto f) (a:as) = let (b, f') = f a in b:runAuto f' as + +-- Some simple example circuits + +-- A resettable counter (first example in several Hawk papers): + +counter :: ArrowCircuit a => a Bool Int +counter = proc reset -> do + rec output <- returnA -< if reset then 0 else next + next <- delay 0 -< output+1 + returnA -< output + +-- Some other basic circuits from the Hawk library. + +-- flush: when reset is True, return d for n ticks, otherwise copy value. +-- (a variation on the resettable counter) + +flush :: ArrowCircuit a => Int -> b -> a (b, Bool) b +flush n d = proc (value, reset) -> do + rec count <- returnA -< if reset then n else max (next-1) 0 + next <- delay 0 -< count + returnA -< if count > 0 then d else value + +-- latch: on each tick, return the last value for which reset was True, +-- or init if there was none. +-- +latch :: ArrowCircuit a => b -> a (b, Bool) b +latch init = proc (value, reset) -> do + rec out <- returnA -< if reset then value else last + last <- delay init -< out + returnA -< out + +-- Some tests using the counter + +test_input = [True, False, True, False, False, True, False, True] +test_input2 = zip [1..] test_input + +-- A test of the resettable counter. + +main = do + print (runStreamMap counter test_input) + print (runAuto counter test_input) + print (runStreamMap (flush 2 0) test_input2) + print (runAuto (flush 2 0) test_input2) + print (runStreamMap (latch 0) test_input2) + print (runAuto (latch 0) test_input2) + +-- A step function (cf current in Lustre) + +step :: ArrowCircuit a => b -> a (Either b c) b +step b = proc x -> do + rec last_b <- delay b -< getLeft last_b x + returnA -< last_b + where getLeft _ (Left b) = b + getLeft b (Right _) = b diff --git a/testsuite/tests/arrows/should_run/arrowrun003.stdout b/testsuite/tests/arrows/should_run/arrowrun003.stdout new file mode 100644 index 0000000000..21a7156d60 --- /dev/null +++ b/testsuite/tests/arrows/should_run/arrowrun003.stdout @@ -0,0 +1,6 @@ +[0,1,0,1,2,0,1,0] +[0,1,0,1,2,0,1,0] +[0,0,0,0,5,0,0,0] +[0,0,0,0,5,0,0,0] +[1,1,3,3,3,6,6,8] +[1,1,3,3,3,6,6,8] diff --git a/testsuite/tests/arrows/should_run/arrowrun004.hs b/testsuite/tests/arrows/should_run/arrowrun004.hs new file mode 100644 index 0000000000..c0275065f2 --- /dev/null +++ b/testsuite/tests/arrows/should_run/arrowrun004.hs @@ -0,0 +1,128 @@ +{-# LANGUAGE Arrows, MultiParamTypeClasses, FlexibleInstances #-} + +-- Simple expression parser +-- (uses do-notation and operators) + +module Main(main) where + +import Control.Arrow +import Control.Category +import Data.Char +import Prelude hiding (id, (.)) + +-- Parsers + +class (Eq s, Show s, ArrowPlus a) => ArrowParser s a where + symbol :: s -> a b String + +data Sym s = Sym { token :: s, value :: String } + +-- Simple backtracking instance + +newtype BTParser s a b = BTParser (a -> [Sym s] -> [(b, [Sym s])]) + +instance Category (BTParser s) where + id = BTParser $ \a ss -> [(a, ss)] + BTParser f . BTParser g = BTParser $ \b ss -> + [(d, ss'') | (c, ss') <- g b ss, (d, ss'') <- f c ss'] + +instance Arrow (BTParser s) where + arr f = BTParser $ \a ss -> [(f a, ss)] + first (BTParser f) = BTParser $ \(b,d) ss -> + [((c,d), ss') | (c,ss') <- f b ss] + +instance ArrowZero (BTParser s) where + zeroArrow = BTParser $ \b ss -> [] + +instance ArrowPlus (BTParser s) where + BTParser f <+> BTParser g = BTParser $ \b ss -> f b ss ++ g b ss + +instance (Eq s, Show s) => ArrowParser s (BTParser s) where + symbol s = BTParser $ \b ss -> + case ss of + Sym s' v:ss' | s' == s -> [(v, ss')] + _ -> [] + +runBTParser :: BTParser s () c -> [Sym s] -> c +runBTParser (BTParser parser) syms = + head [c | (c, []) <- parser () syms] + +-- Expressions + +data ESym = LPar | RPar | Plus | Minus | Mult | Div | Number | Unknown + deriving (Show, Eq, Ord) + +type ExprParser = BTParser ESym +type ExprSym = Sym ESym + +-- The grammar + +expr :: ExprParser () Int +expr = proc () -> do + x <- term -< () + expr' -< x + +expr' :: ExprParser Int Int +expr' = proc x -> do + returnA -< x + <+> do + (|(symbol Plus)|) + y <- term -< () + expr' -< x + y + <+> do + (|(symbol Minus)|) + y <- term -< () + expr' -< x - y + +term :: ExprParser () Int +term = proc () -> do + x <- factor -< () + term' -< x + +term' :: ExprParser Int Int +term' = proc x -> do + returnA -< x + <+> do + (|(symbol Mult)|) + y <- factor -< () + term' -< x * y + <+> do + (|(symbol Div)|) + y <- factor -< () + term' -< x `div` y + +factor :: ExprParser () Int +factor = proc () -> do + v <- (|(symbol Number)|) + returnA -< read v::Int + <+> do + (|(symbol Minus)|) + v <- factor -< () + returnA -< -v + <+> do + (|(symbol LPar)|) + v <- expr -< () + (|(symbol RPar)|) + returnA -< v + +-- Lexical analysis + +lexer :: String -> [ExprSym] +lexer [] = [] +lexer ('(':cs) = Sym LPar "(":lexer cs +lexer (')':cs) = Sym RPar ")":lexer cs +lexer ('+':cs) = Sym Plus "+":lexer cs +lexer ('-':cs) = Sym Minus "-":lexer cs +lexer ('*':cs) = Sym Mult "*":lexer cs +lexer ('/':cs) = Sym Div "/":lexer cs +lexer (c:cs) + | isSpace c = lexer cs + | isDigit c = Sym Number (c:w):lexer cs' + | otherwise = Sym Unknown [c]:lexer cs + where (w,cs') = span isDigit cs + +parse = runBTParser expr . lexer + +main = do + print (parse "1+2*(3+4)") + print (parse "3*5-17/3+4") diff --git a/testsuite/tests/arrows/should_run/arrowrun004.stdout b/testsuite/tests/arrows/should_run/arrowrun004.stdout new file mode 100644 index 0000000000..cbb71fdd82 --- /dev/null +++ b/testsuite/tests/arrows/should_run/arrowrun004.stdout @@ -0,0 +1,2 @@ +15 +14 |