From 594d180dd1fc5cbdf5803713296fc80d18575aa2 Mon Sep 17 00:00:00 2001 From: microproofs Date: Fri, 25 Sep 2026 11:45:11 -0400 Subject: [PATCH 01/10] feat(base): complete Prop generators Signed-off-by: microproofs --- .sampo/changesets/prop-generators.md | 5 + crates/nash-driver/base/src/Prop.nash | 111 +++++++++++++-- crates/nash-driver/tests/base_traits.rs | 2 + .../fixtures/base-traits/PropHelpers.nash | 131 ++++++++++++++++++ .../snapshots/base_traits__prop_helpers.snap | 32 +++++ crates/nash-driver/tests/testing_base.rs | 9 +- docs/stdlib.md | 47 +++++-- plans/12-stdlib.md | 47 ++++--- 8 files changed, 333 insertions(+), 51 deletions(-) create mode 100644 .sampo/changesets/prop-generators.md create mode 100644 crates/nash-driver/tests/fixtures/base-traits/PropHelpers.nash create mode 100644 crates/nash-driver/tests/snapshots/base_traits__prop_helpers.snap diff --git a/.sampo/changesets/prop-generators.md b/.sampo/changesets/prop-generators.md new file mode 100644 index 00000000..8dad6608 --- /dev/null +++ b/.sampo/changesets/prop-generators.md @@ -0,0 +1,5 @@ +--- +cargo/nash-driver: minor +--- + +Complete Prop generators with booleans, options, bounded and exact bytes, weighted alternatives, bounded filtering, and arbitrary-precision integer generation. Normalize Big/little bounds and weights, preserve strict replay groups, and express tuple2 through generator do. Add source snapshots for range properties, retry boundaries, and reduced counterexamples. diff --git a/crates/nash-driver/base/src/Prop.nash b/crates/nash-driver/base/src/Prop.nash index 4b880eac..4f70736c 100644 --- a/crates/nash-driver/base/src/Prop.nash +++ b/crates/nash-driver/base/src/Prop.nash @@ -1,4 +1,4 @@ -module Prop exposing (type prng(..), type choiceTree(..), type generator, group, choice, constant, intBetween, int, listOf, listBetween, tuple2, oneOf, bytes) +module Prop exposing (type prng(..), type choiceTree(..), type generator, group, choice, constant, intBetween, int, listOf, listBetween, tuple2, oneOf, bytes, bool, option, bytesBetween, bytesExactly, frequency, suchThat, intAtLeast) import Builtin import Functor @@ -99,13 +99,42 @@ intBetweenInt lo hi = if hi < lo then (fail "Prop.intBetween has reversed bounds") else if hi == lo then constant lo else - let draw = choice (hi - lo) in + let draw = bounded (hi - lo) in \state -> case draw state of None -> None Some (n, next) -> Some (lo + n, next) --- Signed 64-bit values, with small-width draws favored. +-- Bounded offsets; large ranges use whole 64-bit chunks and rejection. +bounded : int -> generator int +bounded maximum = + if maximum <= 18446744073709551615 then choice maximum + else do + high <- bounded (maximum / 18446744073709551616) + low <- choice 18446744073709551615 + let value = high * 18446744073709551616 + low in + if value <= maximum then Applicative.pure value else bounded maximum + +-- Stop with probability 3/4; otherwise append a chunk. No fixed size ceiling. +natural : generator int +natural = naturalFrom 1 + +naturalFrom : int -> generator int +naturalFrom place = do + continue <- choice 3 + case continue of + 3 -> do + digit <- choice 18446744073709551615 + rest <- naturalFrom (place * 18446744073709551616) + Applicative.pure (digit * place + rest) + _ -> Applicative.pure 0 + +intAtLeast : Lift int 'n => 'n -> generator int +intAtLeast minimum = + let lo = lower minimum in + Functor.map (\offset -> lo + offset) natural + +-- Favor small values; the wide branch has no fixed integer ceiling. int : generator int int state = case choice 2 state of @@ -114,7 +143,16 @@ int state = case width of 0 -> choice 255 next 1 -> intBetween -32768 32767 next - _ -> intBetween -9223372036854775808 9223372036854775807 next + _ -> signed next + +signed : generator int +signed = do + magnitude <- natural + case magnitude of + 0 -> Applicative.pure 0 + _ -> do + negative <- bool + Applicative.pure (if negative then -magnitude else magnitude) listOf : generator 'a -> generator (list 'a) listOf = listBetween 0 20 @@ -146,13 +184,10 @@ listIteration required item state = do _ -> (Monad.bind item (\value -> Applicative.pure (Some value))) next tuple2 : generator 'a -> generator 'b -> generator ('a, 'b) -tuple2 first second state = - case group first state of - None -> None - Some (a, next) -> - case group second next of - None -> None - Some (b, final) -> Some ((a, b), final) +tuple2 first second = do + a <- first + b <- second + Applicative.pure (a, b) -- Generators contain functions, so use the Term-representation Cons list. oneOf : cons (generator 'a) -> generator 'a @@ -173,7 +208,59 @@ pick index generators = Cons first rest -> if index == 0 then group first else pick (index - 1) rest bytes : generator bytes -bytes = Functor.map packBytes (listBetween 0 32 (choice 255)) +bytes = bytesBetween 0 32 + +bool : generator bool +bool = Functor.map (\n -> n == 1) (choice 1) + +option : generator 'a -> generator (option 'a) +option item = do + present <- bool + if present then Functor.map Some (group item) else Applicative.pure None + +bytesBetween : (Lift int 'l, Lift int 'h) => 'l -> 'h -> generator bytes +bytesBetween lo hi = Functor.map packBytes (listBetween lo hi (choice 255)) + +bytesExactly : Lift int 'n => 'n -> generator bytes +bytesExactly count = + let n = lower count in + bytesBetween n n + +frequency : Lift int 'w => cons ('w, generator 'a) -> generator 'a +frequency entries = + let total = totalWeight entries in + if total == 0 then \_ -> None + else + let draw = bounded (total - 1) in + \state -> do + (index, next) <- draw state + weighted index entries next + +totalWeight : Lift int 'w => cons ('w, generator 'a) -> int +totalWeight entries = case entries of + Nil -> 0 + Cons (weight, _) rest -> + let n = lower weight in + if n < 0 then (fail "Prop.frequency requires nonnegative weights") + else n + totalWeight rest + +weighted : Lift int 'w => int -> cons ('w, generator 'a) -> generator 'a +weighted index entries = case entries of + Nil -> \_ -> None + Cons (weight, item) rest -> + let n = lower weight in + if index < n then group item + else weighted (index - n) rest + +suchThat : ('a -> bool) -> generator 'a -> generator 'a +suchThat predicate item = retry 100 predicate item + +retry : int -> ('a -> bool) -> generator 'a -> generator 'a +retry remaining predicate item state = + if remaining == 0 then None + else do + (value, next) <- group item state + if predicate value then Some (value, next) else retry (remaining - 1) predicate item next packBytes : list int -> bytes packBytes values = diff --git a/crates/nash-driver/tests/base_traits.rs b/crates/nash-driver/tests/base_traits.rs index e5bee8a0..380760a3 100644 --- a/crates/nash-driver/tests/base_traits.rs +++ b/crates/nash-driver/tests/base_traits.rs @@ -122,3 +122,5 @@ base_snapshot!(generate_bounds, "fixtures/base-traits/PropBounds.nash"); base_snapshot!(data_decoders, "fixtures/base-traits/Decoders.nash"); base_snapshot!(map_helpers, "fixtures/base-traits/Maps.nash"); base_snapshot!(representation_classes, "fixtures/base-traits/Classed.nash"); + +base_snapshot!(prop_helpers, "fixtures/base-traits/PropHelpers.nash"); diff --git a/crates/nash-driver/tests/fixtures/base-traits/PropHelpers.nash b/crates/nash-driver/tests/fixtures/base-traits/PropHelpers.nash new file mode 100644 index 00000000..1319c7b8 --- /dev/null +++ b/crates/nash-driver/tests/fixtures/base-traits/PropHelpers.nash @@ -0,0 +1,131 @@ +module Main exposing (..) + +import Cons exposing (type cons(..)) + +replay choices = Prop.Replayed choices Nil +c = Prop.Choice +g = Prop.Group +one x = Cons x Nil +two x y = Cons x (one y) + +value generator state = case generator state of + Some (result, _) -> Some result + None -> None + +seed = Prop.Seeded #"" Nil +weights : cons (int, Prop.generator int) +weights = Cons (0, \_ -> fail) (Cons (3, Prop.constant 10) (Cons (1, Prop.constant 20) Nil)) +emptyWeights : cons (int, Prop.generator int) +emptyWeights = Nil +bigWeight : Int +bigWeight = 2 +bigCount : Int +bigCount = 3 + +attempts n = + if n == 0 then one (g (one (c 1))) + else Cons (g (one (c 0))) (attempts (n - 1)) + +byteDraw n = g (one (g (one (c n)))) + +rebuild choices = Prop.Rebuilding choices Nil +huge : Int +huge = lift (18446744073709551616 * 18446744073709551616) + +hugeLittle : int +hugeLittle = lower huge +never : Prop.generator int +never _ = fail + +tests + test "wide range strict replay isolates rejected attempts" = do + assert (value (Prop.intBetween 0 18446744073709551616) (replay (Cons (g (one (c 1))) (Cons (g (one (c 1))) (two (g (one (c 1))) (g (one (c 0))))))) == Some 18446744073709551616) + test "weight totals can exceed one choice" = do + let options = two (18446744073709551616, Prop.constant 10) (1, Prop.constant 20) in + assert (value (Prop.frequency options) (rebuild [1, 0]) == Some 20) + let options = two (18446744073709551616, Prop.constant 10) (1, Prop.constant 20) in + assert (value (Prop.frequency options) (rebuild [0, 18446744073709551615]) == Some 10) + test "reversed byte lengths fail" fail = do + case Prop.bytesBetween 3 2 of + _ -> () + prop "wide range counterexample replays and reduces" fail once = + let n via Prop.intBetween hugeLittle (hugeLittle * 2) in + do + assert (n < 0) + test "arbitrary integer offsets preserve large lower bounds" = do + assert (value (Prop.intAtLeast huge) (rebuild [0]) == Some (lower huge)) + assert (value (Prop.intAtLeast (-hugeLittle)) (rebuild [3, 0, 3, 1, 0]) == Some (18446744073709551616 - lower huge)) + assert (value Prop.int (rebuild [2, 3, 0, 3, 1, 0, 0]) == Some 18446744073709551616) + assert (value Prop.int (rebuild [2, 3, 0, 3, 1, 0, 1]) == Some (-18446744073709551616)) + test "wide bounded generation rejects overshoot then retries" = do + assert (value (Prop.intBetween 0 18446744073709551616) (rebuild [1, 1, 1, 0]) == Some 18446744073709551616) + assert (value (Prop.intBetween 0 18446744073709551616) (rebuild [1, 1]) == None) + prop "arbitrary integer lower bound" = + let n via Prop.intAtLeast huge in + do + assert (n >= lower huge) + test "boolean choices" = do + assert (value Prop.bool (replay (one (c 0))) == Some False) + assert (value Prop.bool (replay (one (c 1))) == Some True) + test "optional draw preserves absence and groups the payload" = do + assert (value (Prop.option never) (replay (one (g (one (c 0))))) == Some None) + assert (value (Prop.option (Prop.choice 9)) (replay (two (g (one (c 1))) (g (one (c 7))))) == Some (Some 7)) + test "tuple bind preserves groups and remaining siblings" = do + case Prop.tuple2 (Prop.choice 9) (Prop.choice 9) (replay (Cons (g (one (c 4))) (Cons (g (one (c 5))) (one (c 8))))) of + Some ((4, 5), Prop.Replayed rest used) -> + case (rest, used) of + (Cons (Prop.Choice 8) Nil, Cons (Prop.Group (Cons (Prop.Choice 5) Nil)) (Cons (Prop.Group (Cons (Prop.Choice 4) Nil)) Nil)) -> () + _ -> fail + _ -> fail + test "weighted endpoints skip zero weights" = do + assert (value (Prop.frequency weights) (replay (two (c 0) (g Nil))) == Some 10) + assert (value (Prop.frequency weights) (replay (two (c 2) (g Nil))) == Some 10) + assert (value (Prop.frequency weights) (replay (two (c 3) (g Nil))) == Some 20) + assert (value (Prop.frequency (one (bigWeight, Prop.constant 9))) seed == Some 9) + assert (value (Prop.frequency emptyWeights) seed == None) + assert (value (Prop.frequency (one (0, Prop.constant 9))) seed == None) + test "negative weights fail even after a positive entry" fail = do + case Prop.frequency (Cons (1, Prop.constant 1) (one (-1, Prop.constant 2))) of + _ -> () + test "filter retries recorded groups and keeps remaining siblings" = do + case Prop.suchThat (\n -> n > 0) (Prop.choice 9) (replay (Cons (g (one (c 0))) (Cons (g (one (c 4))) (one (c 8))))) of + Some (4, Prop.Replayed (Cons (Prop.Choice 8) Nil) _) -> () + _ -> fail + assert (value (Prop.suchThat (\n -> n > 0) (Prop.choice 9)) (replay (one (g (one (c 0))))) == None) + assert (value (Prop.suchThat (\_ -> False) (Prop.constant 1)) seed == None) + test "filter allows exactly 100 attempts" = do + assert (value (Prop.suchThat (\n -> n == 1) (Prop.choice 1)) (replay (attempts 99)) == Some 1) + assert (value (Prop.suchThat (\n -> n == 1) (Prop.choice 1)) (replay (attempts 100)) == None) + test "filter cannot borrow choices from the next attempt" = do + assert (value (Prop.suchThat (\_ -> True) (Prop.choice 1)) (replay (two (g Nil) (g (one (c 1))))) == None) + test "exact byte replay includes both endpoints" = do + assert (value (Prop.bytesExactly 2) (replay (two (byteDraw 0) (byteDraw 255))) == Some #"00ff") + assert (value (Prop.bytesExactly 2) (replay (one (byteDraw 0))) == None) + test "exact bytes normalize Big counts" = do + case value (Prop.bytesExactly bigCount) seed of + Some bytes -> assert (Bytes.length bytes == 3) + None -> fail + assert (value (Prop.bytesExactly 0) (replay Nil) == Some #"") + test "negative byte count fails" fail = do + case Prop.bytesExactly -1 of + _ -> () + prop "byte length in bounds" = + let bytes via Prop.bytesBetween 2 7 in + do + assert (Bytes.length bytes >= 2 && Bytes.length bytes <= 7) + prop "generated integer bounds" = + let lo via Prop.int + span via Prop.intBetween 0 1000 + in + do + case value (Prop.intBetween lo (lo + span)) seed of + Some n -> assert (n >= lo && n <= lo + span) + None -> fail + prop "listOf reduces to empty" fail once = + let items via Prop.listOf Prop.int in + do + assert (List.length items < 0) + prop "listOf reduces to one zero" fail once = + let items via Prop.listOf Prop.int in + do + assert (List.length items == 0) diff --git a/crates/nash-driver/tests/snapshots/base_traits__prop_helpers.snap b/crates/nash-driver/tests/snapshots/base_traits__prop_helpers.snap new file mode 100644 index 00000000..b22b1f26 --- /dev/null +++ b/crates/nash-driver/tests/snapshots/base_traits__prop_helpers.snap @@ -0,0 +1,32 @@ +--- +source: crates/nash-driver/tests/base_traits.rs +description: "module Main exposing (..)\n\nimport Cons exposing (type cons(..))\n\nreplay choices = Prop.Replayed choices Nil\nc = Prop.Choice\ng = Prop.Group\none x = Cons x Nil\ntwo x y = Cons x (one y)\n\nvalue generator state = case generator state of\n Some (result, _) -> Some result\n None -> None\n\nseed = Prop.Seeded #\"\" Nil\nweights : cons (int, Prop.generator int)\nweights = Cons (0, \\_ -> fail) (Cons (3, Prop.constant 10) (Cons (1, Prop.constant 20) Nil))\nemptyWeights : cons (int, Prop.generator int)\nemptyWeights = Nil\nbigWeight : Int\nbigWeight = 2\nbigCount : Int\nbigCount = 3\n\nattempts n =\n if n == 0 then one (g (one (c 1)))\n else Cons (g (one (c 0))) (attempts (n - 1))\n\nbyteDraw n = g (one (g (one (c n))))\n\nrebuild choices = Prop.Rebuilding choices Nil\nhuge : Int\nhuge = lift (18446744073709551616 * 18446744073709551616)\n\nhugeLittle : int\nhugeLittle = lower huge\nnever : Prop.generator int\nnever _ = fail\n\ntests\n test \"wide range strict replay isolates rejected attempts\" = do\n assert (value (Prop.intBetween 0 18446744073709551616) (replay (Cons (g (one (c 1))) (Cons (g (one (c 1))) (two (g (one (c 1))) (g (one (c 0))))))) == Some 18446744073709551616)\n test \"weight totals can exceed one choice\" = do\n let options = two (18446744073709551616, Prop.constant 10) (1, Prop.constant 20) in\n assert (value (Prop.frequency options) (rebuild [1, 0]) == Some 20)\n let options = two (18446744073709551616, Prop.constant 10) (1, Prop.constant 20) in\n assert (value (Prop.frequency options) (rebuild [0, 18446744073709551615]) == Some 10)\n test \"reversed byte lengths fail\" fail = do\n case Prop.bytesBetween 3 2 of\n _ -> ()\n prop \"wide range counterexample replays and reduces\" fail once =\n let n via Prop.intBetween hugeLittle (hugeLittle * 2) in\n do\n assert (n < 0)\n test \"arbitrary integer offsets preserve large lower bounds\" = do\n assert (value (Prop.intAtLeast huge) (rebuild [0]) == Some (lower huge))\n assert (value (Prop.intAtLeast (-hugeLittle)) (rebuild [3, 0, 3, 1, 0]) == Some (18446744073709551616 - lower huge))\n assert (value Prop.int (rebuild [2, 3, 0, 3, 1, 0, 0]) == Some 18446744073709551616)\n assert (value Prop.int (rebuild [2, 3, 0, 3, 1, 0, 1]) == Some (-18446744073709551616))\n test \"wide bounded generation rejects overshoot then retries\" = do\n assert (value (Prop.intBetween 0 18446744073709551616) (rebuild [1, 1, 1, 0]) == Some 18446744073709551616)\n assert (value (Prop.intBetween 0 18446744073709551616) (rebuild [1, 1]) == None)\n prop \"arbitrary integer lower bound\" =\n let n via Prop.intAtLeast huge in\n do\n assert (n >= lower huge)\n test \"boolean choices\" = do\n assert (value Prop.bool (replay (one (c 0))) == Some False)\n assert (value Prop.bool (replay (one (c 1))) == Some True)\n test \"optional draw preserves absence and groups the payload\" = do\n assert (value (Prop.option never) (replay (one (g (one (c 0))))) == Some None)\n assert (value (Prop.option (Prop.choice 9)) (replay (two (g (one (c 1))) (g (one (c 7))))) == Some (Some 7))\n test \"tuple bind preserves groups and remaining siblings\" = do\n case Prop.tuple2 (Prop.choice 9) (Prop.choice 9) (replay (Cons (g (one (c 4))) (Cons (g (one (c 5))) (one (c 8))))) of\n Some ((4, 5), Prop.Replayed rest used) ->\n case (rest, used) of\n (Cons (Prop.Choice 8) Nil, Cons (Prop.Group (Cons (Prop.Choice 5) Nil)) (Cons (Prop.Group (Cons (Prop.Choice 4) Nil)) Nil)) -> ()\n _ -> fail\n _ -> fail\n test \"weighted endpoints skip zero weights\" = do\n assert (value (Prop.frequency weights) (replay (two (c 0) (g Nil))) == Some 10)\n assert (value (Prop.frequency weights) (replay (two (c 2) (g Nil))) == Some 10)\n assert (value (Prop.frequency weights) (replay (two (c 3) (g Nil))) == Some 20)\n assert (value (Prop.frequency (one (bigWeight, Prop.constant 9))) seed == Some 9)\n assert (value (Prop.frequency emptyWeights) seed == None)\n assert (value (Prop.frequency (one (0, Prop.constant 9))) seed == None)\n test \"negative weights fail even after a positive entry\" fail = do\n case Prop.frequency (Cons (1, Prop.constant 1) (one (-1, Prop.constant 2))) of\n _ -> ()\n test \"filter retries recorded groups and keeps remaining siblings\" = do\n case Prop.suchThat (\\n -> n > 0) (Prop.choice 9) (replay (Cons (g (one (c 0))) (Cons (g (one (c 4))) (one (c 8))))) of\n Some (4, Prop.Replayed (Cons (Prop.Choice 8) Nil) _) -> ()\n _ -> fail\n assert (value (Prop.suchThat (\\n -> n > 0) (Prop.choice 9)) (replay (one (g (one (c 0))))) == None)\n assert (value (Prop.suchThat (\\_ -> False) (Prop.constant 1)) seed == None)\n test \"filter allows exactly 100 attempts\" = do\n assert (value (Prop.suchThat (\\n -> n == 1) (Prop.choice 1)) (replay (attempts 99)) == Some 1)\n assert (value (Prop.suchThat (\\n -> n == 1) (Prop.choice 1)) (replay (attempts 100)) == None)\n test \"filter cannot borrow choices from the next attempt\" = do\n assert (value (Prop.suchThat (\\_ -> True) (Prop.choice 1)) (replay (two (g Nil) (g (one (c 1))))) == None)\n test \"exact byte replay includes both endpoints\" = do\n assert (value (Prop.bytesExactly 2) (replay (two (byteDraw 0) (byteDraw 255))) == Some #\"00ff\")\n assert (value (Prop.bytesExactly 2) (replay (one (byteDraw 0))) == None)\n test \"exact bytes normalize Big counts\" = do\n case value (Prop.bytesExactly bigCount) seed of\n Some bytes -> assert (Bytes.length bytes == 3)\n None -> fail\n assert (value (Prop.bytesExactly 0) (replay Nil) == Some #\"\")\n test \"negative byte count fails\" fail = do\n case Prop.bytesExactly -1 of\n _ -> ()\n prop \"byte length in bounds\" =\n let bytes via Prop.bytesBetween 2 7 in\n do\n assert (Bytes.length bytes >= 2 && Bytes.length bytes <= 7)\n prop \"generated integer bounds\" =\n let lo via Prop.int\n span via Prop.intBetween 0 1000\n in\n do\n case value (Prop.intBetween lo (lo + span)) seed of\n Some n -> assert (n >= lo && n <= lo + span)\n None -> fail\n prop \"listOf reduces to empty\" fail once =\n let items via Prop.listOf Prop.int in\n do\n assert (List.length items < 0)\n prop \"listOf reduces to one zero\" fail once =\n let items via Prop.listOf Prop.int in\n do\n assert (List.length items == 0)\n" +--- +wide range strict replay isolates rejected attempts: Pass; traces: [] +weight totals can exceed one choice: Pass; traces: [] +reversed byte lengths fail: Pass; traces: ["Prop.listBetween has invalid bounds"] +wide range counterexample replays and reduces: Pass; traces: [] +counterexample: [("n", "340282366920938463463374607431768211456")] +replay: Some([Group([Group([Choice(0)]), Group([Choice(0)])]), Group([Choice(0)])]) +arbitrary integer offsets preserve large lower bounds: Pass; traces: [] +wide bounded generation rejects overshoot then retries: Pass; traces: [] +arbitrary integer lower bound: Pass; traces: [] +boolean choices: Pass; traces: [] +optional draw preserves absence and groups the payload: Pass; traces: [] +tuple bind preserves groups and remaining siblings: Pass; traces: [] +weighted endpoints skip zero weights: Pass; traces: [] +negative weights fail even after a positive entry: Pass; traces: ["Prop.frequency requires nonnegative weights"] +filter retries recorded groups and keeps remaining siblings: Pass; traces: [] +filter allows exactly 100 attempts: Pass; traces: [] +filter cannot borrow choices from the next attempt: Pass; traces: [] +exact byte replay includes both endpoints: Pass; traces: [] +exact bytes normalize Big counts: Pass; traces: [] +negative byte count fails: Pass; traces: ["Prop.listBetween has invalid bounds"] +byte length in bounds: Pass; traces: [] +generated integer bounds: Pass; traces: [] +listOf reduces to empty: Pass; traces: [] +counterexample: [("items", "[]")] +replay: Some([Group([Choice(0)])]) +listOf reduces to one zero: Pass; traces: [] +counterexample: [("items", "[0]")] +replay: Some([Group([Choice(1), Group([Choice(0), Choice(0)])]), Group([Choice(0)])]) diff --git a/crates/nash-driver/tests/testing_base.rs b/crates/nash-driver/tests/testing_base.rs index 168100ff..9a73c06d 100644 --- a/crates/nash-driver/tests/testing_base.rs +++ b/crates/nash-driver/tests/testing_base.rs @@ -96,12 +96,9 @@ async fn generation_functions_thread_choices() { "#\"41\"", ), ("Prop.int", "two (c 0) (c 42)", "42"), - ( - "Prop.int", - "two (c 2) (c 18446744073709551615)", - "9223372036854775807", - ), - ("Prop.int", "two (c 2) (c 0)", "(-9223372036854775808)"), + ("Prop.int", "two (c 1) (c 65535)", "32767"), + ("Prop.int", "two (c 1) (c 0)", "(-32768)"), + ("Prop.int", "two (c 2) (g (one (g (one (c 0)))))", "0"), ( "Prop.oneOf (Cons.Cons (Prop.constant 0) (Cons.Cons (Prop.choice 10) Cons.Nil))", "two (c 1) (g (one (c 7)))", diff --git a/docs/stdlib.md b/docs/stdlib.md index 8addd1cc..c0c802eb 100644 --- a/docs/stdlib.md +++ b/docs/stdlib.md @@ -280,7 +280,7 @@ mapping can change element representations within Storable; it cannot produce Te elements. Builtin pair has no Functor impl: `mkPairData` accepts only Big components, not arbitrary Storable components needed by `map`. Pair.fst, Pair.snd and Pair.make remain the specified projection/construction helpers. -Generation functions have no Functor, Applicative or Monad instances. +The Prop.generator alias implements Functor, Applicative and Monad. ### Equality at the Big boundary @@ -941,9 +941,9 @@ constructor shapes above. Map encoding preserves order and duplicates. ## `Prop` -Plan 10 supplies the executable core subset. `oneOf` uses `Cons.cons` because -generators contain functions and cannot inhabit the native Storable-only list. -Further helpers below remain Plan 12 work. See testing.md "Generators": `prng` is little, choices are `int`s, the +Plan 10 supplies the runner protocol; Plan 12 supplies the generator API. +`oneOf` uses `Cons.cons` because generators contain functions and cannot inhabit the native Storable-only list. +See testing.md "Generators": `prng` is little, choices are `int`s, the primitive is `choice`. ```elm @@ -963,20 +963,19 @@ choice : Lift int 'n => 'n -> generator int group : generator 'a -> generator 'a constant : 'a -> generator 'a -int : generator int -- small-biased, full range possible +int : generator int -- small-biased, arbitrary precision intBetween : (Lift int 'a, Lift int 'b) => 'a -> 'b -> generator int -intAtLeast : int -> generator int +intAtLeast : Lift int 'n => 'n -> generator int bool : generator bool bytes : generator bytes -- length 0..32 -bytesBetween : int -> int -> generator bytes -bytesExactly : int -> generator bytes +bytesBetween : (Lift int 'l, Lift int 'h) => 'l -> 'h -> generator bytes +bytesExactly : Lift int 'n => 'n -> generator bytes option : generator 'a -> generator (option 'a) listOf : generator 'a -> generator (list 'a) -- length 0..20 listBetween : (Lift int 'l, Lift int 'h) => 'l -> 'h -> generator 'a -> generator (list 'a) oneOf : cons (generator 'a) -> generator 'a -frequency : cons (int, generator 'a) -> generator 'a +frequency : Lift int 'w => cons ('w, generator 'a) -> generator 'a suchThat : ('a -> bool) -> generator 'a -> generator 'a -- gives up after 100 draws -data : generator Data -- arbitrary well-formed Data, depth-bounded tuple2 : generator 'a -> generator 'b -> generator ('a, 'b) ``` @@ -991,11 +990,33 @@ smaller values (testing.md "Shrinking"): - draw sizes before contents (`listOf` draws `choice 1` per element as a continue bit, where `0` means stop); -- `intBetween lo hi` is `lo + choice (hi - lo)`; `int` draws a width with - `choice 2` (0: `choice 255`, 1: 16-bit, 2: 64-bit) so small draws give - small magnitudes; +- `intBetween lo hi` has inclusive endpoints. Small spans use one choice; + larger spans assemble 64-bit chunks and reject offsets beyond the span, + avoiding clamping or wrapping the final chunk. Primitive draws retain the + existing hash-modulo distribution. Equal endpoints consume no choices. +- `int` selects one of three branches: 0..255, signed 16-bit, or an + arbitrary-precision signed magnitude. The magnitude stops with probability + 3/4 at each step, otherwise drawing another 64-bit chunk. Zero needs no sign + choice. `intAtLeast lo` adds such a nonnegative magnitude to `lo`. + There is no fixed integer-size ceiling; VM budgets still apply. - never consume choices on a path that cannot fail differently. +`frequency` uses relative integer weights. Zero entries are skipped, negative +weights fail before drawing, and empty/all-zero input returns None. Totals may +exceed u64; selection uses the same bounded chunk sampler as `intBetween`. + +`suchThat predicate generator` makes at most 100 attempts, grouping each draw. +Rejected values advance state; success on attempt 100 is accepted. Exhausting +attempts or an underlying draw returning None returns None. Replay uses only +recorded choices and never borrows from a sibling group or adds randomness. + +`bool` uses one 0/1 choice. `option` groups its presence choice and, when present, +its payload. `bytesBetween` uses inclusive length bounds; `bytesExactly` fixes +both bounds to its count. Negative lengths and reversed bounds fail. All bound +helpers normalize Big/little inputs and return little outer representations. +Arbitrary Data generation is outside this chunk; build domain generators with +ordinary composition instead. + The `prop` desugaring, the preparation programs, and the runner protocol are in testing.md and plans/10. diff --git a/plans/12-stdlib.md b/plans/12-stdlib.md index 28de9d9c..a659c346 100644 --- a/plans/12-stdlib.md +++ b/plans/12-stdlib.md @@ -55,7 +55,7 @@ replace the tested PRNG, replay, label, and assertion protocols. ## Current status -Reconciled with chunk 7 implementation (2026-09-23). Base currently +Reconciled with chunk 8 implementation (2026-09-25). Base currently ships 30 embedded Nash modules. Plan 12 remains incomplete in `SPEC.md`. | Chunk | Status | Remaining work | @@ -67,12 +67,12 @@ ships 30 embedded Nash modules. Plan 12 remains incomplete in `SPEC.md`. | 5 traits / operators | complete | none | | 6 type modules | complete | none | | 7 Data / Map | complete | — | -| 8 Prop | implemented foundation from Plan 10 | audit full planned API and property coverage | +| 8 Prop | complete | none | | 9 Test | complete through Plan 10 | preserve existing runner protocol | | 10 Ast / Derive | not implemented | requires Plan 11 | | 11 Cardano | not implemented in Base | library modules and ledger golden tests | -Latest full validation: 3,386 tests passed, 3 ignored; strict Clippy passed. +Latest full validation is recorded with chunk 8 below. Tests use compiler libraries and the evaluator, never a Nash CLI subprocess. ## Chunk 1: package skeleton and embedding — complete @@ -288,17 +288,20 @@ passed, 3 ignored. Snapshot tests run without a custom Rust stack setting. --- -## Chunk 8: `Prop` — foundation implemented through Plan 10 +## Chunk 8: `Prop` — complete - [x] Ship prng/generator types, choice bounds, seeded draws and validated replay. - [x] Use nested little Choice/Group traces with strict replay, consumed-trace reduction, and ordinary generator Functor/Applicative/Monad instances. - [x] Rebuild structures in Nash and evaluate the prepared property once, with consumed-draw feedback for reduction. -- [x] Ship direct generation functions: choice, constant, intBetween, int, listOf, - listBetween, tuple2, oneOf and bytes; sequence draws with explicit state. +- [x] Ship choice, constant, intBetween, intAtLeast, int, bool, option, listOf, + listBetween, tuple2, oneOf, frequency, suchThat, bytes, bytesBetween and + bytesExactly; sequence draws with generator do or explicit state. +- [x] Use arbitrary-precision integer generation built from u64 choices; normalize + Big/little bounds and weights. Arbitrary Data generation is out of scope. - [x] Test Nash generators against the Rust runner and replay protocol in `crates/nash-driver/tests/testing_base.rs`. -- [ ] Audit the complete docs/stdlib.md generator API and remaining properties. -- [ ] Verify the specific shrinking/range properties listed below. +- [x] Audit the complete docs/stdlib.md generator API and remaining properties. +- [x] Verify the specific shrinking/range properties listed below. Extend the existing implementation; do not replace its tested protocol. @@ -312,7 +315,7 @@ Extend the existing implementation; do not replace its tested protocol. docs/testing.md "Generators", verbatim: the **little** `prng` ADT that the runner builds as native constructor terms, the `generator 'a` function alias, direct draws or ordinary Monad composition, `choice` as the single primitive -over `u64` integer choices (not Aiken's bytes), and the generators listed +over `u64` integer choices, and the generators listed in docs/stdlib.md "`Prop`" built on `choice`. **Implementation and protocol** @@ -323,21 +326,25 @@ Choices are u64 integers. Invalid bounds fail; exhausted or invalid replay returns None. Seeded/Replayed payloads use little types. The existing runner owns sampling, replay and shrinking; preserve their behavior when extending APIs. -**Elm/Aiken reference** +**Acceptance coverage** -Aiken `stdlib/lib/aiken/` (`rand`, `int`, `list`, `bool`, -`bytearray`) and `crates/aiken-lang/src/test_framework.rs` `Prng` -(constructor tags: `Seeded = 0`, `Replayed = 1`; `Some = 0`, `None = 1` -must match the little `option` layout in plans/04). Nash differs in the -choice element type: `int`, not bytes (testing.md "Open questions"). +`PropHelpers.nash` runs through the shared in-process Base snapshot runner. +It covers arbitrary-size bounds and offsets, strict replay after rejected wide +samples, weights above u64, invalid/zero weights, optional draws, byte bounds, +filter attempts 100/101, and isolated replay groups. The tuple2 do rewrite +preserves the two recorded groups and unused sibling choices. -**Tests** +Generated properties verify integer ranges and byte lengths. Counterexample +snapshots show listOf int reducing to both `[]` and `[0]`, and a wide-range +counterexample reducing to its lower bound. Existing protocol tests continue to +exercise seeded/replayed choices, malformed traces and the Rust runner. -- `tests` block: `choice 10 (Seeded #"" Cons.Nil)` is `Some`; `choice 10 (Replayed Cons.Nil Cons.Nil)` is `None`; `choice 10 (Replayed (Cons.singleton (Choice 11)) Cons.Nil)` is `None` (over bound); `intBetween 3 3` is `3`. -- `prop "intBetween in range"`: `let lo via int; n via intBetween 0 1000` then `intBetween lo (lo + n)` called directly with a PRNG state stays in range. -- Rust (plans/10 chunk 6): a shrink test that a failing `listOf int` counterexample shrinks to `[0]` or `[]`. +The generator distribution changed for the wide branch of Prop.int; older +recordings using its previous signed-64-bit layout are not replay-compatible. +No PRNG wire tags, runner protocol, reducer passes or codegen behavior changed. -**Done when** the in-process Base test runner runs the props with the plans/10 runner. +Validation: formatting and strict Clippy passed; 3,444 workspace tests passed, +3 ignored. --- From 4f8f3a685a4888df37e3b0f8e283aba390756c31 Mon Sep 17 00:00:00 2001 From: microproofs Date: Fri, 25 Sep 2026 12:07:41 -0400 Subject: [PATCH 02/10] feat(base): improve Prop offsets and Int powers Signed-off-by: microproofs --- .sampo/changesets/prop-small-offsets.md | 5 ++ crates/nash-driver/base/src/Int.nash | 23 ++++++++- crates/nash-driver/base/src/Prop.nash | 18 +++---- .../fixtures/base-traits/PropHelpers.nash | 30 ++++++++++-- .../fixtures/base-traits/TypeHelpers.nash | 13 +++++ .../snapshots/base_traits__prop_helpers.snap | 4 +- .../snapshots/base_traits__type_helpers.snap | 4 +- ...sting_base__integer_width_bound_costs.snap | 22 +++++++++ crates/nash-driver/tests/testing_base.rs | 48 ++++++++++++++++++- docs/stdlib.md | 17 +++++-- plans/12-stdlib.md | 10 +++- 11 files changed, 171 insertions(+), 23 deletions(-) create mode 100644 .sampo/changesets/prop-small-offsets.md create mode 100644 crates/nash-driver/tests/snapshots/testing_base__integer_width_bound_costs.snap diff --git a/.sampo/changesets/prop-small-offsets.md b/.sampo/changesets/prop-small-offsets.md new file mode 100644 index 00000000..1d1beeff --- /dev/null +++ b/.sampo/changesets/prop-small-offsets.md @@ -0,0 +1,5 @@ +--- +cargo/nash-driver: minor +--- + +Choose small-biased bit widths before generating Prop.intAtLeast offsets and wide Prop.int magnitudes. Preserve arbitrary precision while replacing the zero-or-huge distribution with useful small nonzero values. Use exact expModInteger powers within each eight-bit band, with CEK budget snapshots comparing CPU and memory. Expose Int.pow2 and use its exact modular fast path in Int.pow for base two. diff --git a/crates/nash-driver/base/src/Int.nash b/crates/nash-driver/base/src/Int.nash index cd57a99f..3cfc13b2 100644 --- a/crates/nash-driver/base/src/Int.nash +++ b/crates/nash-driver/base/src/Int.nash @@ -1,4 +1,4 @@ -module Int exposing (add, sub, mul, negate, div, mod, quot, rem, compare, min, max, abs, pow, powMod, toBytes, fromBytes, toString) +module Int exposing (add, sub, mul, negate, div, mod, quot, rem, compare, min, max, abs, pow, pow2, powMod, toBytes, fromBytes, toString) import Prelude exposing (..) import Lift exposing (Lift) @@ -62,7 +62,11 @@ pow value exponent = e : int e = lower exponent in - if e < 0 then fail else power (lower value) e + if e < 0 then fail + else + let n : int + n = lower value + in if n == 2 then powerOfTwo e else power n e power : int -> int -> int power n e = @@ -71,6 +75,21 @@ power n e = let half = power n (quot e 2) in if rem e 2 == 0 then half * half else n * half * half +pow2 : Lift int 'e => 'e -> int +pow2 exponent = + let e : int + e = lower exponent + in if e < 0 then fail else powerOfTwo e + +powerOfTwo : int -> int +powerOfTwo e = + -- Local powers are at most 256, so modulus 257 leaves them unchanged. + if e <= 8 then Builtin.expModInteger 2 e 257 + else + let bands = quot (e - 1) 8 + bits = rem (e - 1) 8 + 1 + in power 256 bands * Builtin.expModInteger 2 bits 257 + powMod : (Lift int 'n, Lift int 'e, Lift int 'm) => 'n -> 'e -> 'm -> int powMod n e m = Builtin.expModInteger (lower n) (lower e) (lower m) diff --git a/crates/nash-driver/base/src/Prop.nash b/crates/nash-driver/base/src/Prop.nash index 4f70736c..c35f3744 100644 --- a/crates/nash-driver/base/src/Prop.nash +++ b/crates/nash-driver/base/src/Prop.nash @@ -1,6 +1,7 @@ module Prop exposing (type prng(..), type choiceTree(..), type generator, group, choice, constant, intBetween, int, listOf, listBetween, tuple2, oneOf, bytes, bool, option, bytesBetween, bytesExactly, frequency, suchThat, intAtLeast) import Builtin +import Int import Functor import Applicative import Monad @@ -115,19 +116,18 @@ bounded maximum = let value = high * 18446744073709551616 + low in if value <= maximum then Applicative.pure value else bounded maximum --- Stop with probability 3/4; otherwise append a chunk. No fixed size ceiling. +-- Favor small bit widths, then sample within that width. natural : generator int natural = naturalFrom 1 naturalFrom : int -> generator int -naturalFrom place = do - continue <- choice 3 - case continue of - 3 -> do - digit <- choice 18446744073709551615 - rest <- naturalFrom (place * 18446744073709551616) - Applicative.pure (digit * place + rest) - _ -> Applicative.pure 0 +naturalFrom scale = do + wider <- choice 3 + case wider of + 3 -> naturalFrom (scale * 256) + _ -> do + bits <- choice 7 + bounded (scale * Int.pow2 (bits + 1) - 1) intAtLeast : Lift int 'n => 'n -> generator int intAtLeast minimum = diff --git a/crates/nash-driver/tests/fixtures/base-traits/PropHelpers.nash b/crates/nash-driver/tests/fixtures/base-traits/PropHelpers.nash index 1319c7b8..fdd88a12 100644 --- a/crates/nash-driver/tests/fixtures/base-traits/PropHelpers.nash +++ b/crates/nash-driver/tests/fixtures/base-traits/PropHelpers.nash @@ -37,7 +37,29 @@ hugeLittle = lower huge never : Prop.generator int never _ = fail +sampleOffsets : int -> Prop.prng -> (int, int, int) +sampleOffsets count state = + if count == 0 then (0, 0, 0) + else case Prop.intAtLeast 0 state of + None -> fail + Some (n, next) -> + let (zeros, small, large) = sampleOffsets (count - 1) next in + if n == 0 then (zeros + 1, small, large) + else if n <= 255 then (zeros, small + 1, large) + else (zeros, small, large + 1) + tests + test "small widths produce useful nonzero offsets" = do + assert (value (Prop.intAtLeast 0) (rebuild [0, 0, 1]) == Some 1) + assert (value (Prop.intAtLeast 0) (rebuild [0, 1, 2]) == Some 2) + assert (value (Prop.intAtLeast 0) (rebuild [0, 3, 10]) == Some 10) + assert (value (Prop.intAtLeast 0) (rebuild [0, 6, 100]) == Some 100) + assert (value (Prop.intAtLeast 0) (rebuild [0, 7, 255]) == Some 255) + assert (value (Prop.intAtLeast 0) (rebuild [3, 0, 0, 256]) == Some 256) + test "seeded offsets favor small nonzero values" = do + let (zeros, small, large) = sampleOffsets 256 seed in + let _ = assert (zeros < 64 && small > 128 && large > 0) in + Builtin.trace (show (zeros, small, large)) () test "wide range strict replay isolates rejected attempts" = do assert (value (Prop.intBetween 0 18446744073709551616) (replay (Cons (g (one (c 1))) (Cons (g (one (c 1))) (two (g (one (c 1))) (g (one (c 0))))))) == Some 18446744073709551616) test "weight totals can exceed one choice" = do @@ -53,10 +75,10 @@ tests do assert (n < 0) test "arbitrary integer offsets preserve large lower bounds" = do - assert (value (Prop.intAtLeast huge) (rebuild [0]) == Some (lower huge)) - assert (value (Prop.intAtLeast (-hugeLittle)) (rebuild [3, 0, 3, 1, 0]) == Some (18446744073709551616 - lower huge)) - assert (value Prop.int (rebuild [2, 3, 0, 3, 1, 0, 0]) == Some 18446744073709551616) - assert (value Prop.int (rebuild [2, 3, 0, 3, 1, 0, 1]) == Some (-18446744073709551616)) + assert (value (Prop.intAtLeast huge) (rebuild [0, 0, 0]) == Some (lower huge)) + assert (value (Prop.intAtLeast (-hugeLittle)) (rebuild [3, 3, 3, 3, 3, 3, 3, 3, 0, 0, 1, 0]) == Some (18446744073709551616 - lower huge)) + assert (value Prop.int (rebuild [2, 3, 3, 3, 3, 3, 3, 3, 3, 0, 0, 1, 0, 0]) == Some 18446744073709551616) + assert (value Prop.int (rebuild [2, 3, 3, 3, 3, 3, 3, 3, 3, 0, 0, 1, 0, 1]) == Some (-18446744073709551616)) test "wide bounded generation rejects overshoot then retries" = do assert (value (Prop.intBetween 0 18446744073709551616) (rebuild [1, 1, 1, 0]) == Some 18446744073709551616) assert (value (Prop.intBetween 0 18446744073709551616) (rebuild [1, 1]) == None) diff --git a/crates/nash-driver/tests/fixtures/base-traits/TypeHelpers.nash b/crates/nash-driver/tests/fixtures/base-traits/TypeHelpers.nash index 16391c59..2ad7ad8a 100644 --- a/crates/nash-driver/tests/fixtures/base-traits/TypeHelpers.nash +++ b/crates/nash-driver/tests/fixtures/base-traits/TypeHelpers.nash @@ -36,6 +36,19 @@ tests assert (Int.abs bigInt == 7) assert (Int.pow 2 10 == 1024) assert (Int.pow bigInt 0 == 1) + test "exact powers of two across byte boundaries" = do + assert (Int.pow2 0 == 1) + assert (Int.pow2 bigInt == 128) + assert (Int.pow2 8 == 256) + assert (Int.pow2 9 == 512) + assert (Int.pow2 64 == 18446744073709551616) + assert (Int.pow2 128 == 18446744073709551616 * 18446744073709551616) + assert (Int.pow 2 128 == Int.pow2 128) + assert (Int.pow (-2) 9 == -512) + assert (Int.pow 3 5 == 243) + assert (Int.pow 0 0 == 1) + test "negative power of two exponent" fail = do + assert (Int.pow2 (-1) == 0) test "integer helpers 2" = do assert (Int.powMod 2 10 7 == 2) assert (Int.toBytes True 2 bigInt == #"0007") diff --git a/crates/nash-driver/tests/snapshots/base_traits__prop_helpers.snap b/crates/nash-driver/tests/snapshots/base_traits__prop_helpers.snap index b22b1f26..68886bbe 100644 --- a/crates/nash-driver/tests/snapshots/base_traits__prop_helpers.snap +++ b/crates/nash-driver/tests/snapshots/base_traits__prop_helpers.snap @@ -1,7 +1,9 @@ --- source: crates/nash-driver/tests/base_traits.rs -description: "module Main exposing (..)\n\nimport Cons exposing (type cons(..))\n\nreplay choices = Prop.Replayed choices Nil\nc = Prop.Choice\ng = Prop.Group\none x = Cons x Nil\ntwo x y = Cons x (one y)\n\nvalue generator state = case generator state of\n Some (result, _) -> Some result\n None -> None\n\nseed = Prop.Seeded #\"\" Nil\nweights : cons (int, Prop.generator int)\nweights = Cons (0, \\_ -> fail) (Cons (3, Prop.constant 10) (Cons (1, Prop.constant 20) Nil))\nemptyWeights : cons (int, Prop.generator int)\nemptyWeights = Nil\nbigWeight : Int\nbigWeight = 2\nbigCount : Int\nbigCount = 3\n\nattempts n =\n if n == 0 then one (g (one (c 1)))\n else Cons (g (one (c 0))) (attempts (n - 1))\n\nbyteDraw n = g (one (g (one (c n))))\n\nrebuild choices = Prop.Rebuilding choices Nil\nhuge : Int\nhuge = lift (18446744073709551616 * 18446744073709551616)\n\nhugeLittle : int\nhugeLittle = lower huge\nnever : Prop.generator int\nnever _ = fail\n\ntests\n test \"wide range strict replay isolates rejected attempts\" = do\n assert (value (Prop.intBetween 0 18446744073709551616) (replay (Cons (g (one (c 1))) (Cons (g (one (c 1))) (two (g (one (c 1))) (g (one (c 0))))))) == Some 18446744073709551616)\n test \"weight totals can exceed one choice\" = do\n let options = two (18446744073709551616, Prop.constant 10) (1, Prop.constant 20) in\n assert (value (Prop.frequency options) (rebuild [1, 0]) == Some 20)\n let options = two (18446744073709551616, Prop.constant 10) (1, Prop.constant 20) in\n assert (value (Prop.frequency options) (rebuild [0, 18446744073709551615]) == Some 10)\n test \"reversed byte lengths fail\" fail = do\n case Prop.bytesBetween 3 2 of\n _ -> ()\n prop \"wide range counterexample replays and reduces\" fail once =\n let n via Prop.intBetween hugeLittle (hugeLittle * 2) in\n do\n assert (n < 0)\n test \"arbitrary integer offsets preserve large lower bounds\" = do\n assert (value (Prop.intAtLeast huge) (rebuild [0]) == Some (lower huge))\n assert (value (Prop.intAtLeast (-hugeLittle)) (rebuild [3, 0, 3, 1, 0]) == Some (18446744073709551616 - lower huge))\n assert (value Prop.int (rebuild [2, 3, 0, 3, 1, 0, 0]) == Some 18446744073709551616)\n assert (value Prop.int (rebuild [2, 3, 0, 3, 1, 0, 1]) == Some (-18446744073709551616))\n test \"wide bounded generation rejects overshoot then retries\" = do\n assert (value (Prop.intBetween 0 18446744073709551616) (rebuild [1, 1, 1, 0]) == Some 18446744073709551616)\n assert (value (Prop.intBetween 0 18446744073709551616) (rebuild [1, 1]) == None)\n prop \"arbitrary integer lower bound\" =\n let n via Prop.intAtLeast huge in\n do\n assert (n >= lower huge)\n test \"boolean choices\" = do\n assert (value Prop.bool (replay (one (c 0))) == Some False)\n assert (value Prop.bool (replay (one (c 1))) == Some True)\n test \"optional draw preserves absence and groups the payload\" = do\n assert (value (Prop.option never) (replay (one (g (one (c 0))))) == Some None)\n assert (value (Prop.option (Prop.choice 9)) (replay (two (g (one (c 1))) (g (one (c 7))))) == Some (Some 7))\n test \"tuple bind preserves groups and remaining siblings\" = do\n case Prop.tuple2 (Prop.choice 9) (Prop.choice 9) (replay (Cons (g (one (c 4))) (Cons (g (one (c 5))) (one (c 8))))) of\n Some ((4, 5), Prop.Replayed rest used) ->\n case (rest, used) of\n (Cons (Prop.Choice 8) Nil, Cons (Prop.Group (Cons (Prop.Choice 5) Nil)) (Cons (Prop.Group (Cons (Prop.Choice 4) Nil)) Nil)) -> ()\n _ -> fail\n _ -> fail\n test \"weighted endpoints skip zero weights\" = do\n assert (value (Prop.frequency weights) (replay (two (c 0) (g Nil))) == Some 10)\n assert (value (Prop.frequency weights) (replay (two (c 2) (g Nil))) == Some 10)\n assert (value (Prop.frequency weights) (replay (two (c 3) (g Nil))) == Some 20)\n assert (value (Prop.frequency (one (bigWeight, Prop.constant 9))) seed == Some 9)\n assert (value (Prop.frequency emptyWeights) seed == None)\n assert (value (Prop.frequency (one (0, Prop.constant 9))) seed == None)\n test \"negative weights fail even after a positive entry\" fail = do\n case Prop.frequency (Cons (1, Prop.constant 1) (one (-1, Prop.constant 2))) of\n _ -> ()\n test \"filter retries recorded groups and keeps remaining siblings\" = do\n case Prop.suchThat (\\n -> n > 0) (Prop.choice 9) (replay (Cons (g (one (c 0))) (Cons (g (one (c 4))) (one (c 8))))) of\n Some (4, Prop.Replayed (Cons (Prop.Choice 8) Nil) _) -> ()\n _ -> fail\n assert (value (Prop.suchThat (\\n -> n > 0) (Prop.choice 9)) (replay (one (g (one (c 0))))) == None)\n assert (value (Prop.suchThat (\\_ -> False) (Prop.constant 1)) seed == None)\n test \"filter allows exactly 100 attempts\" = do\n assert (value (Prop.suchThat (\\n -> n == 1) (Prop.choice 1)) (replay (attempts 99)) == Some 1)\n assert (value (Prop.suchThat (\\n -> n == 1) (Prop.choice 1)) (replay (attempts 100)) == None)\n test \"filter cannot borrow choices from the next attempt\" = do\n assert (value (Prop.suchThat (\\_ -> True) (Prop.choice 1)) (replay (two (g Nil) (g (one (c 1))))) == None)\n test \"exact byte replay includes both endpoints\" = do\n assert (value (Prop.bytesExactly 2) (replay (two (byteDraw 0) (byteDraw 255))) == Some #\"00ff\")\n assert (value (Prop.bytesExactly 2) (replay (one (byteDraw 0))) == None)\n test \"exact bytes normalize Big counts\" = do\n case value (Prop.bytesExactly bigCount) seed of\n Some bytes -> assert (Bytes.length bytes == 3)\n None -> fail\n assert (value (Prop.bytesExactly 0) (replay Nil) == Some #\"\")\n test \"negative byte count fails\" fail = do\n case Prop.bytesExactly -1 of\n _ -> ()\n prop \"byte length in bounds\" =\n let bytes via Prop.bytesBetween 2 7 in\n do\n assert (Bytes.length bytes >= 2 && Bytes.length bytes <= 7)\n prop \"generated integer bounds\" =\n let lo via Prop.int\n span via Prop.intBetween 0 1000\n in\n do\n case value (Prop.intBetween lo (lo + span)) seed of\n Some n -> assert (n >= lo && n <= lo + span)\n None -> fail\n prop \"listOf reduces to empty\" fail once =\n let items via Prop.listOf Prop.int in\n do\n assert (List.length items < 0)\n prop \"listOf reduces to one zero\" fail once =\n let items via Prop.listOf Prop.int in\n do\n assert (List.length items == 0)\n" +description: "module Main exposing (..)\n\nimport Cons exposing (type cons(..))\n\nreplay choices = Prop.Replayed choices Nil\nc = Prop.Choice\ng = Prop.Group\none x = Cons x Nil\ntwo x y = Cons x (one y)\n\nvalue generator state = case generator state of\n Some (result, _) -> Some result\n None -> None\n\nseed = Prop.Seeded #\"\" Nil\nweights : cons (int, Prop.generator int)\nweights = Cons (0, \\_ -> fail) (Cons (3, Prop.constant 10) (Cons (1, Prop.constant 20) Nil))\nemptyWeights : cons (int, Prop.generator int)\nemptyWeights = Nil\nbigWeight : Int\nbigWeight = 2\nbigCount : Int\nbigCount = 3\n\nattempts n =\n if n == 0 then one (g (one (c 1)))\n else Cons (g (one (c 0))) (attempts (n - 1))\n\nbyteDraw n = g (one (g (one (c n))))\n\nrebuild choices = Prop.Rebuilding choices Nil\nhuge : Int\nhuge = lift (18446744073709551616 * 18446744073709551616)\n\nhugeLittle : int\nhugeLittle = lower huge\nnever : Prop.generator int\nnever _ = fail\n\nsampleOffsets : int -> Prop.prng -> (int, int, int)\nsampleOffsets count state =\n if count == 0 then (0, 0, 0)\n else case Prop.intAtLeast 0 state of\n None -> fail\n Some (n, next) ->\n let (zeros, small, large) = sampleOffsets (count - 1) next in\n if n == 0 then (zeros + 1, small, large)\n else if n <= 255 then (zeros, small + 1, large)\n else (zeros, small, large + 1)\n\ntests\n test \"small widths produce useful nonzero offsets\" = do\n assert (value (Prop.intAtLeast 0) (rebuild [0, 0, 1]) == Some 1)\n assert (value (Prop.intAtLeast 0) (rebuild [0, 1, 2]) == Some 2)\n assert (value (Prop.intAtLeast 0) (rebuild [0, 3, 10]) == Some 10)\n assert (value (Prop.intAtLeast 0) (rebuild [0, 6, 100]) == Some 100)\n assert (value (Prop.intAtLeast 0) (rebuild [0, 7, 255]) == Some 255)\n assert (value (Prop.intAtLeast 0) (rebuild [3, 0, 0, 256]) == Some 256)\n test \"seeded offsets favor small nonzero values\" = do\n let (zeros, small, large) = sampleOffsets 256 seed in\n assert (zeros < 64 && small > 128 && large > 0)\n Builtin.trace (show (sampleOffsets 256 seed)) ()\n test \"wide range strict replay isolates rejected attempts\" = do\n assert (value (Prop.intBetween 0 18446744073709551616) (replay (Cons (g (one (c 1))) (Cons (g (one (c 1))) (two (g (one (c 1))) (g (one (c 0))))))) == Some 18446744073709551616)\n test \"weight totals can exceed one choice\" = do\n let options = two (18446744073709551616, Prop.constant 10) (1, Prop.constant 20) in\n assert (value (Prop.frequency options) (rebuild [1, 0]) == Some 20)\n let options = two (18446744073709551616, Prop.constant 10) (1, Prop.constant 20) in\n assert (value (Prop.frequency options) (rebuild [0, 18446744073709551615]) == Some 10)\n test \"reversed byte lengths fail\" fail = do\n case Prop.bytesBetween 3 2 of\n _ -> ()\n prop \"wide range counterexample replays and reduces\" fail once =\n let n via Prop.intBetween hugeLittle (hugeLittle * 2) in\n do\n assert (n < 0)\n test \"arbitrary integer offsets preserve large lower bounds\" = do\n assert (value (Prop.intAtLeast huge) (rebuild [0, 0, 0]) == Some (lower huge))\n assert (value (Prop.intAtLeast (-hugeLittle)) (rebuild [3, 3, 3, 3, 3, 3, 3, 3, 0, 0, 1, 0]) == Some (18446744073709551616 - lower huge))\n assert (value Prop.int (rebuild [2, 3, 3, 3, 3, 3, 3, 3, 3, 0, 0, 1, 0, 0]) == Some 18446744073709551616)\n assert (value Prop.int (rebuild [2, 3, 3, 3, 3, 3, 3, 3, 3, 0, 0, 1, 0, 1]) == Some (-18446744073709551616))\n test \"wide bounded generation rejects overshoot then retries\" = do\n assert (value (Prop.intBetween 0 18446744073709551616) (rebuild [1, 1, 1, 0]) == Some 18446744073709551616)\n assert (value (Prop.intBetween 0 18446744073709551616) (rebuild [1, 1]) == None)\n prop \"arbitrary integer lower bound\" =\n let n via Prop.intAtLeast huge in\n do\n assert (n >= lower huge)\n test \"boolean choices\" = do\n assert (value Prop.bool (replay (one (c 0))) == Some False)\n assert (value Prop.bool (replay (one (c 1))) == Some True)\n test \"optional draw preserves absence and groups the payload\" = do\n assert (value (Prop.option never) (replay (one (g (one (c 0))))) == Some None)\n assert (value (Prop.option (Prop.choice 9)) (replay (two (g (one (c 1))) (g (one (c 7))))) == Some (Some 7))\n test \"tuple bind preserves groups and remaining siblings\" = do\n case Prop.tuple2 (Prop.choice 9) (Prop.choice 9) (replay (Cons (g (one (c 4))) (Cons (g (one (c 5))) (one (c 8))))) of\n Some ((4, 5), Prop.Replayed rest used) ->\n case (rest, used) of\n (Cons (Prop.Choice 8) Nil, Cons (Prop.Group (Cons (Prop.Choice 5) Nil)) (Cons (Prop.Group (Cons (Prop.Choice 4) Nil)) Nil)) -> ()\n _ -> fail\n _ -> fail\n test \"weighted endpoints skip zero weights\" = do\n assert (value (Prop.frequency weights) (replay (two (c 0) (g Nil))) == Some 10)\n assert (value (Prop.frequency weights) (replay (two (c 2) (g Nil))) == Some 10)\n assert (value (Prop.frequency weights) (replay (two (c 3) (g Nil))) == Some 20)\n assert (value (Prop.frequency (one (bigWeight, Prop.constant 9))) seed == Some 9)\n assert (value (Prop.frequency emptyWeights) seed == None)\n assert (value (Prop.frequency (one (0, Prop.constant 9))) seed == None)\n test \"negative weights fail even after a positive entry\" fail = do\n case Prop.frequency (Cons (1, Prop.constant 1) (one (-1, Prop.constant 2))) of\n _ -> ()\n test \"filter retries recorded groups and keeps remaining siblings\" = do\n case Prop.suchThat (\\n -> n > 0) (Prop.choice 9) (replay (Cons (g (one (c 0))) (Cons (g (one (c 4))) (one (c 8))))) of\n Some (4, Prop.Replayed (Cons (Prop.Choice 8) Nil) _) -> ()\n _ -> fail\n assert (value (Prop.suchThat (\\n -> n > 0) (Prop.choice 9)) (replay (one (g (one (c 0))))) == None)\n assert (value (Prop.suchThat (\\_ -> False) (Prop.constant 1)) seed == None)\n test \"filter allows exactly 100 attempts\" = do\n assert (value (Prop.suchThat (\\n -> n == 1) (Prop.choice 1)) (replay (attempts 99)) == Some 1)\n assert (value (Prop.suchThat (\\n -> n == 1) (Prop.choice 1)) (replay (attempts 100)) == None)\n test \"filter cannot borrow choices from the next attempt\" = do\n assert (value (Prop.suchThat (\\_ -> True) (Prop.choice 1)) (replay (two (g Nil) (g (one (c 1))))) == None)\n test \"exact byte replay includes both endpoints\" = do\n assert (value (Prop.bytesExactly 2) (replay (two (byteDraw 0) (byteDraw 255))) == Some #\"00ff\")\n assert (value (Prop.bytesExactly 2) (replay (one (byteDraw 0))) == None)\n test \"exact bytes normalize Big counts\" = do\n case value (Prop.bytesExactly bigCount) seed of\n Some bytes -> assert (Bytes.length bytes == 3)\n None -> fail\n assert (value (Prop.bytesExactly 0) (replay Nil) == Some #\"\")\n test \"negative byte count fails\" fail = do\n case Prop.bytesExactly -1 of\n _ -> ()\n prop \"byte length in bounds\" =\n let bytes via Prop.bytesBetween 2 7 in\n do\n assert (Bytes.length bytes >= 2 && Bytes.length bytes <= 7)\n prop \"generated integer bounds\" =\n let lo via Prop.int\n span via Prop.intBetween 0 1000\n in\n do\n case value (Prop.intBetween lo (lo + span)) seed of\n Some n -> assert (n >= lo && n <= lo + span)\n None -> fail\n prop \"listOf reduces to empty\" fail once =\n let items via Prop.listOf Prop.int in\n do\n assert (List.length items < 0)\n prop \"listOf reduces to one zero\" fail once =\n let items via Prop.listOf Prop.int in\n do\n assert (List.length items == 0)\n" --- +small widths produce useful nonzero offsets: Pass; traces: [] +seeded offsets favor small nonzero values: Pass; traces: ["(26, 169, 61)"] wide range strict replay isolates rejected attempts: Pass; traces: [] weight totals can exceed one choice: Pass; traces: [] reversed byte lengths fail: Pass; traces: ["Prop.listBetween has invalid bounds"] diff --git a/crates/nash-driver/tests/snapshots/base_traits__type_helpers.snap b/crates/nash-driver/tests/snapshots/base_traits__type_helpers.snap index 8e06e228..7df96ef4 100644 --- a/crates/nash-driver/tests/snapshots/base_traits__type_helpers.snap +++ b/crates/nash-driver/tests/snapshots/base_traits__type_helpers.snap @@ -1,11 +1,13 @@ --- source: crates/nash-driver/tests/base_traits.rs -description: "module Main exposing (..)\n\nbigInt : Int\nbigInt = 7\nbigBytes : Bytes\nbigBytes = #\"abcd\"\nbigIndices : List Int\nbigIndices = lift [Builtin.iData 0, Builtin.iData 8]\nbigOption : Option Int\nbigOption = Option.Some 7\nlittleOption : option Int\nlittleOption = Some 7\nnone : option int\nnone = None\nboth : (Lift bool 'a, Lift bool 'b) => 'a -> 'b -> bool\nboth a b = Bool.and a b\nbadBig : unit -> Bool\nbadBig _ = fail\n\ntests\n test \"mixed booleans and ordering\" = do\n assert (Bool.and Bool.True True)\n assert (Bool.or False Bool.True)\n assert (not (Bool.xor Bool.True True))\n assert (not (Bool.not Bool.True))\n test \"mixed booleans and ordering 2\" = do\n assert (Bool.or Bool.True (badBig ()))\n assert (not (Bool.and False (badBig ())))\n assert (Bool.True || badBig ())\n assert (both Bool.True True)\n test \"mixed booleans and ordering 3\" = do\n assert (Ordering.invert Ordering.LT == GT)\n assert (Ordering.then_ EQ Ordering.GT == GT)\n test \"integer helpers\" = do\n assert (Int.abs (-7) == 7)\n assert (Int.abs bigInt == 7)\n assert (Int.pow 2 10 == 1024)\n assert (Int.pow bigInt 0 == 1)\n test \"integer helpers 2\" = do\n assert (Int.powMod 2 10 7 == 2)\n assert (Int.toBytes True 2 bigInt == #\"0007\")\n assert (Int.fromBytes False bigBytes == 52651)\n assert (Int.toString bigInt == \"7\")\n test \"negative exponent\" fail = do\n assert (Int.pow 2 (-1) == 0)\n test \"negative integer encoding\" fail = do\n assert (Int.toBytes True 0 (-1) == #\"\")\n test \"byte access and slicing\" = do\n assert (Bytes.length bigBytes == 2)\n assert (Bytes.at bigBytes 1 == 205)\n assert (Bytes.slice 1 1 bigBytes == #\"cd\")\n assert (Bytes.take 1 bigBytes == #\"ab\")\n test \"byte access and slicing 2\" = do\n assert (Bytes.drop 1 bigBytes == #\"cd\")\n assert (Bytes.drop (-1) bigBytes == #\"abcd\")\n assert (Bytes.take (-1) bigBytes == #\"\")\n assert (Bytes.toHex bigBytes == \"abcd\")\n test \"byte access and slicing 3\" = do\n assert (Bytes.concat [bigBytes, bigBytes] == #\"abcdabcd\")\n test \"byte logic\" = do\n assert (Bytes.and False bigBytes #\"ff00\" == #\"ab00\")\n assert (Bytes.or False bigBytes #\"00ff\" == #\"abff\")\n assert (Bytes.xor False bigBytes #\"ffff\" == #\"5432\")\n assert (Bytes.complement bigBytes == #\"5432\")\n test \"byte logic 2\" = do\n assert (Bytes.readBit #\"01\" 0)\n assert (Bytes.writeBits #\"0000\" bigIndices True == #\"0101\")\n assert (Bytes.shift #\"01\" 1 == #\"02\")\n assert (Bytes.rotate #\"80\" 1 == #\"01\")\n test \"byte logic 3\" = do\n assert (Bytes.countSetBits bigBytes == 10)\n assert (Bytes.findFirstSetBit #\"08\" == 3)\n assert (Bytes.findFirstSetBit #\"00\" == -1)\n test \"hashes\" = do\n assert (Bytes.sha2_256 #\"\" == #\"e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855\")\n assert (Bytes.sha3_256 #\"\" == #\"a7ffc6f8bf1ed76651c14756a061d662f580ff4de43b49fa82d80a4b80f8434a\")\n assert (Bytes.blake2b_256 #\"\" == #\"0e5751c026e543b2e8ab2eb06099daa1d1e5df47778f7787faab45cdf12fe3a8\")\n assert (Bytes.blake2b_224 #\"\" == #\"836cc68931c2e4e3e838602eca1902591d216837bafddfe6f0c8cb07\")\n test \"hashes 2\" = do\n assert (Bytes.keccak_256 #\"\" == #\"c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470\")\n assert (Bytes.ripemd_160 #\"\" == #\"9c1185a5c5e9fc54612808977ee8f548b2258d31\")\n test \"byte index out of bounds\" fail = do\n assert (Bytes.at #\"\" 0 == 0)\n test \"text helpers\" = do\n assert (String.toBytes \"é\" == #\"c3a9\")\n assert (String.fromBytes #\"c3a9\" == \"é\")\n assert (String.fromInt bigInt == \"7\")\n assert (String.concat [\"a\", \"b\"] == \"ab\")\n test \"text helpers 2\" = do\n assert (String.join \",\" [\"a\", \"b\", \"c\"] == \"a,b,c\")\n assert (String.join \",\" [] == \"\")\n test \"invalid UTF-8\" fail = do\n assert (String.fromBytes #\"ff\" == \"\")\n test \"option helpers preserve payloads\" = do\n assert (Option.map Builtin.unIData bigOption == Some 7)\n assert (Option.map2 Int.add littleOption bigOption == Some 14)\n assert (Option.andThen (\\x -> Option.Some x) littleOption == littleOption)\n assert (Option.isSome bigOption)\n assert (Option.apply (Some Builtin.unIData) bigOption == Some 7)\n test \"option helpers preserve payloads 2\" = do\n assert (not (Option.isSome none))\n assert (Option.unwrap bigOption == bigInt)\n assert (Option.withDefault bigInt bigOption == bigInt)\n test \"unwrap None\" fail = do\n assert (Option.unwrap none == 0)\n test \"Big scalar control inputs\" = do\n assert (List.take bigInt [1, 2] == [1, 2])\n assert (Int.pow 2 bigInt == 128)\n assert (Int.toBytes Bool.True bigInt 1 == #\"00000000000001\")\n assert (Bytes.or Bool.True #\"00\" #\"ff\" == #\"ff\")\n test \"Big array indices\" = do\n assert (Array.at (Array.fromList [5, 6]) (Builtin.iData 1) == 6)\n assert (Array.get (Array.fromList [5, 6]) bigInt == None)\n" +description: "module Main exposing (..)\n\nbigInt : Int\nbigInt = 7\nbigBytes : Bytes\nbigBytes = #\"abcd\"\nbigIndices : List Int\nbigIndices = lift [Builtin.iData 0, Builtin.iData 8]\nbigOption : Option Int\nbigOption = Option.Some 7\nlittleOption : option Int\nlittleOption = Some 7\nnone : option int\nnone = None\nboth : (Lift bool 'a, Lift bool 'b) => 'a -> 'b -> bool\nboth a b = Bool.and a b\nbadBig : unit -> Bool\nbadBig _ = fail\n\ntests\n test \"mixed booleans and ordering\" = do\n assert (Bool.and Bool.True True)\n assert (Bool.or False Bool.True)\n assert (not (Bool.xor Bool.True True))\n assert (not (Bool.not Bool.True))\n test \"mixed booleans and ordering 2\" = do\n assert (Bool.or Bool.True (badBig ()))\n assert (not (Bool.and False (badBig ())))\n assert (Bool.True || badBig ())\n assert (both Bool.True True)\n test \"mixed booleans and ordering 3\" = do\n assert (Ordering.invert Ordering.LT == GT)\n assert (Ordering.then_ EQ Ordering.GT == GT)\n test \"integer helpers\" = do\n assert (Int.abs (-7) == 7)\n assert (Int.abs bigInt == 7)\n assert (Int.pow 2 10 == 1024)\n assert (Int.pow bigInt 0 == 1)\n test \"exact powers of two across byte boundaries\" = do\n assert (Int.pow2 0 == 1)\n assert (Int.pow2 bigInt == 128)\n assert (Int.pow2 8 == 256)\n assert (Int.pow2 9 == 512)\n assert (Int.pow2 64 == 18446744073709551616)\n assert (Int.pow2 128 == 18446744073709551616 * 18446744073709551616)\n assert (Int.pow 2 128 == Int.pow2 128)\n assert (Int.pow (-2) 9 == -512)\n assert (Int.pow 3 5 == 243)\n assert (Int.pow 0 0 == 1)\n test \"negative power of two exponent\" fail = do\n assert (Int.pow2 (-1) == 0)\n test \"integer helpers 2\" = do\n assert (Int.powMod 2 10 7 == 2)\n assert (Int.toBytes True 2 bigInt == #\"0007\")\n assert (Int.fromBytes False bigBytes == 52651)\n assert (Int.toString bigInt == \"7\")\n test \"negative exponent\" fail = do\n assert (Int.pow 2 (-1) == 0)\n test \"negative integer encoding\" fail = do\n assert (Int.toBytes True 0 (-1) == #\"\")\n test \"byte access and slicing\" = do\n assert (Bytes.length bigBytes == 2)\n assert (Bytes.at bigBytes 1 == 205)\n assert (Bytes.slice 1 1 bigBytes == #\"cd\")\n assert (Bytes.take 1 bigBytes == #\"ab\")\n test \"byte access and slicing 2\" = do\n assert (Bytes.drop 1 bigBytes == #\"cd\")\n assert (Bytes.drop (-1) bigBytes == #\"abcd\")\n assert (Bytes.take (-1) bigBytes == #\"\")\n assert (Bytes.toHex bigBytes == \"abcd\")\n test \"byte access and slicing 3\" = do\n assert (Bytes.concat [bigBytes, bigBytes] == #\"abcdabcd\")\n test \"byte logic\" = do\n assert (Bytes.and False bigBytes #\"ff00\" == #\"ab00\")\n assert (Bytes.or False bigBytes #\"00ff\" == #\"abff\")\n assert (Bytes.xor False bigBytes #\"ffff\" == #\"5432\")\n assert (Bytes.complement bigBytes == #\"5432\")\n test \"byte logic 2\" = do\n assert (Bytes.readBit #\"01\" 0)\n assert (Bytes.writeBits #\"0000\" bigIndices True == #\"0101\")\n assert (Bytes.shift #\"01\" 1 == #\"02\")\n assert (Bytes.rotate #\"80\" 1 == #\"01\")\n test \"byte logic 3\" = do\n assert (Bytes.countSetBits bigBytes == 10)\n assert (Bytes.findFirstSetBit #\"08\" == 3)\n assert (Bytes.findFirstSetBit #\"00\" == -1)\n test \"hashes\" = do\n assert (Bytes.sha2_256 #\"\" == #\"e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855\")\n assert (Bytes.sha3_256 #\"\" == #\"a7ffc6f8bf1ed76651c14756a061d662f580ff4de43b49fa82d80a4b80f8434a\")\n assert (Bytes.blake2b_256 #\"\" == #\"0e5751c026e543b2e8ab2eb06099daa1d1e5df47778f7787faab45cdf12fe3a8\")\n assert (Bytes.blake2b_224 #\"\" == #\"836cc68931c2e4e3e838602eca1902591d216837bafddfe6f0c8cb07\")\n test \"hashes 2\" = do\n assert (Bytes.keccak_256 #\"\" == #\"c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470\")\n assert (Bytes.ripemd_160 #\"\" == #\"9c1185a5c5e9fc54612808977ee8f548b2258d31\")\n test \"byte index out of bounds\" fail = do\n assert (Bytes.at #\"\" 0 == 0)\n test \"text helpers\" = do\n assert (String.toBytes \"é\" == #\"c3a9\")\n assert (String.fromBytes #\"c3a9\" == \"é\")\n assert (String.fromInt bigInt == \"7\")\n assert (String.concat [\"a\", \"b\"] == \"ab\")\n test \"text helpers 2\" = do\n assert (String.join \",\" [\"a\", \"b\", \"c\"] == \"a,b,c\")\n assert (String.join \",\" [] == \"\")\n test \"invalid UTF-8\" fail = do\n assert (String.fromBytes #\"ff\" == \"\")\n test \"option helpers preserve payloads\" = do\n assert (Option.map Builtin.unIData bigOption == Some 7)\n assert (Option.map2 Int.add littleOption bigOption == Some 14)\n assert (Option.andThen (\\x -> Option.Some x) littleOption == littleOption)\n assert (Option.isSome bigOption)\n assert (Option.apply (Some Builtin.unIData) bigOption == Some 7)\n test \"option helpers preserve payloads 2\" = do\n assert (not (Option.isSome none))\n assert (Option.unwrap bigOption == bigInt)\n assert (Option.withDefault bigInt bigOption == bigInt)\n test \"unwrap None\" fail = do\n assert (Option.unwrap none == 0)\n test \"Big scalar control inputs\" = do\n assert (List.take bigInt [1, 2] == [1, 2])\n assert (Int.pow 2 bigInt == 128)\n assert (Int.toBytes Bool.True bigInt 1 == #\"00000000000001\")\n assert (Bytes.or Bool.True #\"00\" #\"ff\" == #\"ff\")\n test \"Big array indices\" = do\n assert (Array.at (Array.fromList [5, 6]) (Builtin.iData 1) == 6)\n assert (Array.get (Array.fromList [5, 6]) bigInt == None)\n" --- mixed booleans and ordering: Pass; traces: [] mixed booleans and ordering 2: Pass; traces: [] mixed booleans and ordering 3: Pass; traces: [] integer helpers: Pass; traces: [] +exact powers of two across byte boundaries: Pass; traces: [] +negative power of two exponent: Pass; traces: [] integer helpers 2: Pass; traces: [] negative exponent: Pass; traces: [] negative integer encoding: Pass; traces: [] diff --git a/crates/nash-driver/tests/snapshots/testing_base__integer_width_bound_costs.snap b/crates/nash-driver/tests/snapshots/testing_base__integer_width_bound_costs.snap new file mode 100644 index 00000000..42263235 --- /dev/null +++ b/crates/nash-driver/tests/snapshots/testing_base__integer_width_bound_costs.snap @@ -0,0 +1,22 @@ +--- +source: crates/nash-driver/tests/testing_base.rs +description: " assert ((Int.pow 2 1 - 1) == 1)\n assert ((Int.pow2 1 - 1) == 1)\n assert ((1 * Builtin.expModInteger 2 1 257 - 1) == 1)\n assert ((Int.pow 2 4 - 1) == 15)\n assert ((Int.pow2 4 - 1) == 15)\n assert ((1 * Builtin.expModInteger 2 4 257 - 1) == 15)\n assert ((Int.pow 2 8 - 1) == 255)\n assert ((Int.pow2 8 - 1) == 255)\n assert ((1 * Builtin.expModInteger 2 8 257 - 1) == 255)\n assert ((Int.pow 2 16 - 1) == 65535)\n assert ((Int.pow2 16 - 1) == 65535)\n assert ((256 * Builtin.expModInteger 2 8 257 - 1) == 65535)\n assert ((Int.pow 2 64 - 1) == 18446744073709551615)\n assert ((Int.pow2 64 - 1) == 18446744073709551615)\n assert ((72057594037927936 * Builtin.expModInteger 2 8 257 - 1) == 18446744073709551615)\n assert ((Int.pow 2 120 - 1) == 1329227995784915872903807060280344575)\n assert ((Int.pow2 120 - 1) == 1329227995784915872903807060280344575)\n assert ((5192296858534827628530496329220096 * Builtin.expModInteger 2 8 257 - 1) == 1329227995784915872903807060280344575)" +--- +width=1 pow: cpu=4344881 memory=19508 +width=1 pow2: cpu=3956548 memory=17407 +width=1 expMod: cpu=2096588 memory=6106 +width=4 pow: cpu=4344881 memory=19508 +width=4 pow2: cpu=3956548 memory=17407 +width=4 expMod: cpu=2096588 memory=6106 +width=8 pow: cpu=4493214 memory=20109 +width=8 pow2: cpu=4104881 memory=18008 +width=8 expMod: cpu=2096588 memory=6106 +width=16 pow: cpu=8474416 memory=37128 +width=16 pow2: cpu=8086083 memory=35027 +width=16 expMod: cpu=2096588 memory=6106 +width=64 pow: cpu=11911700 memory=51745 +width=64 pow2: cpu=11523367 memory=49644 +width=64 expMod: cpu=2097008 memory=6107 +width=120 pow: cpu=13476256 memory=58652 +width=120 pow2: cpu=13087923 memory=56551 +width=120 expMod: cpu=2098085 memory=6108 diff --git a/crates/nash-driver/tests/testing_base.rs b/crates/nash-driver/tests/testing_base.rs index 9a73c06d..d75d4a9e 100644 --- a/crates/nash-driver/tests/testing_base.rs +++ b/crates/nash-driver/tests/testing_base.rs @@ -98,7 +98,6 @@ async fn generation_functions_thread_choices() { ("Prop.int", "two (c 0) (c 42)", "42"), ("Prop.int", "two (c 1) (c 65535)", "32767"), ("Prop.int", "two (c 1) (c 0)", "(-32768)"), - ("Prop.int", "two (c 2) (g (one (g (one (c 0)))))", "0"), ( "Prop.oneOf (Cons.Cons (Prop.constant 0) (Cons.Cons (Prop.choice 10) Cons.Nil))", "two (c 1) (g (one (c 7)))", @@ -181,3 +180,50 @@ async fn test_protocol_logs_survive_silent_user_traces() { assert_eq!(result.info.logs, expected); } } + +#[tokio::test] +async fn integer_width_bound_costs() { + let mut report = String::new(); + let mut sources = Vec::new(); + for width in [1_u32, 4, 8, 16, 64, 120] { + let local = (width - 1) % 8 + 1; + let scale = 1_i128 << (width - local); + let expected = (1_i128 << width) - 1; + for (name, expression) in [ + ("pow", format!("Int.pow 2 {width} - 1")), + ("pow2", format!("Int.pow2 {width} - 1")), + ( + "expMod", + format!("{scale} * Builtin.expModInteger 2 {local} 257 - 1"), + ), + ] { + let source = format!(" assert (({expression}) == {expected})"); + let output = compile(&source).await; + let arena = Arena::new(); + let program = syn::parse_program(&arena, &output.uplc).unwrap(); + let result = program + .apply( + &arena, + Term::data( + &arena, + nash_plutus::data::PlutusData::integer_from(&arena, 0), + ), + ) + .eval(&arena); + assert!( + result.term.is_ok(), + "{name} width {width}: {:?}", + result.term + ); + let budget = result.info.consumed_budget; + report.push_str(&format!( + "width={width} {name}: cpu={} memory={}\n", + budget.cpu, budget.mem + )); + sources.push(source); + } + } + insta::with_settings!({description => sources.join("\n"), omit_expression => true}, { + insta::assert_snapshot!(report); + }); +} diff --git a/docs/stdlib.md b/docs/stdlib.md index c0c802eb..b060eb94 100644 --- a/docs/stdlib.md +++ b/docs/stdlib.md @@ -671,6 +671,7 @@ min : (Lift int 'a, Lift int 'b) => 'a -> 'b -> int max : (Lift int 'a, Lift int 'b) => 'a -> 'b -> int abs : Lift int 'n => 'n -> int pow : (Lift int 'n, Lift int 'e) => 'n -> 'e -> int +pow2 : Lift int 'e => 'e -> int powMod : (Lift int 'n, Lift int 'e, Lift int 'm) => 'n -> 'e -> 'm -> int toBytes : (Lift bool 'b, Lift int 's, Lift int 'n) => 'b -> 's -> 'n -> bytes fromBytes : (Lift bool 'b, Lift bytes 'v) => 'b -> 'v -> int @@ -770,7 +771,11 @@ any is False and empty all is True. Sum computes an int for native or Big intege Predicates, comparators, and filterMap callbacks accept either outer representation. Fold callbacks take item then accumulator. Native dropList handles list skipping. -Int.pow rejects negative exponents and returns 1 for exponent zero. Int.powMod +Int.pow and Int.pow2 reject negative exponents and return 1 for exponent zero. +Int.pow2 computes exact powers of two: local powers through eight bits use +expModInteger with modulus 257; larger exponents combine that result with a +power of 256. Int.pow uses this path for base 2 and exponentiation by squaring +for other bases. These helpers impose no fixed result-size limit. Int.powMod uses Plutus expModInteger semantics, including its invalid-modulus and modular inverse failures. Int.toBytes rejects negative values, invalid sizes and values that do not fit. Bytes.at/readBit/writeBits fail for invalid indices; slice @@ -995,9 +1000,13 @@ smaller values (testing.md "Shrinking"): avoiding clamping or wrapping the final chunk. Primitive draws retain the existing hash-modulo distribution. Equal endpoints consume no choices. - `int` selects one of three branches: 0..255, signed 16-bit, or an - arbitrary-precision signed magnitude. The magnitude stops with probability - 3/4 at each step, otherwise drawing another 64-bit chunk. Zero needs no sign - choice. `intAtLeast lo` adds such a nonnegative magnitude to `lo`. + arbitrary-precision signed magnitude. The magnitude first selects a bit width: 75% in 1–8, 18.75% + in 9–16, and successively wider eight-bit bands with probability decreasing + by a factor of four. Each width within a band is equally likely. It then + samples from zero through 2^width - 1; zero has roughly 9% probability, + rather than being the stopping result. Zero needs no sign choice. `intAtLeast lo` adds such a nonnegative magnitude to `lo`. + The local 1–8-bit power uses `Int.pow2`, backed by `expModInteger 2 bits 257`, which is exact + because the result is at most 256. Wider bands multiply the scale by 256. There is no fixed integer-size ceiling; VM budgets still apply. - never consume choices on a path that cannot fail differently. diff --git a/plans/12-stdlib.md b/plans/12-stdlib.md index a659c346..759ced81 100644 --- a/plans/12-stdlib.md +++ b/plans/12-stdlib.md @@ -339,11 +339,19 @@ snapshots show listOf int reducing to both `[]` and `[0]`, and a wide-range counterexample reducing to its lower bound. Existing protocol tests continue to exercise seeded/replayed choices, malformed traces and the Rust runner. +Nonnegative offsets use small-biased bit widths rather than zero-or-u64 chunks. +A seeded 256-draw snapshot checks zero, small nonzero and larger offsets; explicit +replay cases cover 1, 2, 10, 100, 255, 256 and values beyond u64. +CEK measurements showed exact modular powers cheaper than the previous +Int.pow implementation. Int.pow2 now provides that reusable exact operation, +and Int.pow selects it for base 2. A budget snapshot covers both public helpers +and direct modular bound expressions. Prop uses Int.pow2 for its local width. + The generator distribution changed for the wide branch of Prop.int; older recordings using its previous signed-64-bit layout are not replay-compatible. No PRNG wire tags, runner protocol, reducer passes or codegen behavior changed. -Validation: formatting and strict Clippy passed; 3,444 workspace tests passed, +Validation: formatting and strict Clippy passed; 3,445 workspace tests passed, 3 ignored. --- From 80fbf7435cec0556f21ca503218e5bf208adabf0 Mon Sep 17 00:00:00 2001 From: microproofs Date: Fri, 25 Sep 2026 12:36:54 -0400 Subject: [PATCH 03/10] refactor(base): use power table and drop filtering Signed-off-by: microproofs --- .sampo/changesets/int-power-table.md | 5 +++ .sampo/changesets/prop-generators.md | 2 +- .sampo/changesets/remove-prop-filtering.md | 5 +++ crates/nash-driver/base/src/Int.nash | 8 ++-- crates/nash-driver/base/src/Prop.nash | 12 +----- .../fixtures/base-traits/PropHelpers.nash | 15 -------- .../fixtures/base-traits/TypeHelpers.nash | 2 + .../snapshots/base_traits__prop_helpers.snap | 5 +-- .../snapshots/base_traits__type_helpers.snap | 3 +- ...sting_base__integer_width_bound_costs.snap | 38 ++++++++++++------- crates/nash-driver/tests/testing_base.rs | 12 ++++++ docs/stdlib.md | 16 +++----- plans/12-stdlib.md | 13 ++++--- 13 files changed, 71 insertions(+), 65 deletions(-) create mode 100644 .sampo/changesets/int-power-table.md create mode 100644 .sampo/changesets/remove-prop-filtering.md diff --git a/.sampo/changesets/int-power-table.md b/.sampo/changesets/int-power-table.md new file mode 100644 index 00000000..f283e1ea --- /dev/null +++ b/.sampo/changesets/int-power-table.md @@ -0,0 +1,5 @@ +--- +cargo/nash-driver: patch +--- + +Use a compile-time constant array for small powers of two in Int.pow2 and the base-two Int.pow path. Budget snapshots compare runtime arrays, constant arrays and modular exponentiation; constant lookup uses less CPU and memory without runtime table construction. diff --git a/.sampo/changesets/prop-generators.md b/.sampo/changesets/prop-generators.md index 8dad6608..63e19b51 100644 --- a/.sampo/changesets/prop-generators.md +++ b/.sampo/changesets/prop-generators.md @@ -2,4 +2,4 @@ cargo/nash-driver: minor --- -Complete Prop generators with booleans, options, bounded and exact bytes, weighted alternatives, bounded filtering, and arbitrary-precision integer generation. Normalize Big/little bounds and weights, preserve strict replay groups, and express tuple2 through generator do. Add source snapshots for range properties, retry boundaries, and reduced counterexamples. +Complete Prop generators with booleans, options, bounded and exact bytes, weighted alternatives, and arbitrary-precision integer generation. Normalize Big/little bounds and weights, preserve strict replay groups, and express tuple2 through generator do. Add source snapshots for range properties and reduced counterexamples. diff --git a/.sampo/changesets/remove-prop-filtering.md b/.sampo/changesets/remove-prop-filtering.md new file mode 100644 index 00000000..7834f636 --- /dev/null +++ b/.sampo/changesets/remove-prop-filtering.md @@ -0,0 +1,5 @@ +--- +cargo/nash-driver: patch +--- + +Remove predicate-filtered generators and their bounded retry loop, fixtures, and documentation. Build valid values directly through generator composition. diff --git a/crates/nash-driver/base/src/Int.nash b/crates/nash-driver/base/src/Int.nash index 3cfc13b2..cf50c99a 100644 --- a/crates/nash-driver/base/src/Int.nash +++ b/crates/nash-driver/base/src/Int.nash @@ -81,14 +81,16 @@ pow2 exponent = e = lower exponent in if e < 0 then fail else powerOfTwo e +smallPowersOfTwo : array int +smallPowersOfTwo = comptime (Builtin.listToArray [1, 2, 4, 8, 16, 32, 64, 128, 256]) + powerOfTwo : int -> int powerOfTwo e = - -- Local powers are at most 256, so modulus 257 leaves them unchanged. - if e <= 8 then Builtin.expModInteger 2 e 257 + if e <= 8 then Builtin.indexArray smallPowersOfTwo e else let bands = quot (e - 1) 8 bits = rem (e - 1) 8 + 1 - in power 256 bands * Builtin.expModInteger 2 bits 257 + in power 256 bands * Builtin.indexArray smallPowersOfTwo bits powMod : (Lift int 'n, Lift int 'e, Lift int 'm) => 'n -> 'e -> 'm -> int powMod n e m = Builtin.expModInteger (lower n) (lower e) (lower m) diff --git a/crates/nash-driver/base/src/Prop.nash b/crates/nash-driver/base/src/Prop.nash index c35f3744..f2ae5020 100644 --- a/crates/nash-driver/base/src/Prop.nash +++ b/crates/nash-driver/base/src/Prop.nash @@ -1,4 +1,4 @@ -module Prop exposing (type prng(..), type choiceTree(..), type generator, group, choice, constant, intBetween, int, listOf, listBetween, tuple2, oneOf, bytes, bool, option, bytesBetween, bytesExactly, frequency, suchThat, intAtLeast) +module Prop exposing (type prng(..), type choiceTree(..), type generator, group, choice, constant, intBetween, int, listOf, listBetween, tuple2, oneOf, bytes, bool, option, bytesBetween, bytesExactly, frequency, intAtLeast) import Builtin import Int @@ -252,16 +252,6 @@ weighted index entries = case entries of if index < n then group item else weighted (index - n) rest -suchThat : ('a -> bool) -> generator 'a -> generator 'a -suchThat predicate item = retry 100 predicate item - -retry : int -> ('a -> bool) -> generator 'a -> generator 'a -retry remaining predicate item state = - if remaining == 0 then None - else do - (value, next) <- group item state - if predicate value then Some (value, next) else retry (remaining - 1) predicate item next - packBytes : list int -> bytes packBytes values = case values of diff --git a/crates/nash-driver/tests/fixtures/base-traits/PropHelpers.nash b/crates/nash-driver/tests/fixtures/base-traits/PropHelpers.nash index fdd88a12..d772802a 100644 --- a/crates/nash-driver/tests/fixtures/base-traits/PropHelpers.nash +++ b/crates/nash-driver/tests/fixtures/base-traits/PropHelpers.nash @@ -22,10 +22,6 @@ bigWeight = 2 bigCount : Int bigCount = 3 -attempts n = - if n == 0 then one (g (one (c 1))) - else Cons (g (one (c 0))) (attempts (n - 1)) - byteDraw n = g (one (g (one (c n)))) rebuild choices = Prop.Rebuilding choices Nil @@ -109,17 +105,6 @@ tests test "negative weights fail even after a positive entry" fail = do case Prop.frequency (Cons (1, Prop.constant 1) (one (-1, Prop.constant 2))) of _ -> () - test "filter retries recorded groups and keeps remaining siblings" = do - case Prop.suchThat (\n -> n > 0) (Prop.choice 9) (replay (Cons (g (one (c 0))) (Cons (g (one (c 4))) (one (c 8))))) of - Some (4, Prop.Replayed (Cons (Prop.Choice 8) Nil) _) -> () - _ -> fail - assert (value (Prop.suchThat (\n -> n > 0) (Prop.choice 9)) (replay (one (g (one (c 0))))) == None) - assert (value (Prop.suchThat (\_ -> False) (Prop.constant 1)) seed == None) - test "filter allows exactly 100 attempts" = do - assert (value (Prop.suchThat (\n -> n == 1) (Prop.choice 1)) (replay (attempts 99)) == Some 1) - assert (value (Prop.suchThat (\n -> n == 1) (Prop.choice 1)) (replay (attempts 100)) == None) - test "filter cannot borrow choices from the next attempt" = do - assert (value (Prop.suchThat (\_ -> True) (Prop.choice 1)) (replay (two (g Nil) (g (one (c 1))))) == None) test "exact byte replay includes both endpoints" = do assert (value (Prop.bytesExactly 2) (replay (two (byteDraw 0) (byteDraw 255))) == Some #"00ff") assert (value (Prop.bytesExactly 2) (replay (one (byteDraw 0))) == None) diff --git a/crates/nash-driver/tests/fixtures/base-traits/TypeHelpers.nash b/crates/nash-driver/tests/fixtures/base-traits/TypeHelpers.nash index 2ad7ad8a..1a35b5b2 100644 --- a/crates/nash-driver/tests/fixtures/base-traits/TypeHelpers.nash +++ b/crates/nash-driver/tests/fixtures/base-traits/TypeHelpers.nash @@ -36,6 +36,8 @@ tests assert (Int.abs bigInt == 7) assert (Int.pow 2 10 == 1024) assert (Int.pow bigInt 0 == 1) + test "every small power lookup" = do + assert (List.map Int.pow2 (List.range 0 9) == [1, 2, 4, 8, 16, 32, 64, 128, 256]) test "exact powers of two across byte boundaries" = do assert (Int.pow2 0 == 1) assert (Int.pow2 bigInt == 128) diff --git a/crates/nash-driver/tests/snapshots/base_traits__prop_helpers.snap b/crates/nash-driver/tests/snapshots/base_traits__prop_helpers.snap index 68886bbe..f88296f1 100644 --- a/crates/nash-driver/tests/snapshots/base_traits__prop_helpers.snap +++ b/crates/nash-driver/tests/snapshots/base_traits__prop_helpers.snap @@ -1,6 +1,6 @@ --- source: crates/nash-driver/tests/base_traits.rs -description: "module Main exposing (..)\n\nimport Cons exposing (type cons(..))\n\nreplay choices = Prop.Replayed choices Nil\nc = Prop.Choice\ng = Prop.Group\none x = Cons x Nil\ntwo x y = Cons x (one y)\n\nvalue generator state = case generator state of\n Some (result, _) -> Some result\n None -> None\n\nseed = Prop.Seeded #\"\" Nil\nweights : cons (int, Prop.generator int)\nweights = Cons (0, \\_ -> fail) (Cons (3, Prop.constant 10) (Cons (1, Prop.constant 20) Nil))\nemptyWeights : cons (int, Prop.generator int)\nemptyWeights = Nil\nbigWeight : Int\nbigWeight = 2\nbigCount : Int\nbigCount = 3\n\nattempts n =\n if n == 0 then one (g (one (c 1)))\n else Cons (g (one (c 0))) (attempts (n - 1))\n\nbyteDraw n = g (one (g (one (c n))))\n\nrebuild choices = Prop.Rebuilding choices Nil\nhuge : Int\nhuge = lift (18446744073709551616 * 18446744073709551616)\n\nhugeLittle : int\nhugeLittle = lower huge\nnever : Prop.generator int\nnever _ = fail\n\nsampleOffsets : int -> Prop.prng -> (int, int, int)\nsampleOffsets count state =\n if count == 0 then (0, 0, 0)\n else case Prop.intAtLeast 0 state of\n None -> fail\n Some (n, next) ->\n let (zeros, small, large) = sampleOffsets (count - 1) next in\n if n == 0 then (zeros + 1, small, large)\n else if n <= 255 then (zeros, small + 1, large)\n else (zeros, small, large + 1)\n\ntests\n test \"small widths produce useful nonzero offsets\" = do\n assert (value (Prop.intAtLeast 0) (rebuild [0, 0, 1]) == Some 1)\n assert (value (Prop.intAtLeast 0) (rebuild [0, 1, 2]) == Some 2)\n assert (value (Prop.intAtLeast 0) (rebuild [0, 3, 10]) == Some 10)\n assert (value (Prop.intAtLeast 0) (rebuild [0, 6, 100]) == Some 100)\n assert (value (Prop.intAtLeast 0) (rebuild [0, 7, 255]) == Some 255)\n assert (value (Prop.intAtLeast 0) (rebuild [3, 0, 0, 256]) == Some 256)\n test \"seeded offsets favor small nonzero values\" = do\n let (zeros, small, large) = sampleOffsets 256 seed in\n assert (zeros < 64 && small > 128 && large > 0)\n Builtin.trace (show (sampleOffsets 256 seed)) ()\n test \"wide range strict replay isolates rejected attempts\" = do\n assert (value (Prop.intBetween 0 18446744073709551616) (replay (Cons (g (one (c 1))) (Cons (g (one (c 1))) (two (g (one (c 1))) (g (one (c 0))))))) == Some 18446744073709551616)\n test \"weight totals can exceed one choice\" = do\n let options = two (18446744073709551616, Prop.constant 10) (1, Prop.constant 20) in\n assert (value (Prop.frequency options) (rebuild [1, 0]) == Some 20)\n let options = two (18446744073709551616, Prop.constant 10) (1, Prop.constant 20) in\n assert (value (Prop.frequency options) (rebuild [0, 18446744073709551615]) == Some 10)\n test \"reversed byte lengths fail\" fail = do\n case Prop.bytesBetween 3 2 of\n _ -> ()\n prop \"wide range counterexample replays and reduces\" fail once =\n let n via Prop.intBetween hugeLittle (hugeLittle * 2) in\n do\n assert (n < 0)\n test \"arbitrary integer offsets preserve large lower bounds\" = do\n assert (value (Prop.intAtLeast huge) (rebuild [0, 0, 0]) == Some (lower huge))\n assert (value (Prop.intAtLeast (-hugeLittle)) (rebuild [3, 3, 3, 3, 3, 3, 3, 3, 0, 0, 1, 0]) == Some (18446744073709551616 - lower huge))\n assert (value Prop.int (rebuild [2, 3, 3, 3, 3, 3, 3, 3, 3, 0, 0, 1, 0, 0]) == Some 18446744073709551616)\n assert (value Prop.int (rebuild [2, 3, 3, 3, 3, 3, 3, 3, 3, 0, 0, 1, 0, 1]) == Some (-18446744073709551616))\n test \"wide bounded generation rejects overshoot then retries\" = do\n assert (value (Prop.intBetween 0 18446744073709551616) (rebuild [1, 1, 1, 0]) == Some 18446744073709551616)\n assert (value (Prop.intBetween 0 18446744073709551616) (rebuild [1, 1]) == None)\n prop \"arbitrary integer lower bound\" =\n let n via Prop.intAtLeast huge in\n do\n assert (n >= lower huge)\n test \"boolean choices\" = do\n assert (value Prop.bool (replay (one (c 0))) == Some False)\n assert (value Prop.bool (replay (one (c 1))) == Some True)\n test \"optional draw preserves absence and groups the payload\" = do\n assert (value (Prop.option never) (replay (one (g (one (c 0))))) == Some None)\n assert (value (Prop.option (Prop.choice 9)) (replay (two (g (one (c 1))) (g (one (c 7))))) == Some (Some 7))\n test \"tuple bind preserves groups and remaining siblings\" = do\n case Prop.tuple2 (Prop.choice 9) (Prop.choice 9) (replay (Cons (g (one (c 4))) (Cons (g (one (c 5))) (one (c 8))))) of\n Some ((4, 5), Prop.Replayed rest used) ->\n case (rest, used) of\n (Cons (Prop.Choice 8) Nil, Cons (Prop.Group (Cons (Prop.Choice 5) Nil)) (Cons (Prop.Group (Cons (Prop.Choice 4) Nil)) Nil)) -> ()\n _ -> fail\n _ -> fail\n test \"weighted endpoints skip zero weights\" = do\n assert (value (Prop.frequency weights) (replay (two (c 0) (g Nil))) == Some 10)\n assert (value (Prop.frequency weights) (replay (two (c 2) (g Nil))) == Some 10)\n assert (value (Prop.frequency weights) (replay (two (c 3) (g Nil))) == Some 20)\n assert (value (Prop.frequency (one (bigWeight, Prop.constant 9))) seed == Some 9)\n assert (value (Prop.frequency emptyWeights) seed == None)\n assert (value (Prop.frequency (one (0, Prop.constant 9))) seed == None)\n test \"negative weights fail even after a positive entry\" fail = do\n case Prop.frequency (Cons (1, Prop.constant 1) (one (-1, Prop.constant 2))) of\n _ -> ()\n test \"filter retries recorded groups and keeps remaining siblings\" = do\n case Prop.suchThat (\\n -> n > 0) (Prop.choice 9) (replay (Cons (g (one (c 0))) (Cons (g (one (c 4))) (one (c 8))))) of\n Some (4, Prop.Replayed (Cons (Prop.Choice 8) Nil) _) -> ()\n _ -> fail\n assert (value (Prop.suchThat (\\n -> n > 0) (Prop.choice 9)) (replay (one (g (one (c 0))))) == None)\n assert (value (Prop.suchThat (\\_ -> False) (Prop.constant 1)) seed == None)\n test \"filter allows exactly 100 attempts\" = do\n assert (value (Prop.suchThat (\\n -> n == 1) (Prop.choice 1)) (replay (attempts 99)) == Some 1)\n assert (value (Prop.suchThat (\\n -> n == 1) (Prop.choice 1)) (replay (attempts 100)) == None)\n test \"filter cannot borrow choices from the next attempt\" = do\n assert (value (Prop.suchThat (\\_ -> True) (Prop.choice 1)) (replay (two (g Nil) (g (one (c 1))))) == None)\n test \"exact byte replay includes both endpoints\" = do\n assert (value (Prop.bytesExactly 2) (replay (two (byteDraw 0) (byteDraw 255))) == Some #\"00ff\")\n assert (value (Prop.bytesExactly 2) (replay (one (byteDraw 0))) == None)\n test \"exact bytes normalize Big counts\" = do\n case value (Prop.bytesExactly bigCount) seed of\n Some bytes -> assert (Bytes.length bytes == 3)\n None -> fail\n assert (value (Prop.bytesExactly 0) (replay Nil) == Some #\"\")\n test \"negative byte count fails\" fail = do\n case Prop.bytesExactly -1 of\n _ -> ()\n prop \"byte length in bounds\" =\n let bytes via Prop.bytesBetween 2 7 in\n do\n assert (Bytes.length bytes >= 2 && Bytes.length bytes <= 7)\n prop \"generated integer bounds\" =\n let lo via Prop.int\n span via Prop.intBetween 0 1000\n in\n do\n case value (Prop.intBetween lo (lo + span)) seed of\n Some n -> assert (n >= lo && n <= lo + span)\n None -> fail\n prop \"listOf reduces to empty\" fail once =\n let items via Prop.listOf Prop.int in\n do\n assert (List.length items < 0)\n prop \"listOf reduces to one zero\" fail once =\n let items via Prop.listOf Prop.int in\n do\n assert (List.length items == 0)\n" +description: "module Main exposing (..)\n\nimport Cons exposing (type cons(..))\n\nreplay choices = Prop.Replayed choices Nil\nc = Prop.Choice\ng = Prop.Group\none x = Cons x Nil\ntwo x y = Cons x (one y)\n\nvalue generator state = case generator state of\n Some (result, _) -> Some result\n None -> None\n\nseed = Prop.Seeded #\"\" Nil\nweights : cons (int, Prop.generator int)\nweights = Cons (0, \\_ -> fail) (Cons (3, Prop.constant 10) (Cons (1, Prop.constant 20) Nil))\nemptyWeights : cons (int, Prop.generator int)\nemptyWeights = Nil\nbigWeight : Int\nbigWeight = 2\nbigCount : Int\nbigCount = 3\n\nbyteDraw n = g (one (g (one (c n))))\n\nrebuild choices = Prop.Rebuilding choices Nil\nhuge : Int\nhuge = lift (18446744073709551616 * 18446744073709551616)\n\nhugeLittle : int\nhugeLittle = lower huge\nnever : Prop.generator int\nnever _ = fail\n\nsampleOffsets : int -> Prop.prng -> (int, int, int)\nsampleOffsets count state =\n if count == 0 then (0, 0, 0)\n else case Prop.intAtLeast 0 state of\n None -> fail\n Some (n, next) ->\n let (zeros, small, large) = sampleOffsets (count - 1) next in\n if n == 0 then (zeros + 1, small, large)\n else if n <= 255 then (zeros, small + 1, large)\n else (zeros, small, large + 1)\n\ntests\n test \"small widths produce useful nonzero offsets\" = do\n assert (value (Prop.intAtLeast 0) (rebuild [0, 0, 1]) == Some 1)\n assert (value (Prop.intAtLeast 0) (rebuild [0, 1, 2]) == Some 2)\n assert (value (Prop.intAtLeast 0) (rebuild [0, 3, 10]) == Some 10)\n assert (value (Prop.intAtLeast 0) (rebuild [0, 6, 100]) == Some 100)\n assert (value (Prop.intAtLeast 0) (rebuild [0, 7, 255]) == Some 255)\n assert (value (Prop.intAtLeast 0) (rebuild [3, 0, 0, 256]) == Some 256)\n test \"seeded offsets favor small nonzero values\" = do\n let (zeros, small, large) = sampleOffsets 256 seed in\n let _ = assert (zeros < 64 && small > 128 && large > 0) in\n Builtin.trace (show (zeros, small, large)) ()\n test \"wide range strict replay isolates rejected attempts\" = do\n assert (value (Prop.intBetween 0 18446744073709551616) (replay (Cons (g (one (c 1))) (Cons (g (one (c 1))) (two (g (one (c 1))) (g (one (c 0))))))) == Some 18446744073709551616)\n test \"weight totals can exceed one choice\" = do\n let options = two (18446744073709551616, Prop.constant 10) (1, Prop.constant 20) in\n assert (value (Prop.frequency options) (rebuild [1, 0]) == Some 20)\n let options = two (18446744073709551616, Prop.constant 10) (1, Prop.constant 20) in\n assert (value (Prop.frequency options) (rebuild [0, 18446744073709551615]) == Some 10)\n test \"reversed byte lengths fail\" fail = do\n case Prop.bytesBetween 3 2 of\n _ -> ()\n prop \"wide range counterexample replays and reduces\" fail once =\n let n via Prop.intBetween hugeLittle (hugeLittle * 2) in\n do\n assert (n < 0)\n test \"arbitrary integer offsets preserve large lower bounds\" = do\n assert (value (Prop.intAtLeast huge) (rebuild [0, 0, 0]) == Some (lower huge))\n assert (value (Prop.intAtLeast (-hugeLittle)) (rebuild [3, 3, 3, 3, 3, 3, 3, 3, 0, 0, 1, 0]) == Some (18446744073709551616 - lower huge))\n assert (value Prop.int (rebuild [2, 3, 3, 3, 3, 3, 3, 3, 3, 0, 0, 1, 0, 0]) == Some 18446744073709551616)\n assert (value Prop.int (rebuild [2, 3, 3, 3, 3, 3, 3, 3, 3, 0, 0, 1, 0, 1]) == Some (-18446744073709551616))\n test \"wide bounded generation rejects overshoot then retries\" = do\n assert (value (Prop.intBetween 0 18446744073709551616) (rebuild [1, 1, 1, 0]) == Some 18446744073709551616)\n assert (value (Prop.intBetween 0 18446744073709551616) (rebuild [1, 1]) == None)\n prop \"arbitrary integer lower bound\" =\n let n via Prop.intAtLeast huge in\n do\n assert (n >= lower huge)\n test \"boolean choices\" = do\n assert (value Prop.bool (replay (one (c 0))) == Some False)\n assert (value Prop.bool (replay (one (c 1))) == Some True)\n test \"optional draw preserves absence and groups the payload\" = do\n assert (value (Prop.option never) (replay (one (g (one (c 0))))) == Some None)\n assert (value (Prop.option (Prop.choice 9)) (replay (two (g (one (c 1))) (g (one (c 7))))) == Some (Some 7))\n test \"tuple bind preserves groups and remaining siblings\" = do\n case Prop.tuple2 (Prop.choice 9) (Prop.choice 9) (replay (Cons (g (one (c 4))) (Cons (g (one (c 5))) (one (c 8))))) of\n Some ((4, 5), Prop.Replayed rest used) ->\n case (rest, used) of\n (Cons (Prop.Choice 8) Nil, Cons (Prop.Group (Cons (Prop.Choice 5) Nil)) (Cons (Prop.Group (Cons (Prop.Choice 4) Nil)) Nil)) -> ()\n _ -> fail\n _ -> fail\n test \"weighted endpoints skip zero weights\" = do\n assert (value (Prop.frequency weights) (replay (two (c 0) (g Nil))) == Some 10)\n assert (value (Prop.frequency weights) (replay (two (c 2) (g Nil))) == Some 10)\n assert (value (Prop.frequency weights) (replay (two (c 3) (g Nil))) == Some 20)\n assert (value (Prop.frequency (one (bigWeight, Prop.constant 9))) seed == Some 9)\n assert (value (Prop.frequency emptyWeights) seed == None)\n assert (value (Prop.frequency (one (0, Prop.constant 9))) seed == None)\n test \"negative weights fail even after a positive entry\" fail = do\n case Prop.frequency (Cons (1, Prop.constant 1) (one (-1, Prop.constant 2))) of\n _ -> ()\n test \"exact byte replay includes both endpoints\" = do\n assert (value (Prop.bytesExactly 2) (replay (two (byteDraw 0) (byteDraw 255))) == Some #\"00ff\")\n assert (value (Prop.bytesExactly 2) (replay (one (byteDraw 0))) == None)\n test \"exact bytes normalize Big counts\" = do\n case value (Prop.bytesExactly bigCount) seed of\n Some bytes -> assert (Bytes.length bytes == 3)\n None -> fail\n assert (value (Prop.bytesExactly 0) (replay Nil) == Some #\"\")\n test \"negative byte count fails\" fail = do\n case Prop.bytesExactly -1 of\n _ -> ()\n prop \"byte length in bounds\" =\n let bytes via Prop.bytesBetween 2 7 in\n do\n assert (Bytes.length bytes >= 2 && Bytes.length bytes <= 7)\n prop \"generated integer bounds\" =\n let lo via Prop.int\n span via Prop.intBetween 0 1000\n in\n do\n case value (Prop.intBetween lo (lo + span)) seed of\n Some n -> assert (n >= lo && n <= lo + span)\n None -> fail\n prop \"listOf reduces to empty\" fail once =\n let items via Prop.listOf Prop.int in\n do\n assert (List.length items < 0)\n prop \"listOf reduces to one zero\" fail once =\n let items via Prop.listOf Prop.int in\n do\n assert (List.length items == 0)\n" --- small widths produce useful nonzero offsets: Pass; traces: [] seeded offsets favor small nonzero values: Pass; traces: ["(26, 169, 61)"] @@ -18,9 +18,6 @@ optional draw preserves absence and groups the payload: Pass; traces: [] tuple bind preserves groups and remaining siblings: Pass; traces: [] weighted endpoints skip zero weights: Pass; traces: [] negative weights fail even after a positive entry: Pass; traces: ["Prop.frequency requires nonnegative weights"] -filter retries recorded groups and keeps remaining siblings: Pass; traces: [] -filter allows exactly 100 attempts: Pass; traces: [] -filter cannot borrow choices from the next attempt: Pass; traces: [] exact byte replay includes both endpoints: Pass; traces: [] exact bytes normalize Big counts: Pass; traces: [] negative byte count fails: Pass; traces: ["Prop.listBetween has invalid bounds"] diff --git a/crates/nash-driver/tests/snapshots/base_traits__type_helpers.snap b/crates/nash-driver/tests/snapshots/base_traits__type_helpers.snap index 7df96ef4..f3f1ea8e 100644 --- a/crates/nash-driver/tests/snapshots/base_traits__type_helpers.snap +++ b/crates/nash-driver/tests/snapshots/base_traits__type_helpers.snap @@ -1,11 +1,12 @@ --- source: crates/nash-driver/tests/base_traits.rs -description: "module Main exposing (..)\n\nbigInt : Int\nbigInt = 7\nbigBytes : Bytes\nbigBytes = #\"abcd\"\nbigIndices : List Int\nbigIndices = lift [Builtin.iData 0, Builtin.iData 8]\nbigOption : Option Int\nbigOption = Option.Some 7\nlittleOption : option Int\nlittleOption = Some 7\nnone : option int\nnone = None\nboth : (Lift bool 'a, Lift bool 'b) => 'a -> 'b -> bool\nboth a b = Bool.and a b\nbadBig : unit -> Bool\nbadBig _ = fail\n\ntests\n test \"mixed booleans and ordering\" = do\n assert (Bool.and Bool.True True)\n assert (Bool.or False Bool.True)\n assert (not (Bool.xor Bool.True True))\n assert (not (Bool.not Bool.True))\n test \"mixed booleans and ordering 2\" = do\n assert (Bool.or Bool.True (badBig ()))\n assert (not (Bool.and False (badBig ())))\n assert (Bool.True || badBig ())\n assert (both Bool.True True)\n test \"mixed booleans and ordering 3\" = do\n assert (Ordering.invert Ordering.LT == GT)\n assert (Ordering.then_ EQ Ordering.GT == GT)\n test \"integer helpers\" = do\n assert (Int.abs (-7) == 7)\n assert (Int.abs bigInt == 7)\n assert (Int.pow 2 10 == 1024)\n assert (Int.pow bigInt 0 == 1)\n test \"exact powers of two across byte boundaries\" = do\n assert (Int.pow2 0 == 1)\n assert (Int.pow2 bigInt == 128)\n assert (Int.pow2 8 == 256)\n assert (Int.pow2 9 == 512)\n assert (Int.pow2 64 == 18446744073709551616)\n assert (Int.pow2 128 == 18446744073709551616 * 18446744073709551616)\n assert (Int.pow 2 128 == Int.pow2 128)\n assert (Int.pow (-2) 9 == -512)\n assert (Int.pow 3 5 == 243)\n assert (Int.pow 0 0 == 1)\n test \"negative power of two exponent\" fail = do\n assert (Int.pow2 (-1) == 0)\n test \"integer helpers 2\" = do\n assert (Int.powMod 2 10 7 == 2)\n assert (Int.toBytes True 2 bigInt == #\"0007\")\n assert (Int.fromBytes False bigBytes == 52651)\n assert (Int.toString bigInt == \"7\")\n test \"negative exponent\" fail = do\n assert (Int.pow 2 (-1) == 0)\n test \"negative integer encoding\" fail = do\n assert (Int.toBytes True 0 (-1) == #\"\")\n test \"byte access and slicing\" = do\n assert (Bytes.length bigBytes == 2)\n assert (Bytes.at bigBytes 1 == 205)\n assert (Bytes.slice 1 1 bigBytes == #\"cd\")\n assert (Bytes.take 1 bigBytes == #\"ab\")\n test \"byte access and slicing 2\" = do\n assert (Bytes.drop 1 bigBytes == #\"cd\")\n assert (Bytes.drop (-1) bigBytes == #\"abcd\")\n assert (Bytes.take (-1) bigBytes == #\"\")\n assert (Bytes.toHex bigBytes == \"abcd\")\n test \"byte access and slicing 3\" = do\n assert (Bytes.concat [bigBytes, bigBytes] == #\"abcdabcd\")\n test \"byte logic\" = do\n assert (Bytes.and False bigBytes #\"ff00\" == #\"ab00\")\n assert (Bytes.or False bigBytes #\"00ff\" == #\"abff\")\n assert (Bytes.xor False bigBytes #\"ffff\" == #\"5432\")\n assert (Bytes.complement bigBytes == #\"5432\")\n test \"byte logic 2\" = do\n assert (Bytes.readBit #\"01\" 0)\n assert (Bytes.writeBits #\"0000\" bigIndices True == #\"0101\")\n assert (Bytes.shift #\"01\" 1 == #\"02\")\n assert (Bytes.rotate #\"80\" 1 == #\"01\")\n test \"byte logic 3\" = do\n assert (Bytes.countSetBits bigBytes == 10)\n assert (Bytes.findFirstSetBit #\"08\" == 3)\n assert (Bytes.findFirstSetBit #\"00\" == -1)\n test \"hashes\" = do\n assert (Bytes.sha2_256 #\"\" == #\"e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855\")\n assert (Bytes.sha3_256 #\"\" == #\"a7ffc6f8bf1ed76651c14756a061d662f580ff4de43b49fa82d80a4b80f8434a\")\n assert (Bytes.blake2b_256 #\"\" == #\"0e5751c026e543b2e8ab2eb06099daa1d1e5df47778f7787faab45cdf12fe3a8\")\n assert (Bytes.blake2b_224 #\"\" == #\"836cc68931c2e4e3e838602eca1902591d216837bafddfe6f0c8cb07\")\n test \"hashes 2\" = do\n assert (Bytes.keccak_256 #\"\" == #\"c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470\")\n assert (Bytes.ripemd_160 #\"\" == #\"9c1185a5c5e9fc54612808977ee8f548b2258d31\")\n test \"byte index out of bounds\" fail = do\n assert (Bytes.at #\"\" 0 == 0)\n test \"text helpers\" = do\n assert (String.toBytes \"é\" == #\"c3a9\")\n assert (String.fromBytes #\"c3a9\" == \"é\")\n assert (String.fromInt bigInt == \"7\")\n assert (String.concat [\"a\", \"b\"] == \"ab\")\n test \"text helpers 2\" = do\n assert (String.join \",\" [\"a\", \"b\", \"c\"] == \"a,b,c\")\n assert (String.join \",\" [] == \"\")\n test \"invalid UTF-8\" fail = do\n assert (String.fromBytes #\"ff\" == \"\")\n test \"option helpers preserve payloads\" = do\n assert (Option.map Builtin.unIData bigOption == Some 7)\n assert (Option.map2 Int.add littleOption bigOption == Some 14)\n assert (Option.andThen (\\x -> Option.Some x) littleOption == littleOption)\n assert (Option.isSome bigOption)\n assert (Option.apply (Some Builtin.unIData) bigOption == Some 7)\n test \"option helpers preserve payloads 2\" = do\n assert (not (Option.isSome none))\n assert (Option.unwrap bigOption == bigInt)\n assert (Option.withDefault bigInt bigOption == bigInt)\n test \"unwrap None\" fail = do\n assert (Option.unwrap none == 0)\n test \"Big scalar control inputs\" = do\n assert (List.take bigInt [1, 2] == [1, 2])\n assert (Int.pow 2 bigInt == 128)\n assert (Int.toBytes Bool.True bigInt 1 == #\"00000000000001\")\n assert (Bytes.or Bool.True #\"00\" #\"ff\" == #\"ff\")\n test \"Big array indices\" = do\n assert (Array.at (Array.fromList [5, 6]) (Builtin.iData 1) == 6)\n assert (Array.get (Array.fromList [5, 6]) bigInt == None)\n" +description: "module Main exposing (..)\n\nbigInt : Int\nbigInt = 7\nbigBytes : Bytes\nbigBytes = #\"abcd\"\nbigIndices : List Int\nbigIndices = lift [Builtin.iData 0, Builtin.iData 8]\nbigOption : Option Int\nbigOption = Option.Some 7\nlittleOption : option Int\nlittleOption = Some 7\nnone : option int\nnone = None\nboth : (Lift bool 'a, Lift bool 'b) => 'a -> 'b -> bool\nboth a b = Bool.and a b\nbadBig : unit -> Bool\nbadBig _ = fail\n\ntests\n test \"mixed booleans and ordering\" = do\n assert (Bool.and Bool.True True)\n assert (Bool.or False Bool.True)\n assert (not (Bool.xor Bool.True True))\n assert (not (Bool.not Bool.True))\n test \"mixed booleans and ordering 2\" = do\n assert (Bool.or Bool.True (badBig ()))\n assert (not (Bool.and False (badBig ())))\n assert (Bool.True || badBig ())\n assert (both Bool.True True)\n test \"mixed booleans and ordering 3\" = do\n assert (Ordering.invert Ordering.LT == GT)\n assert (Ordering.then_ EQ Ordering.GT == GT)\n test \"integer helpers\" = do\n assert (Int.abs (-7) == 7)\n assert (Int.abs bigInt == 7)\n assert (Int.pow 2 10 == 1024)\n assert (Int.pow bigInt 0 == 1)\n test \"every small power lookup\" = do\n assert (List.map Int.pow2 (List.range 0 9) == [1, 2, 4, 8, 16, 32, 64, 128, 256])\n test \"exact powers of two across byte boundaries\" = do\n assert (Int.pow2 0 == 1)\n assert (Int.pow2 bigInt == 128)\n assert (Int.pow2 8 == 256)\n assert (Int.pow2 9 == 512)\n assert (Int.pow2 64 == 18446744073709551616)\n assert (Int.pow2 128 == 18446744073709551616 * 18446744073709551616)\n assert (Int.pow 2 128 == Int.pow2 128)\n assert (Int.pow (-2) 9 == -512)\n assert (Int.pow 3 5 == 243)\n assert (Int.pow 0 0 == 1)\n test \"negative power of two exponent\" fail = do\n assert (Int.pow2 (-1) == 0)\n test \"integer helpers 2\" = do\n assert (Int.powMod 2 10 7 == 2)\n assert (Int.toBytes True 2 bigInt == #\"0007\")\n assert (Int.fromBytes False bigBytes == 52651)\n assert (Int.toString bigInt == \"7\")\n test \"negative exponent\" fail = do\n assert (Int.pow 2 (-1) == 0)\n test \"negative integer encoding\" fail = do\n assert (Int.toBytes True 0 (-1) == #\"\")\n test \"byte access and slicing\" = do\n assert (Bytes.length bigBytes == 2)\n assert (Bytes.at bigBytes 1 == 205)\n assert (Bytes.slice 1 1 bigBytes == #\"cd\")\n assert (Bytes.take 1 bigBytes == #\"ab\")\n test \"byte access and slicing 2\" = do\n assert (Bytes.drop 1 bigBytes == #\"cd\")\n assert (Bytes.drop (-1) bigBytes == #\"abcd\")\n assert (Bytes.take (-1) bigBytes == #\"\")\n assert (Bytes.toHex bigBytes == \"abcd\")\n test \"byte access and slicing 3\" = do\n assert (Bytes.concat [bigBytes, bigBytes] == #\"abcdabcd\")\n test \"byte logic\" = do\n assert (Bytes.and False bigBytes #\"ff00\" == #\"ab00\")\n assert (Bytes.or False bigBytes #\"00ff\" == #\"abff\")\n assert (Bytes.xor False bigBytes #\"ffff\" == #\"5432\")\n assert (Bytes.complement bigBytes == #\"5432\")\n test \"byte logic 2\" = do\n assert (Bytes.readBit #\"01\" 0)\n assert (Bytes.writeBits #\"0000\" bigIndices True == #\"0101\")\n assert (Bytes.shift #\"01\" 1 == #\"02\")\n assert (Bytes.rotate #\"80\" 1 == #\"01\")\n test \"byte logic 3\" = do\n assert (Bytes.countSetBits bigBytes == 10)\n assert (Bytes.findFirstSetBit #\"08\" == 3)\n assert (Bytes.findFirstSetBit #\"00\" == -1)\n test \"hashes\" = do\n assert (Bytes.sha2_256 #\"\" == #\"e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855\")\n assert (Bytes.sha3_256 #\"\" == #\"a7ffc6f8bf1ed76651c14756a061d662f580ff4de43b49fa82d80a4b80f8434a\")\n assert (Bytes.blake2b_256 #\"\" == #\"0e5751c026e543b2e8ab2eb06099daa1d1e5df47778f7787faab45cdf12fe3a8\")\n assert (Bytes.blake2b_224 #\"\" == #\"836cc68931c2e4e3e838602eca1902591d216837bafddfe6f0c8cb07\")\n test \"hashes 2\" = do\n assert (Bytes.keccak_256 #\"\" == #\"c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470\")\n assert (Bytes.ripemd_160 #\"\" == #\"9c1185a5c5e9fc54612808977ee8f548b2258d31\")\n test \"byte index out of bounds\" fail = do\n assert (Bytes.at #\"\" 0 == 0)\n test \"text helpers\" = do\n assert (String.toBytes \"é\" == #\"c3a9\")\n assert (String.fromBytes #\"c3a9\" == \"é\")\n assert (String.fromInt bigInt == \"7\")\n assert (String.concat [\"a\", \"b\"] == \"ab\")\n test \"text helpers 2\" = do\n assert (String.join \",\" [\"a\", \"b\", \"c\"] == \"a,b,c\")\n assert (String.join \",\" [] == \"\")\n test \"invalid UTF-8\" fail = do\n assert (String.fromBytes #\"ff\" == \"\")\n test \"option helpers preserve payloads\" = do\n assert (Option.map Builtin.unIData bigOption == Some 7)\n assert (Option.map2 Int.add littleOption bigOption == Some 14)\n assert (Option.andThen (\\x -> Option.Some x) littleOption == littleOption)\n assert (Option.isSome bigOption)\n assert (Option.apply (Some Builtin.unIData) bigOption == Some 7)\n test \"option helpers preserve payloads 2\" = do\n assert (not (Option.isSome none))\n assert (Option.unwrap bigOption == bigInt)\n assert (Option.withDefault bigInt bigOption == bigInt)\n test \"unwrap None\" fail = do\n assert (Option.unwrap none == 0)\n test \"Big scalar control inputs\" = do\n assert (List.take bigInt [1, 2] == [1, 2])\n assert (Int.pow 2 bigInt == 128)\n assert (Int.toBytes Bool.True bigInt 1 == #\"00000000000001\")\n assert (Bytes.or Bool.True #\"00\" #\"ff\" == #\"ff\")\n test \"Big array indices\" = do\n assert (Array.at (Array.fromList [5, 6]) (Builtin.iData 1) == 6)\n assert (Array.get (Array.fromList [5, 6]) bigInt == None)\n" --- mixed booleans and ordering: Pass; traces: [] mixed booleans and ordering 2: Pass; traces: [] mixed booleans and ordering 3: Pass; traces: [] integer helpers: Pass; traces: [] +every small power lookup: Pass; traces: [] exact powers of two across byte boundaries: Pass; traces: [] negative power of two exponent: Pass; traces: [] integer helpers 2: Pass; traces: [] diff --git a/crates/nash-driver/tests/snapshots/testing_base__integer_width_bound_costs.snap b/crates/nash-driver/tests/snapshots/testing_base__integer_width_bound_costs.snap index 42263235..a909c8aa 100644 --- a/crates/nash-driver/tests/snapshots/testing_base__integer_width_bound_costs.snap +++ b/crates/nash-driver/tests/snapshots/testing_base__integer_width_bound_costs.snap @@ -1,22 +1,34 @@ --- source: crates/nash-driver/tests/testing_base.rs -description: " assert ((Int.pow 2 1 - 1) == 1)\n assert ((Int.pow2 1 - 1) == 1)\n assert ((1 * Builtin.expModInteger 2 1 257 - 1) == 1)\n assert ((Int.pow 2 4 - 1) == 15)\n assert ((Int.pow2 4 - 1) == 15)\n assert ((1 * Builtin.expModInteger 2 4 257 - 1) == 15)\n assert ((Int.pow 2 8 - 1) == 255)\n assert ((Int.pow2 8 - 1) == 255)\n assert ((1 * Builtin.expModInteger 2 8 257 - 1) == 255)\n assert ((Int.pow 2 16 - 1) == 65535)\n assert ((Int.pow2 16 - 1) == 65535)\n assert ((256 * Builtin.expModInteger 2 8 257 - 1) == 65535)\n assert ((Int.pow 2 64 - 1) == 18446744073709551615)\n assert ((Int.pow2 64 - 1) == 18446744073709551615)\n assert ((72057594037927936 * Builtin.expModInteger 2 8 257 - 1) == 18446744073709551615)\n assert ((Int.pow 2 120 - 1) == 1329227995784915872903807060280344575)\n assert ((Int.pow2 120 - 1) == 1329227995784915872903807060280344575)\n assert ((5192296858534827628530496329220096 * Builtin.expModInteger 2 8 257 - 1) == 1329227995784915872903807060280344575)" +description: " assert ((Int.pow 2 1 - 1) == 1)\n assert ((Int.pow2 1 - 1) == 1)\n assert ((1 * Builtin.expModInteger 2 1 257 - 1) == 1)\n assert ((1 * Builtin.indexArray (Builtin.listToArray [1, 2, 4, 8, 16, 32, 64, 128, 256]) 1 - 1) == 1)\n assert ((1 * Builtin.indexArray (comptime (Builtin.listToArray [1, 2, 4, 8, 16, 32, 64, 128, 256])) 1 - 1) == 1)\n assert ((Int.pow 2 4 - 1) == 15)\n assert ((Int.pow2 4 - 1) == 15)\n assert ((1 * Builtin.expModInteger 2 4 257 - 1) == 15)\n assert ((1 * Builtin.indexArray (Builtin.listToArray [1, 2, 4, 8, 16, 32, 64, 128, 256]) 4 - 1) == 15)\n assert ((1 * Builtin.indexArray (comptime (Builtin.listToArray [1, 2, 4, 8, 16, 32, 64, 128, 256])) 4 - 1) == 15)\n assert ((Int.pow 2 8 - 1) == 255)\n assert ((Int.pow2 8 - 1) == 255)\n assert ((1 * Builtin.expModInteger 2 8 257 - 1) == 255)\n assert ((1 * Builtin.indexArray (Builtin.listToArray [1, 2, 4, 8, 16, 32, 64, 128, 256]) 8 - 1) == 255)\n assert ((1 * Builtin.indexArray (comptime (Builtin.listToArray [1, 2, 4, 8, 16, 32, 64, 128, 256])) 8 - 1) == 255)\n assert ((Int.pow 2 16 - 1) == 65535)\n assert ((Int.pow2 16 - 1) == 65535)\n assert ((256 * Builtin.expModInteger 2 8 257 - 1) == 65535)\n assert ((256 * Builtin.indexArray (Builtin.listToArray [1, 2, 4, 8, 16, 32, 64, 128, 256]) 8 - 1) == 65535)\n assert ((256 * Builtin.indexArray (comptime (Builtin.listToArray [1, 2, 4, 8, 16, 32, 64, 128, 256])) 8 - 1) == 65535)\n assert ((Int.pow 2 64 - 1) == 18446744073709551615)\n assert ((Int.pow2 64 - 1) == 18446744073709551615)\n assert ((72057594037927936 * Builtin.expModInteger 2 8 257 - 1) == 18446744073709551615)\n assert ((72057594037927936 * Builtin.indexArray (Builtin.listToArray [1, 2, 4, 8, 16, 32, 64, 128, 256]) 8 - 1) == 18446744073709551615)\n assert ((72057594037927936 * Builtin.indexArray (comptime (Builtin.listToArray [1, 2, 4, 8, 16, 32, 64, 128, 256])) 8 - 1) == 18446744073709551615)\n assert ((Int.pow 2 120 - 1) == 1329227995784915872903807060280344575)\n assert ((Int.pow2 120 - 1) == 1329227995784915872903807060280344575)\n assert ((5192296858534827628530496329220096 * Builtin.expModInteger 2 8 257 - 1) == 1329227995784915872903807060280344575)\n assert ((5192296858534827628530496329220096 * Builtin.indexArray (Builtin.listToArray [1, 2, 4, 8, 16, 32, 64, 128, 256]) 8 - 1) == 1329227995784915872903807060280344575)\n assert ((5192296858534827628530496329220096 * Builtin.indexArray (comptime (Builtin.listToArray [1, 2, 4, 8, 16, 32, 64, 128, 256])) 8 - 1) == 1329227995784915872903807060280344575)" --- -width=1 pow: cpu=4344881 memory=19508 -width=1 pow2: cpu=3956548 memory=17407 +width=1 pow: cpu=3620897 memory=19139 +width=1 pow2: cpu=3232564 memory=17038 width=1 expMod: cpu=2096588 memory=6106 -width=4 pow: cpu=4344881 memory=19508 -width=4 pow2: cpu=3956548 memory=17407 +width=1 array: cpu=3400404 memory=13241 +width=1 constantArray: cpu=1324604 memory=5437 +width=4 pow: cpu=3620897 memory=19139 +width=4 pow2: cpu=3232564 memory=17038 width=4 expMod: cpu=2096588 memory=6106 -width=8 pow: cpu=4493214 memory=20109 -width=8 pow2: cpu=4104881 memory=18008 +width=4 array: cpu=3400404 memory=13241 +width=4 constantArray: cpu=1324604 memory=5437 +width=8 pow: cpu=3769230 memory=19740 +width=8 pow2: cpu=3380897 memory=17639 width=8 expMod: cpu=2096588 memory=6106 -width=16 pow: cpu=8474416 memory=37128 -width=16 pow2: cpu=8086083 memory=35027 +width=8 array: cpu=3400404 memory=13241 +width=8 constantArray: cpu=1324604 memory=5437 +width=16 pow: cpu=7750432 memory=36759 +width=16 pow2: cpu=7362099 memory=34658 width=16 expMod: cpu=2096588 memory=6106 -width=64 pow: cpu=11911700 memory=51745 -width=64 pow2: cpu=11523367 memory=49644 +width=16 array: cpu=3400404 memory=13241 +width=16 constantArray: cpu=1324604 memory=5437 +width=64 pow: cpu=11187716 memory=51376 +width=64 pow2: cpu=10799383 memory=49275 width=64 expMod: cpu=2097008 memory=6107 -width=120 pow: cpu=13476256 memory=58652 -width=120 pow2: cpu=13087923 memory=56551 +width=64 array: cpu=3400824 memory=13242 +width=64 constantArray: cpu=1325024 memory=5438 +width=120 pow: cpu=12752272 memory=58283 +width=120 pow2: cpu=12363939 memory=56182 width=120 expMod: cpu=2098085 memory=6108 +width=120 array: cpu=3401901 memory=13243 +width=120 constantArray: cpu=1326101 memory=5439 diff --git a/crates/nash-driver/tests/testing_base.rs b/crates/nash-driver/tests/testing_base.rs index d75d4a9e..b3652d7e 100644 --- a/crates/nash-driver/tests/testing_base.rs +++ b/crates/nash-driver/tests/testing_base.rs @@ -196,6 +196,18 @@ async fn integer_width_bound_costs() { "expMod", format!("{scale} * Builtin.expModInteger 2 {local} 257 - 1"), ), + ( + "array", + format!( + "{scale} * Builtin.indexArray (Builtin.listToArray [1, 2, 4, 8, 16, 32, 64, 128, 256]) {local} - 1" + ), + ), + ( + "constantArray", + format!( + "{scale} * Builtin.indexArray (comptime (Builtin.listToArray [1, 2, 4, 8, 16, 32, 64, 128, 256])) {local} - 1" + ), + ), ] { let source = format!(" assert (({expression}) == {expected})"); let output = compile(&source).await; diff --git a/docs/stdlib.md b/docs/stdlib.md index b060eb94..f43030a7 100644 --- a/docs/stdlib.md +++ b/docs/stdlib.md @@ -772,9 +772,9 @@ Predicates, comparators, and filterMap callbacks accept either outer representat item then accumulator. Native dropList handles list skipping. Int.pow and Int.pow2 reject negative exponents and return 1 for exponent zero. -Int.pow2 computes exact powers of two: local powers through eight bits use -expModInteger with modulus 257; larger exponents combine that result with a -power of 256. Int.pow uses this path for base 2 and exponentiation by squaring +Int.pow2 computes exact powers of two: local powers through eight bits index +a compile-time constant array; larger exponents combine that result with a +power of 256. The array is embedded directly and is not constructed at runtime. Int.pow uses this path for base 2 and exponentiation by squaring for other bases. These helpers impose no fixed result-size limit. Int.powMod uses Plutus expModInteger semantics, including its invalid-modulus and modular inverse failures. Int.toBytes rejects negative values, invalid sizes and values @@ -980,7 +980,6 @@ listOf : generator 'a -> generator (list 'a) -- length 0..20 listBetween : (Lift int 'l, Lift int 'h) => 'l -> 'h -> generator 'a -> generator (list 'a) oneOf : cons (generator 'a) -> generator 'a frequency : Lift int 'w => cons ('w, generator 'a) -> generator 'a -suchThat : ('a -> bool) -> generator 'a -> generator 'a -- gives up after 100 draws tuple2 : generator 'a -> generator 'b -> generator ('a, 'b) ``` @@ -1005,8 +1004,8 @@ smaller values (testing.md "Shrinking"): by a factor of four. Each width within a band is equally likely. It then samples from zero through 2^width - 1; zero has roughly 9% probability, rather than being the stopping result. Zero needs no sign choice. `intAtLeast lo` adds such a nonnegative magnitude to `lo`. - The local 1–8-bit power uses `Int.pow2`, backed by `expModInteger 2 bits 257`, which is exact - because the result is at most 256. Wider bands multiply the scale by 256. + The local 1–8-bit power uses `Int.pow2`, backed by a compile-time constant + array. Wider bands multiply the scale by 256. There is no fixed integer-size ceiling; VM budgets still apply. - never consume choices on a path that cannot fail differently. @@ -1014,11 +1013,6 @@ smaller values (testing.md "Shrinking"): weights fail before drawing, and empty/all-zero input returns None. Totals may exceed u64; selection uses the same bounded chunk sampler as `intBetween`. -`suchThat predicate generator` makes at most 100 attempts, grouping each draw. -Rejected values advance state; success on attempt 100 is accepted. Exhausting -attempts or an underlying draw returning None returns None. Replay uses only -recorded choices and never borrows from a sibling group or adds randomness. - `bool` uses one 0/1 choice. `option` groups its presence choice and, when present, its payload. `bytesBetween` uses inclusive length bounds; `bytesExactly` fixes both bounds to its count. Negative lengths and reversed bounds fail. All bound diff --git a/plans/12-stdlib.md b/plans/12-stdlib.md index 759ced81..bdec6ff3 100644 --- a/plans/12-stdlib.md +++ b/plans/12-stdlib.md @@ -294,7 +294,7 @@ passed, 3 ignored. Snapshot tests run without a custom Rust stack setting. - [x] Use nested little Choice/Group traces with strict replay, consumed-trace reduction, and ordinary generator Functor/Applicative/Monad instances. - [x] Rebuild structures in Nash and evaluate the prepared property once, with consumed-draw feedback for reduction. - [x] Ship choice, constant, intBetween, intAtLeast, int, bool, option, listOf, - listBetween, tuple2, oneOf, frequency, suchThat, bytes, bytesBetween and + listBetween, tuple2, oneOf, frequency, bytes, bytesBetween and bytesExactly; sequence draws with generator do or explicit state. - [x] Use arbitrary-precision integer generation built from u64 choices; normalize Big/little bounds and weights. Arbitrary Data generation is out of scope. @@ -331,7 +331,7 @@ sampling, replay and shrinking; preserve their behavior when extending APIs. `PropHelpers.nash` runs through the shared in-process Base snapshot runner. It covers arbitrary-size bounds and offsets, strict replay after rejected wide samples, weights above u64, invalid/zero weights, optional draws, byte bounds, -filter attempts 100/101, and isolated replay groups. The tuple2 do rewrite +and isolated replay groups. The tuple2 do rewrite preserves the two recorded groups and unused sibling choices. Generated properties verify integer ranges and byte lengths. Counterexample @@ -342,10 +342,11 @@ exercise seeded/replayed choices, malformed traces and the Rust runner. Nonnegative offsets use small-biased bit widths rather than zero-or-u64 chunks. A seeded 256-draw snapshot checks zero, small nonzero and larger offsets; explicit replay cases cover 1, 2, 10, 100, 255, 256 and values beyond u64. -CEK measurements showed exact modular powers cheaper than the previous -Int.pow implementation. Int.pow2 now provides that reusable exact operation, -and Int.pow selects it for base 2. A budget snapshot covers both public helpers -and direct modular bound expressions. Prop uses Int.pow2 for its local width. +CEK measurements compared repeated squaring, modular powers, runtime array +construction and constant-array lookup. The compile-time array uses less CPU +and memory than the modular builtin, so Int.pow2 uses it for local powers; +Int.pow selects the same path for base 2. Budget snapshots retain the public +helpers and the competing bound expressions. Prop uses Int.pow2 for its local width. The generator distribution changed for the wide branch of Prop.int; older recordings using its previous signed-64-bit layout are not replay-compatible. From 6c489a62742bd1fb4a64d5f3e9ab51c075785988 Mon Sep 17 00:00:00 2001 From: microproofs Date: Fri, 25 Sep 2026 13:47:20 -0400 Subject: [PATCH 04/10] feat(base): add Cardano V3 library Signed-off-by: microproofs --- .sampo/changesets/cardano-base.md | 6 + Cargo.lock | 2 + SPEC.md | 2 +- crates/nash-can/src/defaults.rs | 4 + crates/nash-codegen/Cargo.toml | 2 + crates/nash-codegen/src/build/tests.rs | 10 +- crates/nash-codegen/src/harness.rs | 51 ++++ crates/nash-codegen/src/tests/integration.rs | 12 +- crates/nash-driver/base/src/Bool.nash | 9 + .../nash-driver/base/src/Cardano/Address.nash | 32 +++ crates/nash-driver/base/src/Cardano/Time.nash | 85 +++++++ crates/nash-driver/base/src/Cardano/Tx.nash | 221 ++++++++++++++++++ .../nash-driver/base/src/Cardano/Value.nash | 45 ++++ crates/nash-driver/base/src/Option.nash | 8 + crates/nash-driver/tests/base_traits.rs | 2 + crates/nash-driver/tests/cardano.rs | 200 ++++++++++++++++ .../tests/fixtures/base-traits/Cardano.nash | 63 +++++ .../fixtures/base-traits/CardanoValue.nash | 23 ++ .../tests/fixtures/cardano/Context.nash | 109 +++++++++ .../tests/fixtures/cardano/Intervals.nash | 24 ++ .../tests/fixtures/cardano/Validate.nash | 11 + .../tests/fixtures/cardano/Values.nash | 22 ++ .../fixtures/cardano/golden/context-0.cbor | Bin 0 -> 1130 bytes .../fixtures/cardano/golden/context-1.cbor | Bin 0 -> 1137 bytes .../fixtures/cardano/golden/context-2.cbor | Bin 0 -> 1131 bytes .../fixtures/cardano/golden/context-3.cbor | Bin 0 -> 1139 bytes .../fixtures/cardano/golden/context-4.cbor | Bin 0 -> 1135 bytes .../fixtures/cardano/golden/context-5.cbor | Bin 0 -> 1140 bytes .../fixtures/cardano/golden/context-6.cbor | Bin 0 -> 1003 bytes .../cardano/golden/interval-results.cbor | 1 + .../fixtures/cardano/golden/intervals.cbor | Bin 0 -> 2642 bytes .../tests/fixtures/cardano/golden/values.cbor | Bin 0 -> 23 bytes .../tests/fixtures/cardano/reference/Main.hs | 80 +++++++ .../fixtures/cardano/reference/README.md | 32 +++ .../fixtures/cardano/reference/regenerate.sh | 22 ++ .../base_traits__cardano_helpers.snap | 16 ++ .../snapshots/base_traits__cardano_value.snap | 8 + .../snapshots/cardano__ledger_contexts.snap | 11 + .../cardano__ledger_interval_semantics.snap | 104 +++++++++ .../cardano__ledger_value_operations.snap | 5 + .../cardano__malformed_contexts.snap | 24 ++ crates/nash-driver/tests/support/mod.rs | 39 ++++ crates/nash-driver/tests/testing_base.rs | 21 +- docs/stdlib.md | 148 +++++++----- examples/vesting/README.md | 2 +- examples/vesting/app/src/Vesting.nash | 2 +- examples/vesting/app/src/VestingParam.nash | 2 +- .../src/{Cardano/Tx.nash => VestingTx.nash} | 2 +- plans/07-codegen.md | 2 +- plans/12-stdlib.md | 94 +++----- 50 files changed, 1414 insertions(+), 144 deletions(-) create mode 100644 .sampo/changesets/cardano-base.md create mode 100644 crates/nash-driver/base/src/Cardano/Address.nash create mode 100644 crates/nash-driver/base/src/Cardano/Time.nash create mode 100644 crates/nash-driver/base/src/Cardano/Tx.nash create mode 100644 crates/nash-driver/base/src/Cardano/Value.nash create mode 100644 crates/nash-driver/tests/cardano.rs create mode 100644 crates/nash-driver/tests/fixtures/base-traits/Cardano.nash create mode 100644 crates/nash-driver/tests/fixtures/base-traits/CardanoValue.nash create mode 100644 crates/nash-driver/tests/fixtures/cardano/Context.nash create mode 100644 crates/nash-driver/tests/fixtures/cardano/Intervals.nash create mode 100644 crates/nash-driver/tests/fixtures/cardano/Validate.nash create mode 100644 crates/nash-driver/tests/fixtures/cardano/Values.nash create mode 100644 crates/nash-driver/tests/fixtures/cardano/golden/context-0.cbor create mode 100644 crates/nash-driver/tests/fixtures/cardano/golden/context-1.cbor create mode 100644 crates/nash-driver/tests/fixtures/cardano/golden/context-2.cbor create mode 100644 crates/nash-driver/tests/fixtures/cardano/golden/context-3.cbor create mode 100644 crates/nash-driver/tests/fixtures/cardano/golden/context-4.cbor create mode 100644 crates/nash-driver/tests/fixtures/cardano/golden/context-5.cbor create mode 100644 crates/nash-driver/tests/fixtures/cardano/golden/context-6.cbor create mode 100644 crates/nash-driver/tests/fixtures/cardano/golden/interval-results.cbor create mode 100644 crates/nash-driver/tests/fixtures/cardano/golden/intervals.cbor create mode 100644 crates/nash-driver/tests/fixtures/cardano/golden/values.cbor create mode 100644 crates/nash-driver/tests/fixtures/cardano/reference/Main.hs create mode 100644 crates/nash-driver/tests/fixtures/cardano/reference/README.md create mode 100755 crates/nash-driver/tests/fixtures/cardano/reference/regenerate.sh create mode 100644 crates/nash-driver/tests/snapshots/base_traits__cardano_helpers.snap create mode 100644 crates/nash-driver/tests/snapshots/base_traits__cardano_value.snap create mode 100644 crates/nash-driver/tests/snapshots/cardano__ledger_contexts.snap create mode 100644 crates/nash-driver/tests/snapshots/cardano__ledger_interval_semantics.snap create mode 100644 crates/nash-driver/tests/snapshots/cardano__ledger_value_operations.snap create mode 100644 crates/nash-driver/tests/snapshots/cardano__malformed_contexts.snap create mode 100644 crates/nash-driver/tests/support/mod.rs rename examples/vesting/app/src/{Cardano/Tx.nash => VestingTx.nash} (89%) diff --git a/.sampo/changesets/cardano-base.md b/.sampo/changesets/cardano-base.md new file mode 100644 index 00000000..d9b89e1a --- /dev/null +++ b/.sampo/changesets/cardano-base.md @@ -0,0 +1,6 @@ +--- +cargo/nash-driver: minor +cargo/nash-can: minor +--- + +Ship Cardano V3 context, address, governance, value, and interval modules in Base. Use native value builtins, explicit Nash wire validation, and Haskell-generated encoding and interval comparisons. diff --git a/Cargo.lock b/Cargo.lock index cf11d1f5..2c874877 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1948,6 +1948,7 @@ dependencies = [ "nash-can", "nash-config", "nash-constrain", + "nash-driver", "nash-ir", "nash-nitpick", "nash-parse", @@ -1957,6 +1958,7 @@ dependencies = [ "nash-test", "serde_json", "thiserror 2.0.21", + "url", ] [[package]] diff --git a/SPEC.md b/SPEC.md index 7dfb919d..96a167fb 100644 --- a/SPEC.md +++ b/SPEC.md @@ -67,7 +67,7 @@ Implementation plans (Plan 08 is deferred; Plans 09 and 10 are complete): - [x] 09 Validators + `nash build` — [plans/09-validators-build.md](plans/09-validators-build.md) - [x] 10 Testing: `tests` block, props, generators, shrinking, power-assert, `nash test` — [plans/10-testing.md](plans/10-testing.md) - [ ] 11 Macros + comptime — [plans/11-macros-comptime.md](plans/11-macros-comptime.md) -- [ ] 12 Stdlib `nash/base` — [plans/12-stdlib.md](plans/12-stdlib.md) +- [ ] 12 Stdlib `nash/base` — [plans/12-stdlib.md](plans/12-stdlib.md) (chunks 1–9 and 11 complete; chunk 10 Ast/Derive deferred) - [ ] 13 `nash fmt`, `nash docs` — [plans/13-fmt-docs.md](plans/13-fmt-docs.md) - [x] Representation-classed impl heads: `Big`/`Little` blankets and concrete little impls coexist — [plans/repr-classed-impl-heads.md](plans/repr-classed-impl-heads.md) diff --git a/crates/nash-can/src/defaults.rs b/crates/nash-can/src/defaults.rs index 6195e9ca..5b05153f 100644 --- a/crates/nash-can/src/defaults.rs +++ b/crates/nash-can/src/defaults.rs @@ -41,6 +41,10 @@ pub const MODULES: &[(&str, &[&str])] = &[ ("Array", &[]), ("Prop", &[]), ("Test", &[]), + ("Cardano.Tx", &[]), + ("Cardano.Address", &[]), + ("Cardano.Value", &[]), + ("Cardano.Time", &[]), ]; pub fn imports<'a>( diff --git a/crates/nash-codegen/Cargo.toml b/crates/nash-codegen/Cargo.toml index e080a6b1..531125df 100644 --- a/crates/nash-codegen/Cargo.toml +++ b/crates/nash-codegen/Cargo.toml @@ -20,6 +20,8 @@ nash-config = { path = "../nash-config", version = "0.5.0" } thiserror.workspace = true [dev-dependencies] +nash-driver = { path = "../nash-driver" } +url.workspace = true nash-parse = { path = "../nash-parse" } nash-constrain = { path = "../nash-constrain" } nash-nitpick = { path = "../nash-nitpick" } diff --git a/crates/nash-codegen/src/build/tests.rs b/crates/nash-codegen/src/build/tests.rs index 37067418..811c4a76 100644 --- a/crates/nash-codegen/src/build/tests.rs +++ b/crates/nash-codegen/src/build/tests.rs @@ -162,7 +162,7 @@ fn with_base_modules( let bump = arena.as_bump(); let mut interfaces = BTreeMap::from([("Builtin", nash_can::kinds::builtin_interface(bump))]); let mut units = Vec::new(); - for (source, package) in modules { + for (source, package) in crate::harness::dependency_order(modules) { let source = bump.alloc_str(source); let parsed = nash_parse::Parser::new(bump, source).module().unwrap(); let canonical = nash_can::canonicalize( @@ -188,7 +188,13 @@ fn with_base_modules( units.push(Unit { canonical, solved }); } let root = QualifiedName { - home: units.last().unwrap().canonical.module.name, + home: units + .iter() + .find(|unit| unit.canonical.module.name.package.is_none()) + .unwrap() + .canonical + .module + .name, name: "main", }; let build = Build::new(units.iter().map(|u| Input { diff --git a/crates/nash-codegen/src/harness.rs b/crates/nash-codegen/src/harness.rs index 60a5feea..bb266428 100644 --- a/crates/nash-codegen/src/harness.rs +++ b/crates/nash-codegen/src/harness.rs @@ -1,4 +1,6 @@ //! Shared executable Core fixture harness. +use std::collections::BTreeMap; + use nash_ir::core::Core; use nash_plutus::{ arena::Arena, @@ -8,6 +10,55 @@ use nash_plutus::{ program::{Program, Version}, }; +pub fn dependency_order<'a>( + sources: impl IntoIterator>)>, +) -> Vec<(&'a str, Option>)> { + let bump = bumpalo::Bump::new(); + let sources: BTreeMap<_, _> = sources + .into_iter() + .map(|(source, package)| { + let parsed = nash_parse::Parser::new(&bump, source).module().unwrap(); + (parsed.name.unwrap().value, (source, package, parsed)) + }) + .collect(); + let uri = |name| url::Url::parse(&format!("file:///fixtures/{name}.nash")).unwrap(); + let mut graph = nash_driver::DepGraph::new(); + let mut modules = BTreeMap::new(); + for (name, (source, package, parsed)) in &sources { + let mut imports: Vec<_> = parsed + .imports + .iter() + .chain( + parsed + .tests + .into_iter() + .flat_map(|tests| tests.imports.iter()), + ) + .map(|import| uri(import.import.value)) + .collect(); + if *package != Some(nash_ast::primitives::BASE) { + // Fixture applications receive all supplied Base interfaces. + imports.extend( + sources + .iter() + .filter(|(_, (_, package, _))| *package == Some(nash_ast::primitives::BASE)) + .map(|(name, _)| uri(name)), + ); + } + imports.sort(); + imports.dedup(); + let module_uri = uri(name); + graph.add_module(module_uri.clone(), imports); + modules.insert(module_uri, (*source, *package)); + } + graph.compute_order().expect("acyclic fixture imports"); + graph + .order + .iter() + .filter_map(|uri| modules.remove(uri)) + .collect() +} + pub struct Evaluated { pub uplc: String, pub result: String, diff --git a/crates/nash-codegen/src/tests/integration.rs b/crates/nash-codegen/src/tests/integration.rs index 1ccdae00..ea4fdee4 100644 --- a/crates/nash-codegen/src/tests/integration.rs +++ b/crates/nash-codegen/src/tests/integration.rs @@ -27,7 +27,7 @@ fn compile_selected( let bump = arena.as_bump(); let mut interfaces = BTreeMap::from([("Builtin", nash_can::kinds::builtin_interface(bump))]); let mut modules = Vec::new(); - for (text, package) in [ + for (text, package) in crate::harness::dependency_order([ ( include_str!("../../tests/fixtures/VestingLiteral.nash"), Some(primitives::BASE), @@ -40,6 +40,14 @@ fn compile_selected( include_str!("../../../nash-driver/base/src/Lift.nash"), Some(primitives::BASE), ), + ( + include_str!("../../../nash-driver/base/src/Eq.nash"), + Some(primitives::BASE), + ), + ( + include_str!("../../../nash-driver/base/src/Data.nash"), + Some(primitives::BASE), + ), ( include_str!("../../../nash-driver/base/src/Bool.nash"), Some(primitives::BASE), @@ -77,7 +85,7 @@ fn compile_selected( Some(primitives::BASE), ), (source, None), - ] { + ]) { let text = bump.alloc_str(text); let parsed = nash_parse::Parser::new(bump, text) .module() diff --git a/crates/nash-driver/base/src/Bool.nash b/crates/nash-driver/base/src/Bool.nash index 517ca405..a3bbef6a 100644 --- a/crates/nash-driver/base/src/Bool.nash +++ b/crates/nash-driver/base/src/Bool.nash @@ -3,6 +3,8 @@ module Bool exposing (Bool(..), not, and, or, xor) import Literal exposing (FromBool) import Primitive import Lift exposing (Lift) +import Data exposing (Validate) +import Eq type Bool = False | True @@ -27,3 +29,10 @@ impl Lift bool Bool where impl FromBool Bool where fromBool value = if value then Bool.True else Bool.False + +impl Validate Bool where + validate value = + case Builtin.unConstrData value of + pair(0, []) -> Bool.False + pair(1, []) -> Bool.True + _ -> fail diff --git a/crates/nash-driver/base/src/Cardano/Address.nash b/crates/nash-driver/base/src/Cardano/Address.nash new file mode 100644 index 00000000..f30b01ec --- /dev/null +++ b/crates/nash-driver/base/src/Cardano/Address.nash @@ -0,0 +1,32 @@ +module Cardano.Address exposing (..) + +import Data exposing (Validate) +import Eq +import Literal +import Option exposing (Option) + +type Credential = PubKeyCredential Bytes | ScriptCredential Bytes + +type StakingCredential = StakingHash Credential | StakingPtr Int Int Int + +type Address = Address { paymentCredential : Credential, stakingCredential : Option StakingCredential } + +impl Validate Credential where + validate raw = + case Builtin.unConstrData raw of + pair(0, [f0]) -> PubKeyCredential (validate f0) + pair(1, [f0]) -> ScriptCredential (validate f0) + _ -> fail + +impl Validate StakingCredential where + validate raw = + case Builtin.unConstrData raw of + pair(0, [f0]) -> StakingHash (validate f0) + pair(1, [f0, f1, f2]) -> StakingPtr (validate f0) (validate f1) (validate f2) + _ -> fail + +impl Validate Address where + validate raw = + case Builtin.unConstrData raw of + pair(0, [f0, f1]) -> Address (validate f0) (validate f1) + _ -> fail diff --git a/crates/nash-driver/base/src/Cardano/Time.nash b/crates/nash-driver/base/src/Cardano/Time.nash new file mode 100644 index 00000000..0305d55d --- /dev/null +++ b/crates/nash-driver/base/src/Cardano/Time.nash @@ -0,0 +1,85 @@ +module Cardano.Time exposing (Extended(..), IntervalBound(..), Interval(..), interval, from, to, always, never, member, contains, isEmpty) + +import Prelude exposing (..) +import Primitive exposing (type bool(..)) +import Num +import Data exposing (Validate) +import Bool exposing (Bool) +import Lift exposing (Lift) + +type Extended = NegInf | Finite Int | PosInf + +type IntervalBound = IntervalBound { bound : Extended, inclusive : Bool } + +type Interval = Interval { lowerBound : IntervalBound, upperBound : IntervalBound } + +impl Validate Extended where + validate raw = + case Builtin.unConstrData raw of + pair(0, []) -> NegInf + pair(1, [f0]) -> Finite (validate f0) + pair(2, []) -> PosInf + _ -> fail + +impl Validate IntervalBound where + validate raw = + case Builtin.unConstrData raw of + pair(0, [f0, f1]) -> IntervalBound (validate f0) (validate f1) + _ -> fail + +impl Validate Interval where + validate raw = + case Builtin.unConstrData raw of + pair(0, [f0, f1]) -> Interval (validate f0) (validate f1) + _ -> fail + +type endpoint = Negative | At int | Positive + +inclusiveEndpoint : int -> IntervalBound -> endpoint +inclusiveEndpoint step (IntervalBound bound closed) = + case bound of + NegInf -> Negative + PosInf -> Positive + Finite value -> At (lower value + (if lower closed then 0 else step)) + +endpointLe : endpoint -> endpoint -> bool +endpointLe left right = + case (left, right) of + (Negative, _) -> True + (_, Positive) -> True + (Positive, _) -> False + (_, Negative) -> False + (At a, At b) -> a <= b + +isEmpty : Interval -> bool +isEmpty (Interval lo hi) = + Bool.not (endpointLe (inclusiveEndpoint 1 lo) (inclusiveEndpoint -1 hi)) + +contains : Interval -> Interval -> bool +contains outer inner = + if isEmpty inner then True + else if isEmpty outer then False + else + endpointLe (inclusiveEndpoint 1 outer.lowerBound) (inclusiveEndpoint 1 inner.lowerBound) + && endpointLe (inclusiveEndpoint -1 inner.upperBound) (inclusiveEndpoint -1 outer.upperBound) + +member : Lift int 'n => 'n -> Interval -> bool +member point (Interval lo hi) = + endpointLe (inclusiveEndpoint 1 lo) (At (lower point)) + && endpointLe (At (lower point)) (inclusiveEndpoint -1 hi) + +interval : (Lift int 'a, Lift int 'b) => 'a -> 'b -> Interval +interval start end = + Interval (IntervalBound (Finite (lift (lower start))) Bool.True) (IntervalBound (Finite (lift (lower end))) Bool.True) + +from : Lift int 'a => 'a -> Interval +from start = Interval (IntervalBound (Finite (lift (lower start))) Bool.True) (IntervalBound PosInf Bool.True) + +to : Lift int 'a => 'a -> Interval +to end = Interval (IntervalBound NegInf Bool.True) (IntervalBound (Finite (lift (lower end))) Bool.True) + +always : Interval +always = Interval (IntervalBound NegInf Bool.True) (IntervalBound PosInf Bool.True) + +never : Interval +never = Interval (IntervalBound PosInf Bool.True) (IntervalBound NegInf Bool.True) diff --git a/crates/nash-driver/base/src/Cardano/Tx.nash b/crates/nash-driver/base/src/Cardano/Tx.nash new file mode 100644 index 00000000..be25881e --- /dev/null +++ b/crates/nash-driver/base/src/Cardano/Tx.nash @@ -0,0 +1,221 @@ +module Cardano.Tx exposing (..) + +import Data exposing (Validate) +import Eq +import Literal +import Option exposing (Option) +import Cardano.Address exposing (Address, Credential) +import Cardano.Time exposing (Interval) +import Cardano.Value exposing (Value) + +type DRep = DRep Credential | DRepAlwaysAbstain | DRepAlwaysNoConfidence + +type Delegatee = DelegStake Bytes | DelegVote DRep | DelegStakeVote Bytes DRep + +type Certificate = RegStaking Credential (Option Int) | UnRegStaking Credential (Option Int) | DelegStaking Credential Delegatee | RegDeleg Credential Delegatee Int | RegDRep Credential Int | UpdateDRep Credential | UnRegDRep Credential Int | PoolRegister Bytes Bytes | PoolRetire Bytes Int | AuthHotCommittee Credential Credential | ResignColdCommittee Credential + +type Voter = CommitteeVoter Credential | DRepVoter Credential | StakePoolVoter Bytes + +type Vote = VoteNo | VoteYes | Abstain + +type GovernanceActionId = GovernanceActionId { txId : Bytes, index : Int } + +type Rational = Rational { numerator : Int, denominator : Int } + +type Committee = Committee { members : Map Credential Int, quorum : Rational } + +type Constitution = Constitution (Option Bytes) + +type ProtocolVersion = ProtocolVersion { major : Int, minor : Int } + +type GovernanceAction = ParameterChange (Option GovernanceActionId) Data (Option Bytes) | HardForkInitiation (Option GovernanceActionId) ProtocolVersion | TreasuryWithdrawals (Map Credential Int) (Option Bytes) | NoConfidence (Option GovernanceActionId) | UpdateCommittee (Option GovernanceActionId) (List Credential) (Map Credential Int) Rational | NewConstitution (Option GovernanceActionId) Constitution | InfoAction + +type Proposal = Proposal { deposit : Int, returnAddress : Credential, governanceAction : GovernanceAction } + +type OutputReference = OutputReference { txId : Bytes, index : Int } + +type Datum = NoDatum | DatumHash Bytes | InlineDatum Data + +type Output = Output { address : Address, value : Value, datum : Datum, script : Option Bytes } + +type Input = Input { outputReference : OutputReference, output : Output } + +type ScriptPurpose = Minting Bytes | Spending OutputReference | Rewarding Credential | Certifying Int Certificate | Voting Voter | Proposing Int Proposal + +type ScriptInfo = MintingScript Bytes | SpendingScript OutputReference (Option Data) | RewardingScript Credential | CertifyingScript Int Certificate | VotingScript Voter | ProposingScript Int Proposal + +type Tx = Tx { inputs : List Input + , referenceInputs : List Input + , outputs : List Output + , fee : Int + , mint : Value + , certificates : List Certificate + , withdrawals : Map Credential Int + , validityRange : Interval + , extraSignatories : List Bytes + , redeemers : Map ScriptPurpose Data + , datums : Map Bytes Data + , id : Bytes + , votes : Map Voter (Map GovernanceActionId Vote) + , proposals : List Proposal + , currentTreasury : Option Int + , treasuryDonation : Option Int + } + +type ScriptContext = ScriptContext { tx : Tx, redeemer : Data, scriptInfo : ScriptInfo } + +impl Validate DRep where + validate raw = + case Builtin.unConstrData raw of + pair(0, [f0]) -> DRep (validate f0) + pair(1, []) -> DRepAlwaysAbstain + pair(2, []) -> DRepAlwaysNoConfidence + _ -> fail + +impl Validate Delegatee where + validate raw = + case Builtin.unConstrData raw of + pair(0, [f0]) -> DelegStake (validate f0) + pair(1, [f0]) -> DelegVote (validate f0) + pair(2, [f0, f1]) -> DelegStakeVote (validate f0) (validate f1) + _ -> fail + +impl Validate Certificate where + validate raw = + case Builtin.unConstrData raw of + pair(0, [f0, f1]) -> RegStaking (validate f0) (validate f1) + pair(1, [f0, f1]) -> UnRegStaking (validate f0) (validate f1) + pair(2, [f0, f1]) -> DelegStaking (validate f0) (validate f1) + pair(3, [f0, f1, f2]) -> RegDeleg (validate f0) (validate f1) (validate f2) + pair(4, [f0, f1]) -> RegDRep (validate f0) (validate f1) + pair(5, [f0]) -> UpdateDRep (validate f0) + pair(6, [f0, f1]) -> UnRegDRep (validate f0) (validate f1) + pair(7, [f0, f1]) -> PoolRegister (validate f0) (validate f1) + pair(8, [f0, f1]) -> PoolRetire (validate f0) (validate f1) + pair(9, [f0, f1]) -> AuthHotCommittee (validate f0) (validate f1) + pair(10, [f0]) -> ResignColdCommittee (validate f0) + _ -> fail + +impl Validate Voter where + validate raw = + case Builtin.unConstrData raw of + pair(0, [f0]) -> CommitteeVoter (validate f0) + pair(1, [f0]) -> DRepVoter (validate f0) + pair(2, [f0]) -> StakePoolVoter (validate f0) + _ -> fail + +impl Validate Vote where + validate raw = + case Builtin.unConstrData raw of + pair(0, []) -> VoteNo + pair(1, []) -> VoteYes + pair(2, []) -> Abstain + _ -> fail + +impl Validate GovernanceActionId where + validate raw = + case Builtin.unConstrData raw of + pair(0, [f0, f1]) -> GovernanceActionId (validate f0) (validate f1) + _ -> fail + +impl Validate Rational where + validate raw = + case Builtin.unConstrData raw of + pair(0, [f0, f1]) -> Rational (validate f0) (validate f1) + _ -> fail + +impl Validate Committee where + validate raw = + case Builtin.unConstrData raw of + pair(0, [f0, f1]) -> Committee (validate f0) (validate f1) + _ -> fail + +impl Validate Constitution where + validate raw = + case Builtin.unConstrData raw of + pair(0, [f0]) -> Constitution (validate f0) + _ -> fail + +impl Validate ProtocolVersion where + validate raw = + case Builtin.unConstrData raw of + pair(0, [f0, f1]) -> ProtocolVersion (validate f0) (validate f1) + _ -> fail + +impl Validate GovernanceAction where + validate raw = + case Builtin.unConstrData raw of + pair(0, [f0, f1, f2]) -> ParameterChange (validate f0) (validate f1) (validate f2) + pair(1, [f0, f1]) -> HardForkInitiation (validate f0) (validate f1) + pair(2, [f0, f1]) -> TreasuryWithdrawals (validate f0) (validate f1) + pair(3, [f0]) -> NoConfidence (validate f0) + pair(4, [f0, f1, f2, f3]) -> UpdateCommittee (validate f0) (validate f1) (validate f2) (validate f3) + pair(5, [f0, f1]) -> NewConstitution (validate f0) (validate f1) + pair(6, []) -> InfoAction + _ -> fail + +impl Validate Proposal where + validate raw = + case Builtin.unConstrData raw of + pair(0, [f0, f1, f2]) -> Proposal (validate f0) (validate f1) (validate f2) + _ -> fail + +impl Validate OutputReference where + validate raw = + case Builtin.unConstrData raw of + pair(0, [f0, f1]) -> OutputReference (validate f0) (validate f1) + _ -> fail + +impl Validate Datum where + validate raw = + case Builtin.unConstrData raw of + pair(0, []) -> NoDatum + pair(1, [f0]) -> DatumHash (validate f0) + pair(2, [f0]) -> InlineDatum (validate f0) + _ -> fail + +impl Validate Output where + validate raw = + case Builtin.unConstrData raw of + pair(0, [f0, f1, f2, f3]) -> Output (validate f0) (validate f1) (validate f2) (validate f3) + _ -> fail + +impl Validate Input where + validate raw = + case Builtin.unConstrData raw of + pair(0, [f0, f1]) -> Input (validate f0) (validate f1) + _ -> fail + +impl Validate ScriptPurpose where + validate raw = + case Builtin.unConstrData raw of + pair(0, [f0]) -> Minting (validate f0) + pair(1, [f0]) -> Spending (validate f0) + pair(2, [f0]) -> Rewarding (validate f0) + pair(3, [f0, f1]) -> Certifying (validate f0) (validate f1) + pair(4, [f0]) -> Voting (validate f0) + pair(5, [f0, f1]) -> Proposing (validate f0) (validate f1) + _ -> fail + +impl Validate ScriptInfo where + validate raw = + case Builtin.unConstrData raw of + pair(0, [f0]) -> MintingScript (validate f0) + pair(1, [f0, f1]) -> SpendingScript (validate f0) (validate f1) + pair(2, [f0]) -> RewardingScript (validate f0) + pair(3, [f0, f1]) -> CertifyingScript (validate f0) (validate f1) + pair(4, [f0]) -> VotingScript (validate f0) + pair(5, [f0, f1]) -> ProposingScript (validate f0) (validate f1) + _ -> fail + +impl Validate Tx where + validate raw = + case Builtin.unConstrData raw of + pair(0, [f0, f1, f2, f3, f4, f5, f6, f7, f8, f9, f10, f11, f12, f13, f14, f15]) -> Tx (validate f0) (validate f1) (validate f2) (validate f3) (validate f4) (validate f5) (validate f6) (validate f7) (validate f8) (validate f9) (validate f10) (validate f11) (validate f12) (validate f13) (validate f14) (validate f15) + _ -> fail + +impl Validate ScriptContext where + validate raw = + case Builtin.unConstrData raw of + pair(0, [f0, f1, f2]) -> ScriptContext (validate f0) (validate f1) (validate f2) + _ -> fail diff --git a/crates/nash-driver/base/src/Cardano/Value.nash b/crates/nash-driver/base/src/Cardano/Value.nash new file mode 100644 index 00000000..1fcc1874 --- /dev/null +++ b/crates/nash-driver/base/src/Cardano/Value.nash @@ -0,0 +1,45 @@ +module Cardano.Value exposing (Value, empty, singleton, insert, lovelace, quantityOf, add, contains, scale) + +import Lift exposing (Lift) +import Data exposing (FromData, ToData, Validate) + +type alias Value = Map Bytes (Map Bytes Int) + +impl Lift value Value where + lift value = fromData (Builtin.valueData value) + lower value = Builtin.unValueData (toData value) + +impl Validate Value where + validate raw = + let + checked : Map Bytes (Map Bytes Int) + checked = validate raw + in + fromData (toData checked) + +empty : value +empty = Builtin.unValueData (toData emptyMap) + +emptyMap : Value +emptyMap = Builtin.mapData [] + +singleton : (Lift bytes 'p, Lift bytes 't, Lift int 'n) => 'p -> 't -> 'n -> value +singleton policy token amount = insert policy token amount empty + +insert : (Lift bytes 'p, Lift bytes 't, Lift int 'n, Lift value 'v) => 'p -> 't -> 'n -> 'v -> value +insert policy token amount value = Builtin.insertCoin (lower policy) (lower token) (lower amount) (lower value) + +lovelace : Lift value 'v => 'v -> int +lovelace value = quantityOf #"" #"" value + +quantityOf : (Lift bytes 'p, Lift bytes 't, Lift value 'v) => 'p -> 't -> 'v -> int +quantityOf policy token value = Builtin.lookupCoin (lower policy) (lower token) (lower value) + +add : (Lift value 'a, Lift value 'b) => 'a -> 'b -> value +add left right = Builtin.unionValue (lower left) (lower right) + +contains : (Lift value 'a, Lift value 'b) => 'a -> 'b -> bool +contains left right = Builtin.valueContains (lower left) (lower right) + +scale : (Lift int 'n, Lift value 'v) => 'n -> 'v -> value +scale amount value = Builtin.scaleValue (lower amount) (lower value) diff --git a/crates/nash-driver/base/src/Option.nash b/crates/nash-driver/base/src/Option.nash index ce0e3c49..acb0ff31 100644 --- a/crates/nash-driver/base/src/Option.nash +++ b/crates/nash-driver/base/src/Option.nash @@ -6,6 +6,7 @@ import Lift exposing (Lift) import Functor import Applicative exposing (Applicative) import Monad exposing (Monad) +import Data exposing (Validate) type option 'a = Some 'a | None type Option 'a = Some 'a | None @@ -84,3 +85,10 @@ unwrap value = lowerOuter : Lift (option 'a) ('f 'a) => 'f 'a -> option 'a lowerOuter = lower + +impl Validate 'a => Validate (Option 'a) where + validate value = + case Builtin.unConstrData value of + pair(0, [item]) -> Option.Some (validate item) + pair(1, []) -> Option.None + _ -> fail diff --git a/crates/nash-driver/tests/base_traits.rs b/crates/nash-driver/tests/base_traits.rs index 380760a3..9c094fbb 100644 --- a/crates/nash-driver/tests/base_traits.rs +++ b/crates/nash-driver/tests/base_traits.rs @@ -124,3 +124,5 @@ base_snapshot!(map_helpers, "fixtures/base-traits/Maps.nash"); base_snapshot!(representation_classes, "fixtures/base-traits/Classed.nash"); base_snapshot!(prop_helpers, "fixtures/base-traits/PropHelpers.nash"); +base_snapshot!(cardano_value, "fixtures/base-traits/CardanoValue.nash"); +base_snapshot!(cardano_helpers, "fixtures/base-traits/Cardano.nash"); diff --git a/crates/nash-driver/tests/cardano.rs b/crates/nash-driver/tests/cardano.rs new file mode 100644 index 00000000..95e7056a --- /dev/null +++ b/crates/nash-driver/tests/cardano.rs @@ -0,0 +1,200 @@ +mod support; + +use nash_plutus::{arena::Arena, data::PlutusData, syn, term::Term}; +use std::path::Path; + +#[tokio::test] +async fn ledger_contexts() { + let source = include_str!("fixtures/cardano/Context.nash"); + let output = support::compile_validator(source).await; + let mut results = String::new(); + for index in 0..7 { + let arena = Arena::new(); + let file = format!("context-{index}.cbor"); + let bytes = std::fs::read( + Path::new(env!("CARGO_MANIFEST_DIR")) + .join("tests/fixtures/cardano/golden") + .join(&file), + ) + .unwrap(); + let data = PlutusData::from_cbor(&arena, &bytes).unwrap(); + let program = syn::parse_program(&arena, &output.uplc).unwrap(); + let result = program + .apply( + &arena, + Term::data( + &arena, + PlutusData::constr( + &arena, + 0, + arena.alloc_slice_copy(&[data, PlutusData::byte_string(&arena, &bytes)]), + ), + ), + ) + .eval(&arena); + assert!(result.term.is_ok(), "{file}: {:?}", result.term); + results.push_str(&format!("{file}: {:?}\n", result.term)); + } + insta::with_settings!({description => source, omit_expression => true}, { + insta::assert_snapshot!(results); + }); +} + +#[tokio::test] +async fn ledger_interval_semantics() { + let source = include_str!("fixtures/cardano/Intervals.nash"); + let output = support::compile_validator(source).await; + let fixtures = Path::new(env!("CARGO_MANIFEST_DIR")).join("tests/fixtures/cardano/golden"); + let arena = Arena::new(); + let range_bytes = std::fs::read(fixtures.join("intervals.cbor")).unwrap(); + let result_bytes = std::fs::read(fixtures.join("interval-results.cbor")).unwrap(); + let ranges_data = PlutusData::from_cbor(&arena, &range_bytes).unwrap(); + let PlutusData::List(ranges) = ranges_data else { + panic!("Haskell interval list") + }; + let PlutusData::Constr { fields, .. } = PlutusData::from_cbor(&arena, &result_bytes).unwrap() + else { + panic!("Haskell result tuple") + }; + let [ + PlutusData::List(empty), + PlutusData::List(members), + PlutusData::List(contains), + ] = *fields + else { + panic!("Haskell results") + }; + let mut results = String::new(); + for (index, range) in ranges.iter().enumerate() { + let evaluation_arena = Arena::new(); + let data = PlutusData::constr( + &evaluation_arena, + 0, + evaluation_arena.alloc_slice_copy(&[ + range, + empty[index], + PlutusData::list(&evaluation_arena, &members[index * 5..(index + 1) * 5]), + ranges_data, + PlutusData::list( + &evaluation_arena, + &contains[index * ranges.len()..(index + 1) * ranges.len()], + ), + ]), + ); + let program = syn::parse_program(&evaluation_arena, &output.uplc).unwrap(); + let result = program + .apply(&evaluation_arena, Term::data(&evaluation_arena, data)) + .eval(&evaluation_arena); + assert!(result.term.is_ok(), "interval {index}: {:?}", result.term); + results.push_str(&format!("{index}: {range:?} => {:?}\n", result.term)); + } + insta::with_settings!({description => source, omit_expression => true}, { + insta::assert_snapshot!(results); + }); +} + +#[tokio::test] +async fn malformed_contexts() { + let source = include_str!("fixtures/cardano/Validate.nash"); + let output = support::compile_validator(source).await; + let arena = Arena::new(); + let bytes = include_bytes!("fixtures/cardano/golden/context-0.cbor"); + let valid = PlutusData::from_cbor(&arena, bytes).unwrap(); + let PlutusData::Constr { fields, .. } = valid else { + unreachable!() + }; + let PlutusData::Constr { + fields: tx_fields, .. + } = fields[0] + else { + unreachable!() + }; + let mut cases = vec![ + ("valid context".to_string(), valid, true), + ( + "wrong context tag".into(), + PlutusData::constr(&arena, 1, fields), + false, + ), + ( + "missing context field".into(), + PlutusData::constr(&arena, 0, &fields[..2]), + false, + ), + ( + "extra context field".into(), + PlutusData::constr( + &arena, + 0, + arena.alloc_slice_copy(&[fields[0], fields[1], fields[2], fields[2]]), + ), + false, + ), + ]; + for (index, name) in [ + "inputs", + "reference inputs", + "outputs", + "fee", + "mint", + "certificates", + "withdrawals", + "validity range", + "signatories", + "redeemers", + "datums", + "transaction id", + "votes", + "proposals", + "current treasury", + "treasury donation", + ] + .iter() + .enumerate() + { + let mut bad_tx = tx_fields.to_vec(); + bad_tx[index] = if matches!(bad_tx[index], PlutusData::Integer(_)) { + PlutusData::byte_string(&arena, b"wrong") + } else { + PlutusData::integer_from(&arena, 0) + }; + let mut bad_context = fields.to_vec(); + bad_context[0] = PlutusData::constr(&arena, 0, arena.alloc_slice_copy(&bad_tx)); + cases.push(( + format!("wrong {name} shape"), + PlutusData::constr(&arena, 0, arena.alloc_slice_copy(&bad_context)), + false, + )); + } + let mut results = String::new(); + for (name, data, expected) in cases { + let evaluation_arena = Arena::new(); + let program = syn::parse_program(&evaluation_arena, &output.uplc).unwrap(); + let result = program + .apply(&evaluation_arena, Term::data(&evaluation_arena, data)) + .eval(&evaluation_arena); + assert_eq!(result.term.is_ok(), expected, "{name}: {:?}", result.term); + results.push_str(&format!("{name}: {:?}\n", result.term)); + } + insta::with_settings!({description => source, omit_expression => true}, { + insta::assert_snapshot!(results); + }); +} + +#[tokio::test] +async fn ledger_value_operations() { + let source = include_str!("fixtures/cardano/Values.nash"); + let output = support::compile_validator(source).await; + let arena = Arena::new(); + let bytes = std::fs::read( + Path::new(env!("CARGO_MANIFEST_DIR")).join("tests/fixtures/cardano/golden/values.cbor"), + ) + .unwrap(); + let data = PlutusData::from_cbor(&arena, &bytes).unwrap(); + let program = syn::parse_program(&arena, &output.uplc).unwrap(); + let result = program.apply(&arena, Term::data(&arena, data)).eval(&arena); + assert!(result.term.is_ok(), "{:?}", result.term); + insta::with_settings!({description => source, omit_expression => true}, { + insta::assert_snapshot!(format!("{:?}", result.term)); + }); +} diff --git a/crates/nash-driver/tests/fixtures/base-traits/Cardano.nash b/crates/nash-driver/tests/fixtures/base-traits/Cardano.nash new file mode 100644 index 00000000..19382c11 --- /dev/null +++ b/crates/nash-driver/tests/fixtures/base-traits/Cardano.nash @@ -0,0 +1,63 @@ +module Main exposing (..) + +import Cardano.Time as Time +import Cardano.Tx as Tx +import Cardano.Address as Address +import Cardano.Value as Value exposing (Value) + +checkBool : Data -> bool +checkBool raw = let checked : Bool + checked = validate raw + in lower checked +checkOption : Data -> option Int +checkOption raw = let checked : Option Int + checked = validate raw + in lower checked +checkCommittee raw = let checked : Tx.Committee + checked = validate raw + in toData checked +checkAddress raw = let checked : Address.Address + checked = validate raw + in toData checked +checkValue raw = let checked : Value + checked = validate raw + in toData checked + +committee = Tx.Committee (Builtin.mapData []) (Tx.Rational 2 3) + +bigStart : Int +bigStart = lift (-10) + +tests + test "closed interval builders accept Big endpoints" = do + assert (Time.member bigStart (Time.interval bigStart 20)) + assert (Time.member 20 (Time.interval bigStart 20)) + assert (Bool.not (Time.member 21 (Time.interval bigStart 20))) + assert (Time.contains Time.always (Time.interval bigStart 20)) + assert (Time.contains (Time.from bigStart) (Time.to -20) == False) + assert (Time.contains (Time.interval 0 1) Time.never) + assert (Time.isEmpty Time.never) + assert (Time.member -999 (Time.to 20)) + assert (Time.member 999 (Time.from 20)) + test "committee validates rational tuple representation" = do + assert (checkCommittee (toData committee) == toData committee) + test "Bool invalid tag" fail = do + let _ = checkBool (Builtin.constrData 2 []) in () + test "Bool extra field" fail = do + let _ = checkBool (Builtin.constrData 0 [Data.Encode.int 1]) in () + test "Option missing field" fail = do + let _ = checkOption (Builtin.constrData 0 []) in () + test "Option wrong element" fail = do + let _ = checkOption (Builtin.constrData 0 [Data.Encode.bytes #"aa"]) in () + test "Option extra field" fail = do + let _ = checkOption (Builtin.constrData 1 [Data.Encode.int 1]) in () + test "record rejects wrong tag" fail = do + let _ = checkAddress (Builtin.constrData 1 [toData (Address.PubKeyCredential (lift #"aa")), Builtin.constrData 1 []]) in () + test "record rejects missing field" fail = do + let _ = checkAddress (Builtin.constrData 0 []) in () + test "record rejects extra field" fail = do + let _ = checkAddress (Builtin.constrData 0 [toData (Address.PubKeyCredential (lift #"aa")), Builtin.constrData 1 [], Data.Encode.int 1]) in () + test "record rejects invalid nested credential" fail = do + let _ = checkAddress (Builtin.constrData 0 [Builtin.constrData 2 [], Builtin.constrData 1 []]) in () + test "Value rejects non-map" fail = do + let _ = checkValue (Data.Encode.int 1) in () diff --git a/crates/nash-driver/tests/fixtures/base-traits/CardanoValue.nash b/crates/nash-driver/tests/fixtures/base-traits/CardanoValue.nash new file mode 100644 index 00000000..38b9913a --- /dev/null +++ b/crates/nash-driver/tests/fixtures/base-traits/CardanoValue.nash @@ -0,0 +1,23 @@ +module Main exposing (..) + +big : Cardano.Value.Value +big = lift (Cardano.Value.singleton #"aa" #"01" 5) + +tests + test "native value operations accept Big and little values" = do + assert (Cardano.Value.quantityOf #"aa" #"01" big == 5) + assert (Cardano.Value.quantityOf #"aa" #"02" big == 0) + assert (Cardano.Value.lovelace (Cardano.Value.singleton #"" #"" 100) == 100) + assert (Cardano.Value.quantityOf #"aa" #"01" (Cardano.Value.add big big) == 10) + assert (Cardano.Value.quantityOf #"aa" #"01" (Cardano.Value.scale -2 big) == -10) + assert (Cardano.Value.contains (Cardano.Value.scale 2 big) big) + assert (Cardano.Value.quantityOf #"aa" #"01" (Cardano.Value.insert #"aa" #"01" 0 big) == 0) + test "wire representation is a nested map" = do + assert (Data.serialise (toData big) == #"a141aaa1410105") + assert (Cardano.Value.lovelace Cardano.Value.empty == 0) + assert (Builtin.valueData (lower big) == toData big) + test "zero quantities are removed by native arithmetic" = do + assert (Cardano.Value.scale 0 big == Cardano.Value.empty) + assert (Cardano.Value.add big (Cardano.Value.scale -1 big) == Cardano.Value.empty) + test "native containment rejects negative quantities" fail = do + assert (Cardano.Value.contains (Cardano.Value.scale -1 big) (Cardano.Value.scale -1 big)) diff --git a/crates/nash-driver/tests/fixtures/cardano/Context.nash b/crates/nash-driver/tests/fixtures/cardano/Context.nash new file mode 100644 index 00000000..16d49051 --- /dev/null +++ b/crates/nash-driver/tests/fixtures/cardano/Context.nash @@ -0,0 +1,109 @@ +validator module Context exposing (main) + +import Cardano.Tx exposing (..) +import Cardano.Address exposing (..) +import Cardano.Time as Time +import Cardano.Value as Value exposing (Value) + +integer : int -> Int +integer = lift + +bytes : bytes -> Bytes +bytes = lift + +key = PubKeyCredential (lift #"6b6579") +script = ScriptCredential (lift #"736372697074") +ref = OutputReference (lift #"7478") 2 +rep = DRep key + +certificates : List Certificate +certificates = lift + [ RegStaking key Option.None + , UnRegStaking script (Option.Some 12) + , DelegStaking key (DelegStake (lift #"706f6f6c")) + , RegDeleg key (DelegVote DRepAlwaysAbstain) 13 + , RegDRep key 14 + , UpdateDRep script + , UnRegDRep key 15 + , PoolRegister (lift #"706f6f6c") (lift #"767266") + , PoolRetire (lift #"706f6f6c") 16 + , AuthHotCommittee key script + , ResignColdCommittee script + , DelegStaking key (DelegStakeVote (lift #"706f6f6c") rep) + , DelegStaking key (DelegVote DRepAlwaysNoConfidence) + ] +actionId = GovernanceActionId (lift #"70726576696f7573") 3 + +actions : list GovernanceAction +actions = + [ ParameterChange Option.None (toData (Builtin.mapData (Map.singleton (integer 0) (integer 42)))) (Option.Some (lift #"6775617264")) + , HardForkInitiation (Option.Some actionId) (ProtocolVersion 11 0) + , TreasuryWithdrawals (lift (Map.singleton key (integer 17))) Option.None + , NoConfidence (Option.Some actionId) + , UpdateCommittee Option.None (lift [key]) (lift (Map.singleton script (integer 18))) (Rational 2 3) + , NewConstitution (Option.Some actionId) (Constitution Option.None) + , InfoAction + , NewConstitution Option.None (Constitution (Option.Some (lift #"636f6e737469747574696f6e"))) + ] +proposals : List Proposal +proposals = lift (List.map (Proposal 19 key) actions) +proposal = Proposal 19 key InfoAction +voters = [CommitteeVoter key, DRepVoter script, StakePoolVoter (lift #"706f6f6c")] +purposes = [Minting (lift #"706f6c696379"), Spending ref, Rewarding key, Certifying 0 (RegStaking key Option.None), Voting (CommitteeVoter key), Proposing 6 proposal] +infos = [MintingScript (lift #"706f6c696379"), SpendingScript ref (Option.Some (toData (integer 99))), RewardingScript key, CertifyingScript 0 (RegStaking key Option.None), VotingScript (CommitteeVoter key), ProposingScript 6 proposal] +coin : Value +coin = lift (Value.singleton #"" #"" 100) +mint : Value +mint = lift (Value.singleton #"706f6c696379" #"6173736574" -2) +addresses = [Address key Option.None, Address script (Option.Some (StakingHash key)), Address key (Option.Some (StakingPtr 20 21 22))] +datums = [NoDatum, DatumHash (lift #"646174756d"), InlineDatum (toData (integer 23))] +outputs = List.map2 (\address datum -> Output address coin datum (Option.Some (lift #"7265662d736372697074"))) addresses datums +firstOutput = Output (Address key Option.None) coin NoDatum (Option.Some (lift #"7265662d736372697074")) +lastOutput = Output (Address key (Option.Some (StakingPtr 20 21 22))) coin (InlineDatum (toData (integer 23))) (Option.Some (lift #"7265662d736372697074")) + +tx = Tx + (lift [Input ref firstOutput]) + (lift [Input (OutputReference (lift #"7265666572656e6365") 1) lastOutput]) + (lift outputs) + 2 + mint + certificates + (lift (Map.singleton key (integer 24))) + (Time.Interval (Time.IntervalBound (Time.Finite 10) Bool.False) (Time.IntervalBound (Time.Finite 30) Bool.True)) + (lift [lift #"6b6579"]) + (lift (List.map (\purpose -> Builtin.mkPairData purpose (toData (integer 25))) purposes)) + (lift (Map.singleton (bytes #"646174756d") (toData (integer 23)))) + (lift #"7472616e73616374696f6e") + (lift (List.map2 (\voter vote -> Builtin.mkPairData voter (Builtin.mapData (Map.singleton actionId vote))) voters [VoteNo, VoteYes, Abstain])) + proposals + (Option.Some 26) + Option.None + +main : Data -> unit +main packet = + case packet of + Constr pair(0, [raw, B encoded]) -> check raw encoded + _ -> fail + +check : Data -> bytes -> unit +check raw encoded = + let + context : ScriptContext + context = validate raw + index = Data.tag (toData context.scriptInfo) + expected = + case context.scriptInfo of + SpendingScript _ Option.None -> + ScriptContext + { tx | currentTreasury = Option.None, treasuryDonation = (Option.Some 28), outputs = (lift [Output (Address key Option.None) coin NoDatum Option.None]) } + (toData (integer 27)) + (SpendingScript ref Option.None) + _ -> ScriptContext tx (toData (integer 27)) (List.at index infos |> Option.unwrap) + _ = assert (context == expected) + _ = assert (Data.serialise (toData expected) == encoded) + _ = assert (Value.lovelace (List.at 0 context.tx.outputs |> Option.unwrap).value == 100) + _ = assert (Value.quantityOf #"706f6c696379" #"6173736574" context.tx.mint == -2) + _ = assert (Time.member 11 context.tx.validityRange) + _ = assert (Bool.not (Time.member 10 context.tx.validityRange)) + in + () diff --git a/crates/nash-driver/tests/fixtures/cardano/Intervals.nash b/crates/nash-driver/tests/fixtures/cardano/Intervals.nash new file mode 100644 index 00000000..580c20a3 --- /dev/null +++ b/crates/nash-driver/tests/fixtures/cardano/Intervals.nash @@ -0,0 +1,24 @@ +validator module Intervals exposing (main) + +import Cardano.Time as Time exposing (Interval) + +main : Data -> unit +main raw = + case raw of + Constr pair(0, [rangeData, emptyData, memberData, rangesData, containsData]) -> + let + range : Interval + range = validate rangeData + expectedEmpty : Bool + expectedEmpty = validate emptyData + expectedMembers : List Bool + expectedMembers = validate memberData + ranges : List Interval + ranges = validate rangesData + expectedContains : List Bool + expectedContains = validate containsData + _ = assert (Time.isEmpty range == lower expectedEmpty) + _ = assert (List.map (\point -> Time.member point range) [-2, -1, 0, 1, 2] == List.map lower expectedMembers) + in + assert (List.map (Time.contains range) ranges == List.map lower expectedContains) + _ -> fail diff --git a/crates/nash-driver/tests/fixtures/cardano/Validate.nash b/crates/nash-driver/tests/fixtures/cardano/Validate.nash new file mode 100644 index 00000000..3f0a03b4 --- /dev/null +++ b/crates/nash-driver/tests/fixtures/cardano/Validate.nash @@ -0,0 +1,11 @@ +validator module Validate exposing (main) + +import Cardano.Tx exposing (ScriptContext) + +main : Data -> unit +main raw = + let + context : ScriptContext + context = validate raw + in + () diff --git a/crates/nash-driver/tests/fixtures/cardano/Values.nash b/crates/nash-driver/tests/fixtures/cardano/Values.nash new file mode 100644 index 00000000..8b9cfe04 --- /dev/null +++ b/crates/nash-driver/tests/fixtures/cardano/Values.nash @@ -0,0 +1,22 @@ +validator module Values exposing (main) + +import Cardano.Value as Value + +coin = Value.singleton #"706f6c696379" #"6173736574" 5 +quantity value = Value.quantityOf #"706f6c696379" #"6173736574" value + +main : Data -> unit +main raw = + case raw of + Constr pair(0, [amountData, comparisonData]) -> + let + amounts : List Int + amounts = validate amountData + comparisons : List Bool + comparisons = validate comparisonData + actualAmounts = [quantity coin, Value.quantityOf #"706f6c696379" #"6d697373696e67" coin, Value.quantityOf #"6d697373696e67" #"6173736574" coin, quantity (Value.add coin coin), quantity (Value.scale -2 coin), quantity (Value.singleton #"706f6c696379" #"6173736574" 0)] + actualComparisons = [Value.contains (Value.scale 2 coin) coin, Value.contains coin (Value.scale 2 coin), Value.contains coin (Value.singleton #"706f6c696379" #"6d697373696e67" 1)] + _ = assert (actualAmounts == List.map lower amounts) + in + assert (actualComparisons == List.map lower comparisons) + _ -> fail diff --git a/crates/nash-driver/tests/fixtures/cardano/golden/context-0.cbor b/crates/nash-driver/tests/fixtures/cardano/golden/context-0.cbor new file mode 100644 index 0000000000000000000000000000000000000000..ffffe733dce2c421a1ee74462054f5498b464d24 GIT binary patch literal 1130 zcma)6+e*Vg5Unlx;Qj8ZAowcy529AQ{X|Hk1hpwiDkjmIKUsp{H?3bYp4m;4O%${w zC3AM>%$bvERU%a)`Nnk`zjVnw;`{8uOGu?T6(_}s7^ox%cpZ9o$B`TQqnPk13$K8B zp*M8BnRY#hQ@OddjpULilRKRBJJY}VB8jh5UX)5opwwKqN@aiG#N($Q{h9@a>~E3X z;$jp$`fgJ6ohb6+LoLH{3X+&xWM%N0_OU^u*QG_%>IB15EzdYsSR<~ZvfFES#D?kLku@Mo p)6JVhz-b)RWAkqEp8>90HyB2-ACK90bkL&29x;p9N5XUo=o|L+bgKXW literal 0 HcmV?d00001 diff --git a/crates/nash-driver/tests/fixtures/cardano/golden/context-1.cbor b/crates/nash-driver/tests/fixtures/cardano/golden/context-1.cbor new file mode 100644 index 0000000000000000000000000000000000000000..6f14a9615ac3942ade22e2d5f0ce169046a46449 GIT binary patch literal 1137 zcma)6+e*Vg5REPR;QiiJLGV@ZAH-Vm_7fpZC8$lwMNFb?{$vS)-?VNOKI+20~N z#nr@r@|>g?*kR~KCrXCl6eKlOtx72}iqd8#{Bq*^PXOO)kaBXlCdGS&txGXAnWUIi zXB+Zs(b7Eqmtch5g5GW7wNr%|I)HbZY=)#vCu>8LO4w&=hP*5%OMkjxOb4UFvcy#2 zA$E2wsSl}J{RCK=CD{Rvu~*|oyShB>-ngX+tAQg_nE||%1Z2vWu;2N?#0=fwW zA-01dnP*0*#+xh@uwu0XGhP|IrF?AAsBvl0v?{@{RLRqh6-JBO$n1366)|DzcVsk3 s(p2$g6L1;_b=x$X^k;x|@A%^|@}ii0M+GH{9ZF(hN5;J)mN_{720W>GUjP6A literal 0 HcmV?d00001 diff --git a/crates/nash-driver/tests/fixtures/cardano/golden/context-2.cbor b/crates/nash-driver/tests/fixtures/cardano/golden/context-2.cbor new file mode 100644 index 0000000000000000000000000000000000000000..c95b42303e2ce18d0f7080e03b6cd3fa70d13605 GIT binary patch literal 1131 zcma)6+e*Vg5Unlx;QiiJLGV@ZA4IKq`-zZ732IZ4R7~O}f3gI@Z(6@*JhK~UMG&u4o|j5WpwwKKO66ea#FNLVdd&nw_P5Ax zaXtMBK2OK zto{~qk>kdGF#53gwC;;>=so$tB^w6^tiNWT9sf}s^uBS3aiC+WOjS)ikL9{JF*&N oX}Wl`2{?^|x^13K{xiV3af4A5`|*T*M+GfP>=LtxJ^QBV8*@T-QUCw| literal 0 HcmV?d00001 diff --git a/crates/nash-driver/tests/fixtures/cardano/golden/context-3.cbor b/crates/nash-driver/tests/fixtures/cardano/golden/context-3.cbor new file mode 100644 index 0000000000000000000000000000000000000000..b8890e420678f0acc846b7dc70948eb700c6a298 GIT binary patch literal 1139 zcma)6+e*Vg5Unlx;QiiJLGV@ZA4IKq`-zZ732IZ4R7~O}f3gI@Z(6@*JhK~UMG&u4o|j5WpwwKKO66ea#FNLVdd&nw_P5Ax zaXtMBK2OK zto{~qk>kdGF#53gwC;;>=so$tB^w6^tiNWT9sf}s^uBS3aiC+WOjS)ikL9{JF*&N pX}Wl`2{?^|x^13K{xiV3af4A5`|*T*M+GfP>=Ltxz2EMZz5y@pdlUcw literal 0 HcmV?d00001 diff --git a/crates/nash-driver/tests/fixtures/cardano/golden/context-4.cbor b/crates/nash-driver/tests/fixtures/cardano/golden/context-4.cbor new file mode 100644 index 0000000000000000000000000000000000000000..d06caf920b5006f9590b88006843a87041dd2827 GIT binary patch literal 1135 zcma)6+e*Vg5Unlx;QiiJLGV@ZA4IKq`-zZ732IZ4R7~O}f3gI@Z(6@*JhK~UMG&u4o|j5WpwwKKO66ea#FNLVdd&nw_P5Ax zaXtMBK2OK zto{~qk>kdGF#53gwC;;>=so$tB^w6^tiNWT9sf}s^uBS3aiC+WOjS)ikL9{JF*&N oX}Wl`2{?^|x^13K{xiV3af4A5`|*T*M+GfP>=Ltxz4k5AH&exVLjV8( literal 0 HcmV?d00001 diff --git a/crates/nash-driver/tests/fixtures/cardano/golden/context-5.cbor b/crates/nash-driver/tests/fixtures/cardano/golden/context-5.cbor new file mode 100644 index 0000000000000000000000000000000000000000..1a7b1f52171c605acbc9d5e1f8f274489b72e411 GIT binary patch literal 1140 zcma)6+e*Vg5Unlx;QiiJLGV@ZA4IKq`-zZ732IZ4R7~O}f3gI@Z(6@*JhK~UMG&u4o|j5WpwwKKO66ea#FNLVdd&nw_P5Ax zaXtMBK2OK zto{~qk>kdGF#53gwC;;>=so$tB^w6^tiNWT9sf}s^uBS3aiC+WOjS)ikL9{JF*&N pX}Wl`2{?^|x^13K{xiV3af4A5`|*T*M+GfP>=LtxyJMSnm34&IMiHjz0Nz|@ z(>%iXe7mS=08 z))ZkeMn>f@fJ=}VS)|*rPgj^&qL|UndIK|@e6(Id-*f}h3LVe=*7&6E$FXlu9d8me zSum?9xAZSymO!A|6Zfp=#tHXPO5nzmtY+K-?Y&J*f55}jBV^<2EF7>~s|LntG2ml4 z$2A&xT`Dw{N*LHo%2N}|v=&d0*=SZP;)cnkYYnnAS$w?-Od5`|ZN8iQCE~nVo*RTt UIN`aY0z^rdafRhvYI~Qoe+gbNrT_o{ literal 0 HcmV?d00001 diff --git a/crates/nash-driver/tests/fixtures/cardano/golden/interval-results.cbor b/crates/nash-driver/tests/fixtures/cardano/golden/interval-results.cbor new file mode 100644 index 00000000..e5ff80c1 --- /dev/null +++ b/crates/nash-driver/tests/fixtures/cardano/golden/interval-results.cbor @@ -0,0 +1 @@ +ØyŸŸØy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øy€Øz€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øy€Øz€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øy€Øy€Øy€Øy€Øy€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øy€Øy€Øy€Øy€ÿŸØy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øz€Øy€Øy€Øy€Øy€Øz€Øz€Øy€Øy€Øy€Øz€Øy€Øy€Øy€Øy€Øz€Øz€Øy€Øy€Øy€Øz€Øz€Øy€Øy€Øy€Øz€Øz€Øz€Øy€Øy€Øz€Øz€Øy€Øy€Øy€Øz€Øz€Øz€Øy€Øy€Øz€Øz€Øz€Øy€Øy€Øz€Øz€Øz€Øz€Øy€Øz€Øz€Øz€Øy€Øy€Øz€Øz€Øz€Øz€Øy€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øz€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øz€Øy€Øy€Øy€Øz€Øy€Øy€Øy€Øy€Øz€Øz€Øy€Øy€Øy€Øy€Øz€Øy€Øy€Øy€Øy€Øz€Øz€Øy€Øy€Øz€Øz€Øy€Øy€Øy€Øz€Øz€Øz€Øy€Øy€Øy€Øz€Øz€Øz€Øy€Øy€Øz€Øz€Øz€Øy€Øz€Øz€Øz€Øz€Øy€Øz€Øz€Øz€Øz€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øz€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øz€Øy€Øy€Øy€Øz€Øy€Øy€Øy€Øy€Øz€Øz€Øy€Øy€Øy€Øy€Øz€Øz€Øy€Øy€Øy€Øz€Øz€Øy€Øy€Øz€Øz€Øz€Øy€Øy€Øz€Øz€Øz€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øz€Øy€Øy€Øy€Øy€Øy€Øz€Øy€Øy€Øy€Øy€Øz€Øy€Øy€Øy€Øz€Øz€Øy€Øy€Øy€Øz€Øz€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€ÿŸØz€Øz€Øz€Øz€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øy€Øz€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øy€Øz€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øy€Øy€Øy€Øy€Øy€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øy€Øy€Øy€Øy€Øz€Øz€Øz€Øz€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øy€Øz€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øy€Øz€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øy€Øy€Øy€Øy€Øy€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øy€Øy€Øy€Øy€Øz€Øz€Øz€Øz€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øy€Øz€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øy€Øz€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øy€Øy€Øy€Øy€Øy€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øy€Øy€Øy€Øy€Øz€Øz€Øz€Øz€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øy€Øz€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øy€Øz€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øy€Øy€Øy€Øy€Øy€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øy€Øy€Øy€Øy€Øz€Øz€Øz€Øz€Øz€Øy€Øz€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øy€Øz€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øy€Øz€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øy€Øy€Øy€Øy€Øy€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øy€Øy€Øy€Øy€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øy€Øz€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øy€Øz€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øy€Øz€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øy€Øy€Øy€Øy€Øy€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øy€Øy€Øy€Øy€Øz€Øz€Øz€Øz€Øz€Øy€Øz€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øy€Øz€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øy€Øz€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øy€Øy€Øy€Øy€Øy€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øy€Øy€Øy€Øy€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øy€Øz€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øy€Øz€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øy€Øz€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øy€Øy€Øy€Øy€Øy€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øy€Øy€Øy€Øy€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øy€Øz€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øy€Øz€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øy€Øz€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øy€Øy€Øy€Øy€Øy€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øy€Øy€Øy€Øy€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øy€Øz€Øy€Øy€Øy€Øy€Øy€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øy€Øz€Øy€Øy€Øy€Øy€Øy€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øy€Øz€Øy€Øy€Øy€Øy€Øy€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øy€Øy€Øy€Øy€Øy€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øy€Øy€Øy€Øy€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øy€Øz€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øy€Øz€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øy€Øz€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øy€Øy€Øy€Øy€Øy€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øy€Øy€Øy€Øy€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øy€Øz€Øy€Øy€Øy€Øy€Øy€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øy€Øz€Øy€Øy€Øy€Øy€Øy€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øy€Øz€Øy€Øy€Øy€Øy€Øy€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øy€Øy€Øy€Øy€Øy€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øy€Øy€Øy€Øy€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øy€Øz€Øy€Øy€Øy€Øy€Øy€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øy€Øz€Øy€Øy€Øy€Øy€Øy€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øy€Øz€Øy€Øy€Øy€Øy€Øy€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øy€Øy€Øy€Øy€Øy€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øy€Øy€Øy€Øy€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øy€Øy€Øy€Øy€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øy€Øy€Øy€Øy€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øy€Øy€Øy€Øy€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øy€Øy€Øy€Øy€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øy€Øy€Øy€Øy€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øy€Øz€Øy€Øy€Øy€Øy€Øy€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øy€Øz€Øy€Øy€Øy€Øy€Øy€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øy€Øz€Øy€Øy€Øy€Øy€Øy€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øy€Øy€Øy€Øy€Øy€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øy€Øy€Øy€Øy€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øy€Øy€Øy€Øy€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øy€Øy€Øy€Øy€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øy€Øy€Øy€Øy€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øy€Øy€Øy€Øy€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øy€Øy€Øy€Øy€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øy€Øz€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øy€Øz€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øy€Øy€Øy€Øy€Øy€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øy€Øz€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øy€Øz€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øy€Øy€Øy€Øy€Øy€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øy€Øz€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øy€Øz€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øy€Øy€Øy€Øy€Øy€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øy€Øz€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øy€Øz€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øy€Øy€Øy€Øy€Øy€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øy€Øz€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øy€Øz€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øy€Øy€Øy€Øy€Øy€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øy€Øz€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øy€Øz€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øy€Øy€Øy€Øy€Øy€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øy€Øz€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øy€Øz€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øy€Øy€Øy€Øy€Øy€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øy€Øz€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øy€Øz€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øy€Øy€Øy€Øy€Øy€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øy€Øz€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øy€Øz€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øy€Øy€Øy€Øy€Øy€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øy€Øz€Øz€Øy€Øy€Øz€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øy€Øz€Øy€Øy€Øy€Øy€Øy€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øy€Øy€Øy€Øy€Øy€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øy€Øz€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øy€Øz€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øy€Øy€Øy€Øy€Øy€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øy€Øz€Øy€Øy€Øy€Øy€Øy€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øy€Øz€Øy€Øy€Øy€Øy€Øy€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øy€Øy€Øy€Øy€Øy€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øy€Øz€Øz€Øy€Øy€Øz€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øy€Øz€Øy€Øy€Øy€Øy€Øy€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øy€Øy€Øy€Øy€Øy€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øy€Øz€Øz€Øy€Øy€Øz€Øz€Øy€Øy€Øy€Øy€Øy€Øy€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øy€Øy€Øy€Øy€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øy€Øy€Øy€Øy€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øy€Øz€Øy€Øy€Øy€Øy€Øy€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øy€Øz€Øy€Øy€Øy€Øy€Øy€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øy€Øy€Øy€Øy€Øy€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øy€Øy€Øy€Øy€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øy€Øy€Øy€Øy€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øy€Øy€Øy€Øy€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øy€Øz€Øz€Øy€Øy€Øz€Øz€Øy€Øy€Øz€Øz€Øy€Øy€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øy€Øz€Øz€Øy€Øy€Øz€Øz€Øy€Øy€Øz€Øz€Øy€Øy€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øy€Øz€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øy€Øz€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øy€Øy€Øy€Øy€Øy€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øy€Øz€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øy€Øz€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øy€Øy€Øy€Øy€Øy€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øy€Øz€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øy€Øz€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øy€Øy€Øy€Øy€Øy€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øy€Øz€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øy€Øz€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øy€Øy€Øy€Øy€Øy€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øy€Øz€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øy€Øz€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øy€Øy€Øy€Øy€Øy€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øy€Øz€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øy€Øz€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øy€Øy€Øy€Øy€Øy€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øy€Øz€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øy€Øz€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øy€Øy€Øy€Øy€Øy€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øy€Øz€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øy€Øz€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øy€Øy€Øy€Øy€Øy€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øy€Øz€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øy€Øz€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øy€Øy€Øy€Øy€Øy€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øy€Øz€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øy€Øz€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øy€Øy€Øy€Øy€Øy€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øy€Øz€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øy€Øz€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øy€Øy€Øy€Øy€Øy€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øy€Øz€Øz€Øy€Øy€Øz€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øy€Øz€Øy€Øy€Øy€Øy€Øy€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øy€Øy€Øy€Øy€Øy€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øy€Øz€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øy€Øz€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øy€Øy€Øy€Øy€Øy€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øy€Øz€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øy€Øz€Øz€Øy€Øy€Øy€Øy€Øy€Øy€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øy€Øy€Øy€Øy€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øy€Øz€Øz€Øy€Øy€Øz€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øy€Øz€Øy€Øy€Øy€Øy€Øy€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øy€Øy€Øy€Øy€Øy€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øy€Øz€Øz€Øy€Øy€Øz€Øz€Øy€Øy€Øy€Øy€Øy€Øy€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øy€Øy€Øy€Øy€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øy€Øy€Øy€Øy€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øy€Øz€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øy€Øz€Øz€Øy€Øy€Øz€Øz€Øy€Øy€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øy€Øz€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øy€Øz€Øz€Øy€Øy€Øz€Øz€Øy€Øy€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øy€Øz€Øz€Øy€Øy€Øz€Øz€Øy€Øy€Øz€Øz€Øy€Øy€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øy€Øz€Øz€Øy€Øy€Øz€Øz€Øy€Øy€Øz€Øz€Øy€Øy€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øy€Øz€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øy€Øz€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øy€Øy€Øy€Øy€Øy€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øy€Øz€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øy€Øz€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øy€Øy€Øy€Øy€Øy€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øy€Øz€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øy€Øz€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øy€Øy€Øy€Øy€Øy€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øy€Øz€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øy€Øz€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øy€Øy€Øy€Øy€Øy€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øy€Øz€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øy€Øz€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øy€Øy€Øy€Øy€Øy€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øy€Øz€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øy€Øz€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øy€Øy€Øy€Øy€Øy€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øy€Øz€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øy€Øz€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øy€Øy€Øy€Øy€Øy€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øy€Øz€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øy€Øz€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øy€Øy€Øy€Øy€Øy€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øy€Øz€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øy€Øz€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øy€Øy€Øy€Øy€Øy€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øy€Øz€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øy€Øz€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øy€Øy€Øy€Øy€Øy€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øy€Øz€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øy€Øz€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øy€Øy€Øy€Øy€Øy€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øy€Øz€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øy€Øz€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øy€Øy€Øy€Øy€Øy€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øy€Øz€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øy€Øz€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øy€Øy€Øy€Øy€Øy€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øy€Øz€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øy€Øz€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øy€Øy€Øy€Øy€Øy€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øy€Øz€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øy€Øz€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øy€Øy€Øy€Øy€Øy€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øy€Øz€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øy€Øz€Øz€Øy€Øy€Øy€Øy€Øy€Øy€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øy€Øy€Øy€Øy€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øy€Øz€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øy€Øz€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øy€Øz€Øz€Øy€Øy€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øy€Øz€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øy€Øz€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øy€Øz€Øz€Øy€Øy€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øy€Øz€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øy€Øz€Øz€Øy€Øy€Øz€Øz€Øy€Øy€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øy€Øz€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øy€Øz€Øz€Øy€Øy€Øz€Øz€Øy€Øy€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øy€Øz€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øy€Øz€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øy€Øy€Øy€Øy€Øy€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øy€Øz€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øy€Øz€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øy€Øy€Øy€Øy€Øy€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øy€Øz€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øy€Øz€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øy€Øy€Øy€Øy€Øy€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øy€Øz€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øy€Øz€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øy€Øy€Øy€Øy€Øy€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øy€Øz€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øy€Øz€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øy€Øy€Øy€Øy€Øy€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øy€Øz€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øy€Øz€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øy€Øy€Øy€Øy€Øy€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øy€Øz€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øy€Øz€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øy€Øy€Øy€Øy€Øy€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øy€Øz€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øy€Øz€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øy€Øy€Øy€Øy€Øy€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øy€Øz€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øy€Øz€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øy€Øy€Øy€Øy€Øy€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øy€Øz€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øy€Øz€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øy€Øy€Øy€Øy€Øy€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øy€Øz€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øy€Øz€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øy€Øy€Øy€Øy€Øy€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øy€Øz€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øy€Øz€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øy€Øy€Øy€Øy€Øy€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øy€Øz€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øy€Øz€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øy€Øy€Øy€Øy€Øy€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øy€Øz€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øy€Øz€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øy€Øy€Øy€Øy€Øy€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øy€Øz€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øy€Øz€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øy€Øy€Øy€Øy€Øy€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øy€Øz€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øy€Øz€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øy€Øy€Øy€Øy€Øy€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øy€Øz€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øy€Øz€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øy€Øy€Øy€Øy€Øy€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øy€Øz€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øy€Øz€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øy€Øy€Øy€Øy€Øy€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øy€Øz€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øy€Øz€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øy€Øy€Øy€Øy€Øy€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øy€Øz€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øy€Øz€Øy€Øy€Øy€Øy€Øy€Øy€Øy€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øy€Øy€Øy€Øy€Øy€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€Øz€ÿÿ \ No newline at end of file diff --git a/crates/nash-driver/tests/fixtures/cardano/golden/intervals.cbor b/crates/nash-driver/tests/fixtures/cardano/golden/intervals.cbor new file mode 100644 index 0000000000000000000000000000000000000000..7ea9162918993f070753c448474e89f17c3a6a66 GIT binary patch literal 2642 zcmZ`(+i}A%3`;lhnsI)-iPp+CE@M(808w}Sa0LS31DElAkLU0DzOTE!{L>(OKd&3! z?Z39Z-nl;X&v6)!ln`M~Xf+%}n3HO+t&bthNwtN82y@csZ|h?Sb5d>LAi|vR`8=2K zl@GX!y>opiE%o&d^>ef9B?VDVsChnSR}ZHU<>baV0q8UpI>lMH#kPKY)v5EF`5c^u z>7SVO-1{}WoR6=|ew+#RYk0BEb2Pj*8R*8@Krc>*Ahbu{U6&6+loNexK74DTrkvd0 zVJpYIm(%RDZc>VK@-4RYbA94vFRo8m%=TnQ`$TN(r+3Er^v*b6!%M%07c=Os%T4o9 zUJWOll;b^h`5=UL;k|A+;bX~BPTu6Ob6`HoY0XD@t@#ja>*pJ*44|LyjIfws%?J85 zyqwSVNfR;cGyNJKV#*6my>(63+&c6cP9fmqb?c?+o$Et~e>uF9^>?Fm^tyty9t|hl wHAkq%X%J-NvvAUfbAW1mO+hw33nzUHVNQGfaP!~gj=sv2%og3$l}0C`;t*Z=?k literal 0 HcmV?d00001 diff --git a/crates/nash-driver/tests/fixtures/cardano/reference/Main.hs b/crates/nash-driver/tests/fixtures/cardano/reference/Main.hs new file mode 100644 index 00000000..93e3b80a --- /dev/null +++ b/crates/nash-driver/tests/fixtures/cardano/reference/Main.hs @@ -0,0 +1,80 @@ +{-# LANGUAGE OverloadedStrings #-} +module Main where + +import Codec.Serialise (serialise) +import qualified Data.ByteString.Lazy as LBS +import qualified PlutusLedgerApi.V3 as V +import qualified PlutusLedgerApi.V3.MintValue as Mint +import qualified PlutusLedgerApi.V1.Interval as Interval +import qualified PlutusTx as P +import qualified PlutusTx.AssocMap as M +import System.Directory (createDirectoryIfMissing) +import System.Environment (getArgs) + +key = V.PubKeyCredential (V.PubKeyHash "key") +script = V.ScriptCredential (V.ScriptHash "script") +ref = V.TxOutRef (V.TxId "tx") 2 +rep = V.DRep (V.DRepCredential key) +certificates = + [ V.TxCertRegStaking key Nothing + , V.TxCertUnRegStaking script (Just 12) + , V.TxCertDelegStaking key (V.DelegStake (V.PubKeyHash "pool")) + , V.TxCertRegDeleg key (V.DelegVote V.DRepAlwaysAbstain) 13 + , V.TxCertRegDRep (V.DRepCredential key) 14 + , V.TxCertUpdateDRep (V.DRepCredential script) + , V.TxCertUnRegDRep (V.DRepCredential key) 15 + , V.TxCertPoolRegister (V.PubKeyHash "pool") (V.PubKeyHash "vrf") + , V.TxCertPoolRetire (V.PubKeyHash "pool") 16 + , V.TxCertAuthHotCommittee (V.ColdCommitteeCredential key) (V.HotCommitteeCredential script) + , V.TxCertResignColdCommittee (V.ColdCommitteeCredential script) + , V.TxCertDelegStaking key (V.DelegStakeVote (V.PubKeyHash "pool") rep) + , V.TxCertDelegStaking key (V.DelegVote V.DRepAlwaysNoConfidence) + ] +actionId = V.GovernanceActionId (V.TxId "previous") 3 +actions = + [ V.ParameterChange Nothing (V.ChangedParameters (P.toBuiltinData (M.singleton (0 :: Integer) (42 :: Integer)))) (Just (V.ScriptHash "guard")) + , V.HardForkInitiation (Just actionId) (V.ProtocolVersion 11 0) + , V.TreasuryWithdrawals (M.unsafeFromList [(key, 17)]) Nothing + , V.NoConfidence (Just actionId) + , V.UpdateCommittee Nothing [V.ColdCommitteeCredential key] (M.unsafeFromList [(V.ColdCommitteeCredential script, 18)]) (V.unsafeRatio 2 3) + , V.NewConstitution (Just actionId) (V.Constitution Nothing) + , V.InfoAction + , V.NewConstitution Nothing (V.Constitution (Just (V.ScriptHash "constitution"))) + ] +proposals = map (V.ProposalProcedure 19 key) actions +proposal = proposals !! 6 +voters = [V.CommitteeVoter (V.HotCommitteeCredential key), V.DRepVoter (V.DRepCredential script), V.StakePoolVoter (V.PubKeyHash "pool")] +purposes = [V.Minting (V.CurrencySymbol "policy"), V.Spending ref, V.Rewarding key, V.Certifying 0 (head certificates), V.Voting (head voters), V.Proposing 6 proposal] +infos = [V.MintingScript (V.CurrencySymbol "policy"), V.SpendingScript ref (Just (V.Datum (P.toBuiltinData (99 :: Integer)))), V.RewardingScript key, V.CertifyingScript 0 (head certificates), V.VotingScript (head voters), V.ProposingScript 6 proposal] +coin = V.singleton (V.CurrencySymbol "") (V.TokenName "") 100 +mint = Mint.UnsafeMintValue (M.unsafeFromList [(V.CurrencySymbol "policy", M.unsafeFromList [(V.TokenName "asset", -2)])]) +addresses = [V.Address key Nothing, V.Address script (Just (V.StakingHash key)), V.Address key (Just (V.StakingPtr 20 21 22))] +outputs = zipWith (\address datum -> V.TxOut address coin datum (Just (V.ScriptHash "ref-script"))) addresses [V.NoOutputDatum, V.OutputDatumHash (V.DatumHash "datum"), V.OutputDatum (V.Datum (P.toBuiltinData (23 :: Integer)))] +tx = V.TxInfo + [V.TxInInfo ref (head outputs)] + [V.TxInInfo (V.TxOutRef (V.TxId "reference") 1) (last outputs)] + outputs 2 mint certificates (M.unsafeFromList [(key, 24)]) + (V.Interval (V.LowerBound (V.Finite 10) False) (V.UpperBound (V.Finite 30) True)) + [V.PubKeyHash "key"] + (M.unsafeFromList (zip purposes (repeat (V.Redeemer (P.toBuiltinData (25 :: Integer)))))) + (M.unsafeFromList [(V.DatumHash "datum", V.Datum (P.toBuiltinData (23 :: Integer)))]) + (V.TxId "transaction") + (M.unsafeFromList (zipWith (\voter vote -> (voter, M.unsafeFromList [(actionId, vote)])) voters [V.VoteNo, V.VoteYes, V.Abstain])) + proposals (Just 26) Nothing +contexts = map (V.ScriptContext tx redeemer) infos ++ [V.ScriptContext alternate redeemer (V.SpendingScript ref Nothing)] + where + redeemer = V.Redeemer (P.toBuiltinData (27 :: Integer)) + alternate = tx { V.txInfoCurrentTreasuryAmount = Nothing, V.txInfoTreasuryDonation = Just 28, V.txInfoOutputs = [V.TxOut (head addresses) coin V.NoOutputDatum Nothing] } + +main = do + [directory] <- getArgs + createDirectoryIfMissing True directory + mapM_ (\(index, context) -> LBS.writeFile (directory ++ "/context-" ++ show index ++ ".cbor") (serialise (P.toData context))) (zip [0 :: Int ..] contexts) + let ranges = [V.Interval (V.LowerBound lo lc) (V.UpperBound hi hc) | lo <- [V.NegInf, V.Finite (-1), V.Finite 0, V.Finite 1, V.PosInf], hi <- [V.NegInf, V.Finite (-1), V.Finite 0, V.Finite 1, V.PosInf], lc <- [False, True], hc <- [False, True]] :: [V.POSIXTimeRange] + LBS.writeFile (directory ++ "/intervals.cbor") (serialise (P.toData ranges)) + LBS.writeFile (directory ++ "/interval-results.cbor") (serialise (P.toData (map Interval.isEmpty ranges, [Interval.member n r | r <- ranges, n <- [-2..2]], [Interval.contains a b | a <- ranges, b <- ranges]))) + let v = V.singleton (V.CurrencySymbol "policy") (V.TokenName "asset") 5 + quantity x = V.valueOf x (V.CurrencySymbol "policy") (V.TokenName "asset") + amounts = [quantity v, V.valueOf v (V.CurrencySymbol "policy") (V.TokenName "missing"), V.valueOf v (V.CurrencySymbol "missing") (V.TokenName "asset"), quantity (V.unionWith (+) v v), quantity (V.scale (-2) v), quantity (V.singleton (V.CurrencySymbol "policy") (V.TokenName "asset") 0)] + comparisons = [V.geq (V.scale 2 v) v, V.geq v (V.scale 2 v), V.geq v (V.singleton (V.CurrencySymbol "policy") (V.TokenName "missing") 1)] + LBS.writeFile (directory ++ "/values.cbor") (serialise (P.toData (amounts, comparisons))) diff --git a/crates/nash-driver/tests/fixtures/cardano/reference/README.md b/crates/nash-driver/tests/fixtures/cardano/reference/README.md new file mode 100644 index 00000000..3f21aa26 --- /dev/null +++ b/crates/nash-driver/tests/fixtures/cardano/reference/README.md @@ -0,0 +1,32 @@ +# Plutus V3 reference fixtures + +`Main.hs` uses `PlutusLedgerApi.V3`, `PlutusTx.toData`, and +`Codec.Serialise.serialise` from IntersectMBO/plutus revision +`39981dd733ae276975958e40b0291ce1b24781d5` (plutus-ledger-api 1.70.0.0). +It was executed with GHC 9.6.7 in Docker through OrbStack. + +These synthetic contexts exercise the actual Haskell codecs. Short fixture +hashes are intentional; the fixtures are not ledger-valid transactions. + +Run `./regenerate.sh` to rebuild the committed `../golden/*.cbor` files. +It downloads the pinned upstream source and uses its locked Nix dependencies. +The `nash-cardano-nix` Docker volume retains the build cache. This generator +is a development tool; Cargo tests use the committed bytes and require no +Haskell, Docker, network, or Nash CLI process. + +The seven contexts cover all ScriptInfo variants and both spending datum +cases, all certificate and governance-action tags, both credentials, +staking hashes and pointers, output datum variants, optional scripts, +treasury options, votes, nested maps, and negative mint amounts. Nash checks +its independently constructed typed values against the decoded contexts and +compares `serialiseData` directly with the original Haskell bytes. + +The interval oracle enumerates five endpoints (negative infinity, -1, 0, 1, +positive infinity), both closure flags, and every endpoint pair. It emits +100 emptiness results, 500 membership results, and 10,000 containment results. + +The value oracle compares quantities, signed scaling, missing assets, addition, +and nonnegative containment with Haskell. Native Nash values remove zero +entries and reject negative containment; Haskell's map-based Value preserves +zero entries and permits negative comparisons. Separate Nash snapshots cover +those native builtin contracts. diff --git a/crates/nash-driver/tests/fixtures/cardano/reference/regenerate.sh b/crates/nash-driver/tests/fixtures/cardano/reference/regenerate.sh new file mode 100755 index 00000000..6817ae8b --- /dev/null +++ b/crates/nash-driver/tests/fixtures/cardano/reference/regenerate.sh @@ -0,0 +1,22 @@ +#!/usr/bin/env bash +set -euo pipefail +reference=$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd) +checkout=$(mktemp -d) +trap 'rm -rf "$checkout"' EXIT +revision=39981dd733ae276975958e40b0291ce1b24781d5 +git -C "$checkout" init -q +git -C "$checkout" fetch --depth 1 https://github.com/IntersectMBO/plutus.git "$revision" +git -C "$checkout" checkout -q FETCH_HEAD +docker run --rm --platform linux/amd64 \ + -v nash-cardano-nix:/nix \ + -v "$checkout:/plutus:ro" \ + -v "$reference/..:/fixtures" \ + nixos/nix@sha256:617d914dba5384bf75adf17081583b69371031ec7defce36c34c5fa14fc819b0 \ + nix --extra-experimental-features 'nix-command flakes' \ + --option build-users-group '' \ + --option system x86_64-linux --option extra-platforms x86_64-linux \ + --option accept-flake-config true \ + --option substituters 'https://cache.nixos.org https://cache.iog.io' \ + --option trusted-public-keys 'cache.nixos.org-1:6NCHdD59X431o0gWypbMrAURkbJ16ZPMQFGspcDShjY= hydra.iohk.io:f/Ea+s+dFdN+3Y/G+FDgSq+a5NEWhJGzdjvKNGv0/EQ=' \ + shell --impure --expr 'let f = builtins.getFlake "path:/plutus"; p = f.__internal.x86_64-linux.project; in p.ghcWithPackages (ps: [ ps.plutus-ledger-api ps.plutus-tx ps.serialise ])' \ + --command runghc /fixtures/reference/Main.hs /fixtures/golden diff --git a/crates/nash-driver/tests/snapshots/base_traits__cardano_helpers.snap b/crates/nash-driver/tests/snapshots/base_traits__cardano_helpers.snap new file mode 100644 index 00000000..7ec4e354 --- /dev/null +++ b/crates/nash-driver/tests/snapshots/base_traits__cardano_helpers.snap @@ -0,0 +1,16 @@ +--- +source: crates/nash-driver/tests/base_traits.rs +description: "module Main exposing (..)\n\nimport Cardano.Time as Time\nimport Cardano.Tx as Tx\nimport Cardano.Address as Address\nimport Cardano.Value as Value exposing (Value)\n\ncheckBool : Data -> bool\ncheckBool raw = let checked : Bool\n checked = validate raw\n in lower checked\ncheckOption : Data -> option Int\ncheckOption raw = let checked : Option Int\n checked = validate raw\n in lower checked\ncheckCommittee raw = let checked : Tx.Committee\n checked = validate raw\n in toData checked\ncheckAddress raw = let checked : Address.Address\n checked = validate raw\n in toData checked\ncheckValue raw = let checked : Value\n checked = validate raw\n in toData checked\n\ncommittee = Tx.Committee (Builtin.mapData []) (Tx.Rational 2 3)\n\nbigStart : Int\nbigStart = lift (-10)\n\ntests\n test \"closed interval builders accept Big endpoints\" = do\n assert (Time.member bigStart (Time.interval bigStart 20))\n assert (Time.member 20 (Time.interval bigStart 20))\n assert (Bool.not (Time.member 21 (Time.interval bigStart 20)))\n assert (Time.contains Time.always (Time.interval bigStart 20))\n assert (Time.contains (Time.from bigStart) (Time.to -20) == False)\n assert (Time.contains (Time.interval 0 1) Time.never)\n assert (Time.isEmpty Time.never)\n assert (Time.member -999 (Time.to 20))\n assert (Time.member 999 (Time.from 20))\n test \"committee validates rational tuple representation\" = do\n assert (checkCommittee (toData committee) == toData committee)\n test \"Bool invalid tag\" fail = do\n let _ = checkBool (Builtin.constrData 2 []) in ()\n test \"Bool extra field\" fail = do\n let _ = checkBool (Builtin.constrData 0 [Data.Encode.int 1]) in ()\n test \"Option missing field\" fail = do\n let _ = checkOption (Builtin.constrData 0 []) in ()\n test \"Option wrong element\" fail = do\n let _ = checkOption (Builtin.constrData 0 [Data.Encode.bytes #\"aa\"]) in ()\n test \"Option extra field\" fail = do\n let _ = checkOption (Builtin.constrData 1 [Data.Encode.int 1]) in ()\n test \"record rejects wrong tag\" fail = do\n let _ = checkAddress (Builtin.constrData 1 [toData (Address.PubKeyCredential (lift #\"aa\")), Builtin.constrData 1 []]) in ()\n test \"record rejects missing field\" fail = do\n let _ = checkAddress (Builtin.constrData 0 []) in ()\n test \"record rejects extra field\" fail = do\n let _ = checkAddress (Builtin.constrData 0 [toData (Address.PubKeyCredential (lift #\"aa\")), Builtin.constrData 1 [], Data.Encode.int 1]) in ()\n test \"record rejects invalid nested credential\" fail = do\n let _ = checkAddress (Builtin.constrData 0 [Builtin.constrData 2 [], Builtin.constrData 1 []]) in ()\n test \"Value rejects non-map\" fail = do\n let _ = checkValue (Data.Encode.int 1) in ()\n" +--- +closed interval builders accept Big endpoints: Pass; traces: [] +committee validates rational tuple representation: Pass; traces: [] +Bool invalid tag: Pass; traces: [] +Bool extra field: Pass; traces: [] +Option missing field: Pass; traces: [] +Option wrong element: Pass; traces: [] +Option extra field: Pass; traces: [] +record rejects wrong tag: Pass; traces: [] +record rejects missing field: Pass; traces: [] +record rejects extra field: Pass; traces: [] +record rejects invalid nested credential: Pass; traces: [] +Value rejects non-map: Pass; traces: [] diff --git a/crates/nash-driver/tests/snapshots/base_traits__cardano_value.snap b/crates/nash-driver/tests/snapshots/base_traits__cardano_value.snap new file mode 100644 index 00000000..1eeeca22 --- /dev/null +++ b/crates/nash-driver/tests/snapshots/base_traits__cardano_value.snap @@ -0,0 +1,8 @@ +--- +source: crates/nash-driver/tests/base_traits.rs +description: "module Main exposing (..)\n\nbig : Cardano.Value.Value\nbig = lift (Cardano.Value.singleton #\"aa\" #\"01\" 5)\n\ntests\n test \"native value operations accept Big and little values\" = do\n assert (Cardano.Value.quantityOf #\"aa\" #\"01\" big == 5)\n assert (Cardano.Value.quantityOf #\"aa\" #\"02\" big == 0)\n assert (Cardano.Value.lovelace (Cardano.Value.singleton #\"\" #\"\" 100) == 100)\n assert (Cardano.Value.quantityOf #\"aa\" #\"01\" (Cardano.Value.add big big) == 10)\n assert (Cardano.Value.quantityOf #\"aa\" #\"01\" (Cardano.Value.scale -2 big) == -10)\n assert (Cardano.Value.contains (Cardano.Value.scale 2 big) big)\n assert (Cardano.Value.quantityOf #\"aa\" #\"01\" (Cardano.Value.insert #\"aa\" #\"01\" 0 big) == 0)\n test \"wire representation is a nested map\" = do\n assert (Data.serialise (toData big) == #\"a141aaa1410105\")\n assert (Cardano.Value.lovelace Cardano.Value.empty == 0)\n assert (Builtin.valueData (lower big) == toData big)\n test \"zero quantities are removed by native arithmetic\" = do\n assert (Cardano.Value.scale 0 big == Cardano.Value.empty)\n assert (Cardano.Value.add big (Cardano.Value.scale -1 big) == Cardano.Value.empty)\n test \"native containment rejects negative quantities\" fail = do\n assert (Cardano.Value.contains (Cardano.Value.scale -1 big) (Cardano.Value.scale -1 big))\n" +--- +native value operations accept Big and little values: Pass; traces: [] +wire representation is a nested map: Pass; traces: [] +zero quantities are removed by native arithmetic: Pass; traces: [] +native containment rejects negative quantities: Pass; traces: [] diff --git a/crates/nash-driver/tests/snapshots/cardano__ledger_contexts.snap b/crates/nash-driver/tests/snapshots/cardano__ledger_contexts.snap new file mode 100644 index 00000000..b5f1e077 --- /dev/null +++ b/crates/nash-driver/tests/snapshots/cardano__ledger_contexts.snap @@ -0,0 +1,11 @@ +--- +source: crates/nash-driver/tests/cardano.rs +description: "validator module Context exposing (main)\n\nimport Cardano.Tx exposing (..)\nimport Cardano.Address exposing (..)\nimport Cardano.Time as Time\nimport Cardano.Value as Value exposing (Value)\n\ninteger : int -> Int\ninteger = lift\n\nbytes : bytes -> Bytes\nbytes = lift\n\nkey = PubKeyCredential (lift #\"6b6579\")\nscript = ScriptCredential (lift #\"736372697074\")\nref = OutputReference (lift #\"7478\") 2\nrep = DRep key\n\ncertificates : List Certificate\ncertificates = lift\n [ RegStaking key Option.None\n , UnRegStaking script (Option.Some 12)\n , DelegStaking key (DelegStake (lift #\"706f6f6c\"))\n , RegDeleg key (DelegVote DRepAlwaysAbstain) 13\n , RegDRep key 14\n , UpdateDRep script\n , UnRegDRep key 15\n , PoolRegister (lift #\"706f6f6c\") (lift #\"767266\")\n , PoolRetire (lift #\"706f6f6c\") 16\n , AuthHotCommittee key script\n , ResignColdCommittee script\n , DelegStaking key (DelegStakeVote (lift #\"706f6f6c\") rep)\n , DelegStaking key (DelegVote DRepAlwaysNoConfidence)\n ]\nactionId = GovernanceActionId (lift #\"70726576696f7573\") 3\n\nactions : list GovernanceAction\nactions =\n [ ParameterChange Option.None (toData (Builtin.mapData (Map.singleton (integer 0) (integer 42)))) (Option.Some (lift #\"6775617264\"))\n , HardForkInitiation (Option.Some actionId) (ProtocolVersion 11 0)\n , TreasuryWithdrawals (lift (Map.singleton key (integer 17))) Option.None\n , NoConfidence (Option.Some actionId)\n , UpdateCommittee Option.None (lift [key]) (lift (Map.singleton script (integer 18))) (Rational 2 3)\n , NewConstitution (Option.Some actionId) (Constitution Option.None)\n , InfoAction\n , NewConstitution Option.None (Constitution (Option.Some (lift #\"636f6e737469747574696f6e\")))\n ]\nproposals : List Proposal\nproposals = lift (List.map (Proposal 19 key) actions)\nproposal = Proposal 19 key InfoAction\nvoters = [CommitteeVoter key, DRepVoter script, StakePoolVoter (lift #\"706f6f6c\")]\npurposes = [Minting (lift #\"706f6c696379\"), Spending ref, Rewarding key, Certifying 0 (RegStaking key Option.None), Voting (CommitteeVoter key), Proposing 6 proposal]\ninfos = [MintingScript (lift #\"706f6c696379\"), SpendingScript ref (Option.Some (toData (integer 99))), RewardingScript key, CertifyingScript 0 (RegStaking key Option.None), VotingScript (CommitteeVoter key), ProposingScript 6 proposal]\ncoin : Value\ncoin = lift (Value.singleton #\"\" #\"\" 100)\nmint : Value\nmint = lift (Value.singleton #\"706f6c696379\" #\"6173736574\" -2)\naddresses = [Address key Option.None, Address script (Option.Some (StakingHash key)), Address key (Option.Some (StakingPtr 20 21 22))]\ndatums = [NoDatum, DatumHash (lift #\"646174756d\"), InlineDatum (toData (integer 23))]\noutputs = List.map2 (\\address datum -> Output address coin datum (Option.Some (lift #\"7265662d736372697074\"))) addresses datums\nfirstOutput = Output (Address key Option.None) coin NoDatum (Option.Some (lift #\"7265662d736372697074\"))\nlastOutput = Output (Address key (Option.Some (StakingPtr 20 21 22))) coin (InlineDatum (toData (integer 23))) (Option.Some (lift #\"7265662d736372697074\"))\n\ntx = Tx\n (lift [Input ref firstOutput])\n (lift [Input (OutputReference (lift #\"7265666572656e6365\") 1) lastOutput])\n (lift outputs)\n 2\n mint\n certificates\n (lift (Map.singleton key (integer 24)))\n (Time.Interval (Time.IntervalBound (Time.Finite 10) Bool.False) (Time.IntervalBound (Time.Finite 30) Bool.True))\n (lift [lift #\"6b6579\"])\n (lift (List.map (\\purpose -> Builtin.mkPairData purpose (toData (integer 25))) purposes))\n (lift (Map.singleton (bytes #\"646174756d\") (toData (integer 23))))\n (lift #\"7472616e73616374696f6e\")\n (lift (List.map2 (\\voter vote -> Builtin.mkPairData voter (Builtin.mapData (Map.singleton actionId vote))) voters [VoteNo, VoteYes, Abstain]))\n proposals\n (Option.Some 26)\n Option.None\n\nmain : Data -> unit\nmain packet =\n case packet of\n Constr pair(0, [raw, B encoded]) -> check raw encoded\n _ -> fail\n\ncheck : Data -> bytes -> unit\ncheck raw encoded =\n let\n context : ScriptContext\n context = validate raw\n index = Data.tag (toData context.scriptInfo)\n expected =\n case context.scriptInfo of\n SpendingScript _ Option.None ->\n ScriptContext\n { tx | currentTreasury = Option.None, treasuryDonation = (Option.Some 28), outputs = (lift [Output (Address key Option.None) coin NoDatum Option.None]) }\n (toData (integer 27))\n (SpendingScript ref Option.None)\n _ -> ScriptContext tx (toData (integer 27)) (List.at index infos |> Option.unwrap)\n _ = assert (context == expected)\n _ = assert (Data.serialise (toData expected) == encoded)\n _ = assert (Value.lovelace (List.at 0 context.tx.outputs |> Option.unwrap).value == 100)\n _ = assert (Value.quantityOf #\"706f6c696379\" #\"6173736574\" context.tx.mint == -2)\n _ = assert (Time.member 11 context.tx.validityRange)\n _ = assert (Bool.not (Time.member 10 context.tx.validityRange))\n in\n ()\n" +--- +context-0.cbor: Ok(Constant(Unit)) +context-1.cbor: Ok(Constant(Unit)) +context-2.cbor: Ok(Constant(Unit)) +context-3.cbor: Ok(Constant(Unit)) +context-4.cbor: Ok(Constant(Unit)) +context-5.cbor: Ok(Constant(Unit)) +context-6.cbor: Ok(Constant(Unit)) diff --git a/crates/nash-driver/tests/snapshots/cardano__ledger_interval_semantics.snap b/crates/nash-driver/tests/snapshots/cardano__ledger_interval_semantics.snap new file mode 100644 index 00000000..4ba1c217 --- /dev/null +++ b/crates/nash-driver/tests/snapshots/cardano__ledger_interval_semantics.snap @@ -0,0 +1,104 @@ +--- +source: crates/nash-driver/tests/cardano.rs +description: "validator module Intervals exposing (main)\n\nimport Cardano.Time as Time exposing (Interval)\n\nmain : Data -> unit\nmain raw =\n case raw of\n Constr pair(0, [rangeData, emptyData, memberData, rangesData, containsData]) ->\n let\n range : Interval\n range = validate rangeData\n expectedEmpty : Bool\n expectedEmpty = validate emptyData\n expectedMembers : List Bool\n expectedMembers = validate memberData\n ranges : List Interval\n ranges = validate rangesData\n expectedContains : List Bool\n expectedContains = validate containsData\n _ = assert (Time.isEmpty range == lower expectedEmpty)\n _ = assert (List.map (\\point -> Time.member point range) [-2, -1, 0, 1, 2] == List.map lower expectedMembers)\n in\n assert (List.map (Time.contains range) ranges == List.map lower expectedContains)\n _ -> fail\n" +--- +0: Constr { tag: 0, fields: [Constr { tag: 0, fields: [Constr { tag: 0, fields: [] }, Constr { tag: 0, fields: [] }] }, Constr { tag: 0, fields: [Constr { tag: 0, fields: [] }, Constr { tag: 0, fields: [] }] }] } => Ok(Constant(Unit)) +1: Constr { tag: 0, fields: [Constr { tag: 0, fields: [Constr { tag: 0, fields: [] }, Constr { tag: 0, fields: [] }] }, Constr { tag: 0, fields: [Constr { tag: 0, fields: [] }, Constr { tag: 1, fields: [] }] }] } => Ok(Constant(Unit)) +2: Constr { tag: 0, fields: [Constr { tag: 0, fields: [Constr { tag: 0, fields: [] }, Constr { tag: 1, fields: [] }] }, Constr { tag: 0, fields: [Constr { tag: 0, fields: [] }, Constr { tag: 0, fields: [] }] }] } => Ok(Constant(Unit)) +3: Constr { tag: 0, fields: [Constr { tag: 0, fields: [Constr { tag: 0, fields: [] }, Constr { tag: 1, fields: [] }] }, Constr { tag: 0, fields: [Constr { tag: 0, fields: [] }, Constr { tag: 1, fields: [] }] }] } => Ok(Constant(Unit)) +4: Constr { tag: 0, fields: [Constr { tag: 0, fields: [Constr { tag: 0, fields: [] }, Constr { tag: 0, fields: [] }] }, Constr { tag: 0, fields: [Constr { tag: 1, fields: [Integer(-1)] }, Constr { tag: 0, fields: [] }] }] } => Ok(Constant(Unit)) +5: Constr { tag: 0, fields: [Constr { tag: 0, fields: [Constr { tag: 0, fields: [] }, Constr { tag: 0, fields: [] }] }, Constr { tag: 0, fields: [Constr { tag: 1, fields: [Integer(-1)] }, Constr { tag: 1, fields: [] }] }] } => Ok(Constant(Unit)) +6: Constr { tag: 0, fields: [Constr { tag: 0, fields: [Constr { tag: 0, fields: [] }, Constr { tag: 1, fields: [] }] }, Constr { tag: 0, fields: [Constr { tag: 1, fields: [Integer(-1)] }, Constr { tag: 0, fields: [] }] }] } => Ok(Constant(Unit)) +7: Constr { tag: 0, fields: [Constr { tag: 0, fields: [Constr { tag: 0, fields: [] }, Constr { tag: 1, fields: [] }] }, Constr { tag: 0, fields: [Constr { tag: 1, fields: [Integer(-1)] }, Constr { tag: 1, fields: [] }] }] } => Ok(Constant(Unit)) +8: Constr { tag: 0, fields: [Constr { tag: 0, fields: [Constr { tag: 0, fields: [] }, Constr { tag: 0, fields: [] }] }, Constr { tag: 0, fields: [Constr { tag: 1, fields: [Integer(0)] }, Constr { tag: 0, fields: [] }] }] } => Ok(Constant(Unit)) +9: Constr { tag: 0, fields: [Constr { tag: 0, fields: [Constr { tag: 0, fields: [] }, Constr { tag: 0, fields: [] }] }, Constr { tag: 0, fields: [Constr { tag: 1, fields: [Integer(0)] }, Constr { tag: 1, fields: [] }] }] } => Ok(Constant(Unit)) +10: Constr { tag: 0, fields: [Constr { tag: 0, fields: [Constr { tag: 0, fields: [] }, Constr { tag: 1, fields: [] }] }, Constr { tag: 0, fields: [Constr { tag: 1, fields: [Integer(0)] }, Constr { tag: 0, fields: [] }] }] } => Ok(Constant(Unit)) +11: Constr { tag: 0, fields: [Constr { tag: 0, fields: [Constr { tag: 0, fields: [] }, Constr { tag: 1, fields: [] }] }, Constr { tag: 0, fields: [Constr { tag: 1, fields: [Integer(0)] }, Constr { tag: 1, fields: [] }] }] } => Ok(Constant(Unit)) +12: Constr { tag: 0, fields: [Constr { tag: 0, fields: [Constr { tag: 0, fields: [] }, Constr { tag: 0, fields: [] }] }, Constr { tag: 0, fields: [Constr { tag: 1, fields: [Integer(1)] }, Constr { tag: 0, fields: [] }] }] } => Ok(Constant(Unit)) +13: Constr { tag: 0, fields: [Constr { tag: 0, fields: [Constr { tag: 0, fields: [] }, Constr { tag: 0, fields: [] }] }, Constr { tag: 0, fields: [Constr { tag: 1, fields: [Integer(1)] }, Constr { tag: 1, fields: [] }] }] } => Ok(Constant(Unit)) +14: Constr { tag: 0, fields: [Constr { tag: 0, fields: [Constr { tag: 0, fields: [] }, Constr { tag: 1, fields: [] }] }, Constr { tag: 0, fields: [Constr { tag: 1, fields: [Integer(1)] }, Constr { tag: 0, fields: [] }] }] } => Ok(Constant(Unit)) +15: Constr { tag: 0, fields: [Constr { tag: 0, fields: [Constr { tag: 0, fields: [] }, Constr { tag: 1, fields: [] }] }, Constr { tag: 0, fields: [Constr { tag: 1, fields: [Integer(1)] }, Constr { tag: 1, fields: [] }] }] } => Ok(Constant(Unit)) +16: Constr { tag: 0, fields: [Constr { tag: 0, fields: [Constr { tag: 0, fields: [] }, Constr { tag: 0, fields: [] }] }, Constr { tag: 0, fields: [Constr { tag: 2, fields: [] }, Constr { tag: 0, fields: [] }] }] } => Ok(Constant(Unit)) +17: Constr { tag: 0, fields: [Constr { tag: 0, fields: [Constr { tag: 0, fields: [] }, Constr { tag: 0, fields: [] }] }, Constr { tag: 0, fields: [Constr { tag: 2, fields: [] }, Constr { tag: 1, fields: [] }] }] } => Ok(Constant(Unit)) +18: Constr { tag: 0, fields: [Constr { tag: 0, fields: [Constr { tag: 0, fields: [] }, Constr { tag: 1, fields: [] }] }, Constr { tag: 0, fields: [Constr { tag: 2, fields: [] }, Constr { tag: 0, fields: [] }] }] } => Ok(Constant(Unit)) +19: Constr { tag: 0, fields: [Constr { tag: 0, fields: [Constr { tag: 0, fields: [] }, Constr { tag: 1, fields: [] }] }, Constr { tag: 0, fields: [Constr { tag: 2, fields: [] }, Constr { tag: 1, fields: [] }] }] } => Ok(Constant(Unit)) +20: Constr { tag: 0, fields: [Constr { tag: 0, fields: [Constr { tag: 1, fields: [Integer(-1)] }, Constr { tag: 0, fields: [] }] }, Constr { tag: 0, fields: [Constr { tag: 0, fields: [] }, Constr { tag: 0, fields: [] }] }] } => Ok(Constant(Unit)) +21: Constr { tag: 0, fields: [Constr { tag: 0, fields: [Constr { tag: 1, fields: [Integer(-1)] }, Constr { tag: 0, fields: [] }] }, Constr { tag: 0, fields: [Constr { tag: 0, fields: [] }, Constr { tag: 1, fields: [] }] }] } => Ok(Constant(Unit)) +22: Constr { tag: 0, fields: [Constr { tag: 0, fields: [Constr { tag: 1, fields: [Integer(-1)] }, Constr { tag: 1, fields: [] }] }, Constr { tag: 0, fields: [Constr { tag: 0, fields: [] }, Constr { tag: 0, fields: [] }] }] } => Ok(Constant(Unit)) +23: Constr { tag: 0, fields: [Constr { tag: 0, fields: [Constr { tag: 1, fields: [Integer(-1)] }, Constr { tag: 1, fields: [] }] }, Constr { tag: 0, fields: [Constr { tag: 0, fields: [] }, Constr { tag: 1, fields: [] }] }] } => Ok(Constant(Unit)) +24: Constr { tag: 0, fields: [Constr { tag: 0, fields: [Constr { tag: 1, fields: [Integer(-1)] }, Constr { tag: 0, fields: [] }] }, Constr { tag: 0, fields: [Constr { tag: 1, fields: [Integer(-1)] }, Constr { tag: 0, fields: [] }] }] } => Ok(Constant(Unit)) +25: Constr { tag: 0, fields: [Constr { tag: 0, fields: [Constr { tag: 1, fields: [Integer(-1)] }, Constr { tag: 0, fields: [] }] }, Constr { tag: 0, fields: [Constr { tag: 1, fields: [Integer(-1)] }, Constr { tag: 1, fields: [] }] }] } => Ok(Constant(Unit)) +26: Constr { tag: 0, fields: [Constr { tag: 0, fields: [Constr { tag: 1, fields: [Integer(-1)] }, Constr { tag: 1, fields: [] }] }, Constr { tag: 0, fields: [Constr { tag: 1, fields: [Integer(-1)] }, Constr { tag: 0, fields: [] }] }] } => Ok(Constant(Unit)) +27: Constr { tag: 0, fields: [Constr { tag: 0, fields: [Constr { tag: 1, fields: [Integer(-1)] }, Constr { tag: 1, fields: [] }] }, Constr { tag: 0, fields: [Constr { tag: 1, fields: [Integer(-1)] }, Constr { tag: 1, fields: [] }] }] } => Ok(Constant(Unit)) +28: Constr { tag: 0, fields: [Constr { tag: 0, fields: [Constr { tag: 1, fields: [Integer(-1)] }, Constr { tag: 0, fields: [] }] }, Constr { tag: 0, fields: [Constr { tag: 1, fields: [Integer(0)] }, Constr { tag: 0, fields: [] }] }] } => Ok(Constant(Unit)) +29: Constr { tag: 0, fields: [Constr { tag: 0, fields: [Constr { tag: 1, fields: [Integer(-1)] }, Constr { tag: 0, fields: [] }] }, Constr { tag: 0, fields: [Constr { tag: 1, fields: [Integer(0)] }, Constr { tag: 1, fields: [] }] }] } => Ok(Constant(Unit)) +30: Constr { tag: 0, fields: [Constr { tag: 0, fields: [Constr { tag: 1, fields: [Integer(-1)] }, Constr { tag: 1, fields: [] }] }, Constr { tag: 0, fields: [Constr { tag: 1, fields: [Integer(0)] }, Constr { tag: 0, fields: [] }] }] } => Ok(Constant(Unit)) +31: Constr { tag: 0, fields: [Constr { tag: 0, fields: [Constr { tag: 1, fields: [Integer(-1)] }, Constr { tag: 1, fields: [] }] }, Constr { tag: 0, fields: [Constr { tag: 1, fields: [Integer(0)] }, Constr { tag: 1, fields: [] }] }] } => Ok(Constant(Unit)) +32: Constr { tag: 0, fields: [Constr { tag: 0, fields: [Constr { tag: 1, fields: [Integer(-1)] }, Constr { tag: 0, fields: [] }] }, Constr { tag: 0, fields: [Constr { tag: 1, fields: [Integer(1)] }, Constr { tag: 0, fields: [] }] }] } => Ok(Constant(Unit)) +33: Constr { tag: 0, fields: [Constr { tag: 0, fields: [Constr { tag: 1, fields: [Integer(-1)] }, Constr { tag: 0, fields: [] }] }, Constr { tag: 0, fields: [Constr { tag: 1, fields: [Integer(1)] }, Constr { tag: 1, fields: [] }] }] } => Ok(Constant(Unit)) +34: Constr { tag: 0, fields: [Constr { tag: 0, fields: [Constr { tag: 1, fields: [Integer(-1)] }, Constr { tag: 1, fields: [] }] }, Constr { tag: 0, fields: [Constr { tag: 1, fields: [Integer(1)] }, Constr { tag: 0, fields: [] }] }] } => Ok(Constant(Unit)) +35: Constr { tag: 0, fields: [Constr { tag: 0, fields: [Constr { tag: 1, fields: [Integer(-1)] }, Constr { tag: 1, fields: [] }] }, Constr { tag: 0, fields: [Constr { tag: 1, fields: [Integer(1)] }, Constr { tag: 1, fields: [] }] }] } => Ok(Constant(Unit)) +36: Constr { tag: 0, fields: [Constr { tag: 0, fields: [Constr { tag: 1, fields: [Integer(-1)] }, Constr { tag: 0, fields: [] }] }, Constr { tag: 0, fields: [Constr { tag: 2, fields: [] }, Constr { tag: 0, fields: [] }] }] } => Ok(Constant(Unit)) +37: Constr { tag: 0, fields: [Constr { tag: 0, fields: [Constr { tag: 1, fields: [Integer(-1)] }, Constr { tag: 0, fields: [] }] }, Constr { tag: 0, fields: [Constr { tag: 2, fields: [] }, Constr { tag: 1, fields: [] }] }] } => Ok(Constant(Unit)) +38: Constr { tag: 0, fields: [Constr { tag: 0, fields: [Constr { tag: 1, fields: [Integer(-1)] }, Constr { tag: 1, fields: [] }] }, Constr { tag: 0, fields: [Constr { tag: 2, fields: [] }, Constr { tag: 0, fields: [] }] }] } => Ok(Constant(Unit)) +39: Constr { tag: 0, fields: [Constr { tag: 0, fields: [Constr { tag: 1, fields: [Integer(-1)] }, Constr { tag: 1, fields: [] }] }, Constr { tag: 0, fields: [Constr { tag: 2, fields: [] }, Constr { tag: 1, fields: [] }] }] } => Ok(Constant(Unit)) +40: Constr { tag: 0, fields: [Constr { tag: 0, fields: [Constr { tag: 1, fields: [Integer(0)] }, Constr { tag: 0, fields: [] }] }, Constr { tag: 0, fields: [Constr { tag: 0, fields: [] }, Constr { tag: 0, fields: [] }] }] } => Ok(Constant(Unit)) +41: Constr { tag: 0, fields: [Constr { tag: 0, fields: [Constr { tag: 1, fields: [Integer(0)] }, Constr { tag: 0, fields: [] }] }, Constr { tag: 0, fields: [Constr { tag: 0, fields: [] }, Constr { tag: 1, fields: [] }] }] } => Ok(Constant(Unit)) +42: Constr { tag: 0, fields: [Constr { tag: 0, fields: [Constr { tag: 1, fields: [Integer(0)] }, Constr { tag: 1, fields: [] }] }, Constr { tag: 0, fields: [Constr { tag: 0, fields: [] }, Constr { tag: 0, fields: [] }] }] } => Ok(Constant(Unit)) +43: Constr { tag: 0, fields: [Constr { tag: 0, fields: [Constr { tag: 1, fields: [Integer(0)] }, Constr { tag: 1, fields: [] }] }, Constr { tag: 0, fields: [Constr { tag: 0, fields: [] }, Constr { tag: 1, fields: [] }] }] } => Ok(Constant(Unit)) +44: Constr { tag: 0, fields: [Constr { tag: 0, fields: [Constr { tag: 1, fields: [Integer(0)] }, Constr { tag: 0, fields: [] }] }, Constr { tag: 0, fields: [Constr { tag: 1, fields: [Integer(-1)] }, Constr { tag: 0, fields: [] }] }] } => Ok(Constant(Unit)) +45: Constr { tag: 0, fields: [Constr { tag: 0, fields: [Constr { tag: 1, fields: [Integer(0)] }, Constr { tag: 0, fields: [] }] }, Constr { tag: 0, fields: [Constr { tag: 1, fields: [Integer(-1)] }, Constr { tag: 1, fields: [] }] }] } => Ok(Constant(Unit)) +46: Constr { tag: 0, fields: [Constr { tag: 0, fields: [Constr { tag: 1, fields: [Integer(0)] }, Constr { tag: 1, fields: [] }] }, Constr { tag: 0, fields: [Constr { tag: 1, fields: [Integer(-1)] }, Constr { tag: 0, fields: [] }] }] } => Ok(Constant(Unit)) +47: Constr { tag: 0, fields: [Constr { tag: 0, fields: [Constr { tag: 1, fields: [Integer(0)] }, Constr { tag: 1, fields: [] }] }, Constr { tag: 0, fields: [Constr { tag: 1, fields: [Integer(-1)] }, Constr { tag: 1, fields: [] }] }] } => Ok(Constant(Unit)) +48: Constr { tag: 0, fields: [Constr { tag: 0, fields: [Constr { tag: 1, fields: [Integer(0)] }, Constr { tag: 0, fields: [] }] }, Constr { tag: 0, fields: [Constr { tag: 1, fields: [Integer(0)] }, Constr { tag: 0, fields: [] }] }] } => Ok(Constant(Unit)) +49: Constr { tag: 0, fields: [Constr { tag: 0, fields: [Constr { tag: 1, fields: [Integer(0)] }, Constr { tag: 0, fields: [] }] }, Constr { tag: 0, fields: [Constr { tag: 1, fields: [Integer(0)] }, Constr { tag: 1, fields: [] }] }] } => Ok(Constant(Unit)) +50: Constr { tag: 0, fields: [Constr { tag: 0, fields: [Constr { tag: 1, fields: [Integer(0)] }, Constr { tag: 1, fields: [] }] }, Constr { tag: 0, fields: [Constr { tag: 1, fields: [Integer(0)] }, Constr { tag: 0, fields: [] }] }] } => Ok(Constant(Unit)) +51: Constr { tag: 0, fields: [Constr { tag: 0, fields: [Constr { tag: 1, fields: [Integer(0)] }, Constr { tag: 1, fields: [] }] }, Constr { tag: 0, fields: [Constr { tag: 1, fields: [Integer(0)] }, Constr { tag: 1, fields: [] }] }] } => Ok(Constant(Unit)) +52: Constr { tag: 0, fields: [Constr { tag: 0, fields: [Constr { tag: 1, fields: [Integer(0)] }, Constr { tag: 0, fields: [] }] }, Constr { tag: 0, fields: [Constr { tag: 1, fields: [Integer(1)] }, Constr { tag: 0, fields: [] }] }] } => Ok(Constant(Unit)) +53: Constr { tag: 0, fields: [Constr { tag: 0, fields: [Constr { tag: 1, fields: [Integer(0)] }, Constr { tag: 0, fields: [] }] }, Constr { tag: 0, fields: [Constr { tag: 1, fields: [Integer(1)] }, Constr { tag: 1, fields: [] }] }] } => Ok(Constant(Unit)) +54: Constr { tag: 0, fields: [Constr { tag: 0, fields: [Constr { tag: 1, fields: [Integer(0)] }, Constr { tag: 1, fields: [] }] }, Constr { tag: 0, fields: [Constr { tag: 1, fields: [Integer(1)] }, Constr { tag: 0, fields: [] }] }] } => Ok(Constant(Unit)) +55: Constr { tag: 0, fields: [Constr { tag: 0, fields: [Constr { tag: 1, fields: [Integer(0)] }, Constr { tag: 1, fields: [] }] }, Constr { tag: 0, fields: [Constr { tag: 1, fields: [Integer(1)] }, Constr { tag: 1, fields: [] }] }] } => Ok(Constant(Unit)) +56: Constr { tag: 0, fields: [Constr { tag: 0, fields: [Constr { tag: 1, fields: [Integer(0)] }, Constr { tag: 0, fields: [] }] }, Constr { tag: 0, fields: [Constr { tag: 2, fields: [] }, Constr { tag: 0, fields: [] }] }] } => Ok(Constant(Unit)) +57: Constr { tag: 0, fields: [Constr { tag: 0, fields: [Constr { tag: 1, fields: [Integer(0)] }, Constr { tag: 0, fields: [] }] }, Constr { tag: 0, fields: [Constr { tag: 2, fields: [] }, Constr { tag: 1, fields: [] }] }] } => Ok(Constant(Unit)) +58: Constr { tag: 0, fields: [Constr { tag: 0, fields: [Constr { tag: 1, fields: [Integer(0)] }, Constr { tag: 1, fields: [] }] }, Constr { tag: 0, fields: [Constr { tag: 2, fields: [] }, Constr { tag: 0, fields: [] }] }] } => Ok(Constant(Unit)) +59: Constr { tag: 0, fields: [Constr { tag: 0, fields: [Constr { tag: 1, fields: [Integer(0)] }, Constr { tag: 1, fields: [] }] }, Constr { tag: 0, fields: [Constr { tag: 2, fields: [] }, Constr { tag: 1, fields: [] }] }] } => Ok(Constant(Unit)) +60: Constr { tag: 0, fields: [Constr { tag: 0, fields: [Constr { tag: 1, fields: [Integer(1)] }, Constr { tag: 0, fields: [] }] }, Constr { tag: 0, fields: [Constr { tag: 0, fields: [] }, Constr { tag: 0, fields: [] }] }] } => Ok(Constant(Unit)) +61: Constr { tag: 0, fields: [Constr { tag: 0, fields: [Constr { tag: 1, fields: [Integer(1)] }, Constr { tag: 0, fields: [] }] }, Constr { tag: 0, fields: [Constr { tag: 0, fields: [] }, Constr { tag: 1, fields: [] }] }] } => Ok(Constant(Unit)) +62: Constr { tag: 0, fields: [Constr { tag: 0, fields: [Constr { tag: 1, fields: [Integer(1)] }, Constr { tag: 1, fields: [] }] }, Constr { tag: 0, fields: [Constr { tag: 0, fields: [] }, Constr { tag: 0, fields: [] }] }] } => Ok(Constant(Unit)) +63: Constr { tag: 0, fields: [Constr { tag: 0, fields: [Constr { tag: 1, fields: [Integer(1)] }, Constr { tag: 1, fields: [] }] }, Constr { tag: 0, fields: [Constr { tag: 0, fields: [] }, Constr { tag: 1, fields: [] }] }] } => Ok(Constant(Unit)) +64: Constr { tag: 0, fields: [Constr { tag: 0, fields: [Constr { tag: 1, fields: [Integer(1)] }, Constr { tag: 0, fields: [] }] }, Constr { tag: 0, fields: [Constr { tag: 1, fields: [Integer(-1)] }, Constr { tag: 0, fields: [] }] }] } => Ok(Constant(Unit)) +65: Constr { tag: 0, fields: [Constr { tag: 0, fields: [Constr { tag: 1, fields: [Integer(1)] }, Constr { tag: 0, fields: [] }] }, Constr { tag: 0, fields: [Constr { tag: 1, fields: [Integer(-1)] }, Constr { tag: 1, fields: [] }] }] } => Ok(Constant(Unit)) +66: Constr { tag: 0, fields: [Constr { tag: 0, fields: [Constr { tag: 1, fields: [Integer(1)] }, Constr { tag: 1, fields: [] }] }, Constr { tag: 0, fields: [Constr { tag: 1, fields: [Integer(-1)] }, Constr { tag: 0, fields: [] }] }] } => Ok(Constant(Unit)) +67: Constr { tag: 0, fields: [Constr { tag: 0, fields: [Constr { tag: 1, fields: [Integer(1)] }, Constr { tag: 1, fields: [] }] }, Constr { tag: 0, fields: [Constr { tag: 1, fields: [Integer(-1)] }, Constr { tag: 1, fields: [] }] }] } => Ok(Constant(Unit)) +68: Constr { tag: 0, fields: [Constr { tag: 0, fields: [Constr { tag: 1, fields: [Integer(1)] }, Constr { tag: 0, fields: [] }] }, Constr { tag: 0, fields: [Constr { tag: 1, fields: [Integer(0)] }, Constr { tag: 0, fields: [] }] }] } => Ok(Constant(Unit)) +69: Constr { tag: 0, fields: [Constr { tag: 0, fields: [Constr { tag: 1, fields: [Integer(1)] }, Constr { tag: 0, fields: [] }] }, Constr { tag: 0, fields: [Constr { tag: 1, fields: [Integer(0)] }, Constr { tag: 1, fields: [] }] }] } => Ok(Constant(Unit)) +70: Constr { tag: 0, fields: [Constr { tag: 0, fields: [Constr { tag: 1, fields: [Integer(1)] }, Constr { tag: 1, fields: [] }] }, Constr { tag: 0, fields: [Constr { tag: 1, fields: [Integer(0)] }, Constr { tag: 0, fields: [] }] }] } => Ok(Constant(Unit)) +71: Constr { tag: 0, fields: [Constr { tag: 0, fields: [Constr { tag: 1, fields: [Integer(1)] }, Constr { tag: 1, fields: [] }] }, Constr { tag: 0, fields: [Constr { tag: 1, fields: [Integer(0)] }, Constr { tag: 1, fields: [] }] }] } => Ok(Constant(Unit)) +72: Constr { tag: 0, fields: [Constr { tag: 0, fields: [Constr { tag: 1, fields: [Integer(1)] }, Constr { tag: 0, fields: [] }] }, Constr { tag: 0, fields: [Constr { tag: 1, fields: [Integer(1)] }, Constr { tag: 0, fields: [] }] }] } => Ok(Constant(Unit)) +73: Constr { tag: 0, fields: [Constr { tag: 0, fields: [Constr { tag: 1, fields: [Integer(1)] }, Constr { tag: 0, fields: [] }] }, Constr { tag: 0, fields: [Constr { tag: 1, fields: [Integer(1)] }, Constr { tag: 1, fields: [] }] }] } => Ok(Constant(Unit)) +74: Constr { tag: 0, fields: [Constr { tag: 0, fields: [Constr { tag: 1, fields: [Integer(1)] }, Constr { tag: 1, fields: [] }] }, Constr { tag: 0, fields: [Constr { tag: 1, fields: [Integer(1)] }, Constr { tag: 0, fields: [] }] }] } => Ok(Constant(Unit)) +75: Constr { tag: 0, fields: [Constr { tag: 0, fields: [Constr { tag: 1, fields: [Integer(1)] }, Constr { tag: 1, fields: [] }] }, Constr { tag: 0, fields: [Constr { tag: 1, fields: [Integer(1)] }, Constr { tag: 1, fields: [] }] }] } => Ok(Constant(Unit)) +76: Constr { tag: 0, fields: [Constr { tag: 0, fields: [Constr { tag: 1, fields: [Integer(1)] }, Constr { tag: 0, fields: [] }] }, Constr { tag: 0, fields: [Constr { tag: 2, fields: [] }, Constr { tag: 0, fields: [] }] }] } => Ok(Constant(Unit)) +77: Constr { tag: 0, fields: [Constr { tag: 0, fields: [Constr { tag: 1, fields: [Integer(1)] }, Constr { tag: 0, fields: [] }] }, Constr { tag: 0, fields: [Constr { tag: 2, fields: [] }, Constr { tag: 1, fields: [] }] }] } => Ok(Constant(Unit)) +78: Constr { tag: 0, fields: [Constr { tag: 0, fields: [Constr { tag: 1, fields: [Integer(1)] }, Constr { tag: 1, fields: [] }] }, Constr { tag: 0, fields: [Constr { tag: 2, fields: [] }, Constr { tag: 0, fields: [] }] }] } => Ok(Constant(Unit)) +79: Constr { tag: 0, fields: [Constr { tag: 0, fields: [Constr { tag: 1, fields: [Integer(1)] }, Constr { tag: 1, fields: [] }] }, Constr { tag: 0, fields: [Constr { tag: 2, fields: [] }, Constr { tag: 1, fields: [] }] }] } => Ok(Constant(Unit)) +80: Constr { tag: 0, fields: [Constr { tag: 0, fields: [Constr { tag: 2, fields: [] }, Constr { tag: 0, fields: [] }] }, Constr { tag: 0, fields: [Constr { tag: 0, fields: [] }, Constr { tag: 0, fields: [] }] }] } => Ok(Constant(Unit)) +81: Constr { tag: 0, fields: [Constr { tag: 0, fields: [Constr { tag: 2, fields: [] }, Constr { tag: 0, fields: [] }] }, Constr { tag: 0, fields: [Constr { tag: 0, fields: [] }, Constr { tag: 1, fields: [] }] }] } => Ok(Constant(Unit)) +82: Constr { tag: 0, fields: [Constr { tag: 0, fields: [Constr { tag: 2, fields: [] }, Constr { tag: 1, fields: [] }] }, Constr { tag: 0, fields: [Constr { tag: 0, fields: [] }, Constr { tag: 0, fields: [] }] }] } => Ok(Constant(Unit)) +83: Constr { tag: 0, fields: [Constr { tag: 0, fields: [Constr { tag: 2, fields: [] }, Constr { tag: 1, fields: [] }] }, Constr { tag: 0, fields: [Constr { tag: 0, fields: [] }, Constr { tag: 1, fields: [] }] }] } => Ok(Constant(Unit)) +84: Constr { tag: 0, fields: [Constr { tag: 0, fields: [Constr { tag: 2, fields: [] }, Constr { tag: 0, fields: [] }] }, Constr { tag: 0, fields: [Constr { tag: 1, fields: [Integer(-1)] }, Constr { tag: 0, fields: [] }] }] } => Ok(Constant(Unit)) +85: Constr { tag: 0, fields: [Constr { tag: 0, fields: [Constr { tag: 2, fields: [] }, Constr { tag: 0, fields: [] }] }, Constr { tag: 0, fields: [Constr { tag: 1, fields: [Integer(-1)] }, Constr { tag: 1, fields: [] }] }] } => Ok(Constant(Unit)) +86: Constr { tag: 0, fields: [Constr { tag: 0, fields: [Constr { tag: 2, fields: [] }, Constr { tag: 1, fields: [] }] }, Constr { tag: 0, fields: [Constr { tag: 1, fields: [Integer(-1)] }, Constr { tag: 0, fields: [] }] }] } => Ok(Constant(Unit)) +87: Constr { tag: 0, fields: [Constr { tag: 0, fields: [Constr { tag: 2, fields: [] }, Constr { tag: 1, fields: [] }] }, Constr { tag: 0, fields: [Constr { tag: 1, fields: [Integer(-1)] }, Constr { tag: 1, fields: [] }] }] } => Ok(Constant(Unit)) +88: Constr { tag: 0, fields: [Constr { tag: 0, fields: [Constr { tag: 2, fields: [] }, Constr { tag: 0, fields: [] }] }, Constr { tag: 0, fields: [Constr { tag: 1, fields: [Integer(0)] }, Constr { tag: 0, fields: [] }] }] } => Ok(Constant(Unit)) +89: Constr { tag: 0, fields: [Constr { tag: 0, fields: [Constr { tag: 2, fields: [] }, Constr { tag: 0, fields: [] }] }, Constr { tag: 0, fields: [Constr { tag: 1, fields: [Integer(0)] }, Constr { tag: 1, fields: [] }] }] } => Ok(Constant(Unit)) +90: Constr { tag: 0, fields: [Constr { tag: 0, fields: [Constr { tag: 2, fields: [] }, Constr { tag: 1, fields: [] }] }, Constr { tag: 0, fields: [Constr { tag: 1, fields: [Integer(0)] }, Constr { tag: 0, fields: [] }] }] } => Ok(Constant(Unit)) +91: Constr { tag: 0, fields: [Constr { tag: 0, fields: [Constr { tag: 2, fields: [] }, Constr { tag: 1, fields: [] }] }, Constr { tag: 0, fields: [Constr { tag: 1, fields: [Integer(0)] }, Constr { tag: 1, fields: [] }] }] } => Ok(Constant(Unit)) +92: Constr { tag: 0, fields: [Constr { tag: 0, fields: [Constr { tag: 2, fields: [] }, Constr { tag: 0, fields: [] }] }, Constr { tag: 0, fields: [Constr { tag: 1, fields: [Integer(1)] }, Constr { tag: 0, fields: [] }] }] } => Ok(Constant(Unit)) +93: Constr { tag: 0, fields: [Constr { tag: 0, fields: [Constr { tag: 2, fields: [] }, Constr { tag: 0, fields: [] }] }, Constr { tag: 0, fields: [Constr { tag: 1, fields: [Integer(1)] }, Constr { tag: 1, fields: [] }] }] } => Ok(Constant(Unit)) +94: Constr { tag: 0, fields: [Constr { tag: 0, fields: [Constr { tag: 2, fields: [] }, Constr { tag: 1, fields: [] }] }, Constr { tag: 0, fields: [Constr { tag: 1, fields: [Integer(1)] }, Constr { tag: 0, fields: [] }] }] } => Ok(Constant(Unit)) +95: Constr { tag: 0, fields: [Constr { tag: 0, fields: [Constr { tag: 2, fields: [] }, Constr { tag: 1, fields: [] }] }, Constr { tag: 0, fields: [Constr { tag: 1, fields: [Integer(1)] }, Constr { tag: 1, fields: [] }] }] } => Ok(Constant(Unit)) +96: Constr { tag: 0, fields: [Constr { tag: 0, fields: [Constr { tag: 2, fields: [] }, Constr { tag: 0, fields: [] }] }, Constr { tag: 0, fields: [Constr { tag: 2, fields: [] }, Constr { tag: 0, fields: [] }] }] } => Ok(Constant(Unit)) +97: Constr { tag: 0, fields: [Constr { tag: 0, fields: [Constr { tag: 2, fields: [] }, Constr { tag: 0, fields: [] }] }, Constr { tag: 0, fields: [Constr { tag: 2, fields: [] }, Constr { tag: 1, fields: [] }] }] } => Ok(Constant(Unit)) +98: Constr { tag: 0, fields: [Constr { tag: 0, fields: [Constr { tag: 2, fields: [] }, Constr { tag: 1, fields: [] }] }, Constr { tag: 0, fields: [Constr { tag: 2, fields: [] }, Constr { tag: 0, fields: [] }] }] } => Ok(Constant(Unit)) +99: Constr { tag: 0, fields: [Constr { tag: 0, fields: [Constr { tag: 2, fields: [] }, Constr { tag: 1, fields: [] }] }, Constr { tag: 0, fields: [Constr { tag: 2, fields: [] }, Constr { tag: 1, fields: [] }] }] } => Ok(Constant(Unit)) diff --git a/crates/nash-driver/tests/snapshots/cardano__ledger_value_operations.snap b/crates/nash-driver/tests/snapshots/cardano__ledger_value_operations.snap new file mode 100644 index 00000000..48f0cb89 --- /dev/null +++ b/crates/nash-driver/tests/snapshots/cardano__ledger_value_operations.snap @@ -0,0 +1,5 @@ +--- +source: crates/nash-driver/tests/cardano.rs +description: "validator module Values exposing (main)\n\nimport Cardano.Value as Value\n\ncoin = Value.singleton #\"706f6c696379\" #\"6173736574\" 5\nquantity value = Value.quantityOf #\"706f6c696379\" #\"6173736574\" value\n\nmain : Data -> unit\nmain raw =\n case raw of\n Constr pair(0, [amountData, comparisonData]) ->\n let\n amounts : List Int\n amounts = validate amountData\n comparisons : List Bool\n comparisons = validate comparisonData\n actualAmounts = [quantity coin, Value.quantityOf #\"706f6c696379\" #\"6d697373696e67\" coin, Value.quantityOf #\"6d697373696e67\" #\"6173736574\" coin, quantity (Value.add coin coin), quantity (Value.scale -2 coin), quantity (Value.singleton #\"706f6c696379\" #\"6173736574\" 0)]\n actualComparisons = [Value.contains (Value.scale 2 coin) coin, Value.contains coin (Value.scale 2 coin), Value.contains coin (Value.singleton #\"706f6c696379\" #\"6d697373696e67\" 1)]\n _ = assert (actualAmounts == List.map lower amounts)\n in\n assert (actualComparisons == List.map lower comparisons)\n _ -> fail\n" +--- +Ok(Constant(Unit)) diff --git a/crates/nash-driver/tests/snapshots/cardano__malformed_contexts.snap b/crates/nash-driver/tests/snapshots/cardano__malformed_contexts.snap new file mode 100644 index 00000000..c0dd7882 --- /dev/null +++ b/crates/nash-driver/tests/snapshots/cardano__malformed_contexts.snap @@ -0,0 +1,24 @@ +--- +source: crates/nash-driver/tests/cardano.rs +description: "validator module Validate exposing (main)\n\nimport Cardano.Tx exposing (ScriptContext)\n\nmain : Data -> unit\nmain raw =\n let\n context : ScriptContext\n context = validate raw\n in\n ()\n" +--- +valid context: Ok(Constant(Unit)) +wrong context tag: Err(ExplicitErrorTerm) +missing context field: Err(ExplicitErrorTerm) +extra context field: Err(ExplicitErrorTerm) +wrong inputs shape: Err(Runtime(MalFormedData(Integer(0)))) +wrong reference inputs shape: Err(Runtime(MalFormedData(Integer(0)))) +wrong outputs shape: Err(Runtime(MalFormedData(Integer(0)))) +wrong fee shape: Err(Runtime(MalFormedData(ByteString([119, 114, 111, 110, 103])))) +wrong mint shape: Err(Runtime(MalFormedData(Integer(0)))) +wrong certificates shape: Err(Runtime(MalFormedData(Integer(0)))) +wrong withdrawals shape: Err(Runtime(MalFormedData(Integer(0)))) +wrong validity range shape: Err(Runtime(MalFormedData(Integer(0)))) +wrong signatories shape: Err(Runtime(MalFormedData(Integer(0)))) +wrong redeemers shape: Err(Runtime(MalFormedData(Integer(0)))) +wrong datums shape: Err(Runtime(MalFormedData(Integer(0)))) +wrong transaction id shape: Err(Runtime(MalFormedData(Integer(0)))) +wrong votes shape: Err(Runtime(MalFormedData(Integer(0)))) +wrong proposals shape: Err(Runtime(MalFormedData(Integer(0)))) +wrong current treasury shape: Err(Runtime(MalFormedData(Integer(0)))) +wrong treasury donation shape: Err(Runtime(MalFormedData(Integer(0)))) diff --git a/crates/nash-driver/tests/support/mod.rs b/crates/nash-driver/tests/support/mod.rs new file mode 100644 index 00000000..f1376c10 --- /dev/null +++ b/crates/nash-driver/tests/support/mod.rs @@ -0,0 +1,39 @@ +use nash_driver::{Database, InMemorySource, build_graph, build_with, bundled_base}; +use std::sync::Arc; +use tokio::sync::Mutex; +use url::Url; + +pub async fn compile_validator(source: &str) -> nash_driver::build::ValidatorOutput { + let memory = InMemorySource::new(); + let uri = Url::parse("file:///project/src/Main.nash").unwrap(); + memory.insert(uri.clone(), source.into()); + let mut origins = bundled_base::modules(); + origins.insert(uri, None); + let db = Arc::new(Mutex::new(Database::new(memory))); + let graph = build_graph(db.clone(), &origins.keys().cloned().collect::>()) + .await + .unwrap(); + let (report, result) = build_with(db, &graph, &origins, |solved| { + nash_driver::build::build_validators(solved, nash_config::Build::default()) + }) + .await; + assert!( + report.is_success(), + "{}", + report + .ordered_reports() + .iter() + .map(|reports| { + let view = nash_report::Source::new(&reports.source); + reports + .reports + .iter() + .map(|report| nash_report::render_plain(report, &view, &reports.path)) + .collect::>() + .join("\n") + }) + .collect::>() + .join("\n") + ); + result.unwrap().unwrap().remove(0) +} diff --git a/crates/nash-driver/tests/testing_base.rs b/crates/nash-driver/tests/testing_base.rs index b3652d7e..551f8cbe 100644 --- a/crates/nash-driver/tests/testing_base.rs +++ b/crates/nash-driver/tests/testing_base.rs @@ -1,27 +1,10 @@ //! Execute the real core testing helpers through production code generation. -use std::sync::Arc; +mod support; -use nash_driver::{Database, InMemorySource, build_graph, build_with}; use nash_plutus::{arena::Arena, syn, term::Term}; -use tokio::sync::Mutex; -use url::Url; async fn compile(body: &str) -> nash_driver::build::ValidatorOutput { - let memory = InMemorySource::new(); - let mut origins = nash_driver::bundled_base::modules(); - let uri = Url::parse("file:///project/src/TestingCore.nash").unwrap(); - memory.insert(uri.clone(), format!("validator module TestingCore exposing (main)\nimport Primitive exposing (type bool(..))\nimport Builtin\nimport Prelude exposing (..)\nimport Literal\nimport Num exposing (Num)\nimport Lift exposing (Lift)\nimport Prop exposing (type prng(..))\nimport Option exposing (type option(..))\nimport Test\nimport Cons\nimport List\none value = Cons.Cons value Cons.Nil\ntwo a b = Cons.Cons a (one b)\ng = Prop.Group\nc = Prop.Choice\nreplay : Cons.cons Prop.choiceTree -> prng\nreplay values = Replayed values Cons.Nil\nmain : Data -> unit\nmain _ =\n{body}\n")); - origins.insert(uri, None); - let db = Arc::new(Mutex::new(Database::new(memory))); - let graph = build_graph(db.clone(), &origins.keys().cloned().collect::>()) - .await - .unwrap(); - let (report, result) = build_with(db, &graph, &origins, |solved| { - nash_driver::build::build_validators(solved, nash_config::Build::default()) - }) - .await; - assert!(report.is_success(), "{report:#?}"); - result.unwrap().unwrap().remove(0) + support::compile_validator(&format!("validator module TestingCore exposing (main)\nimport Primitive exposing (type bool(..))\nimport Builtin\nimport Prelude exposing (..)\nimport Literal\nimport Num exposing (Num)\nimport Lift exposing (Lift)\nimport Prop exposing (type prng(..))\nimport Option exposing (type option(..))\nimport Test\nimport Cons\nimport List\none value = Cons.Cons value Cons.Nil\ntwo a b = Cons.Cons a (one b)\ng = Prop.Group\nc = Prop.Choice\nreplay : Cons.cons Prop.choiceTree -> prng\nreplay values = Replayed values Cons.Nil\nmain : Data -> unit\nmain _ =\n{body}\n")).await } #[tokio::test] diff --git a/docs/stdlib.md b/docs/stdlib.md index f43030a7..1ff83d25 100644 --- a/docs/stdlib.md +++ b/docs/stdlib.md @@ -57,7 +57,7 @@ crates/nash-driver/base/ Test.nash label, property preparation and assertion reporting Ast.nash macro AST: little ADTs over cons (see macros.md) Derive.nash @derive - Cardano/Tx.nash script context (sketch) + Cardano/Tx.nash V3 script context and governance types Cardano/Address.nash Cardano/Value.nash Cardano/Time.nash @@ -110,10 +110,19 @@ import Option exposing (Option, type option(..)) import Ordering exposing (Ordering, type ordering(..)) import Cons exposing (type cons(..)) import Builtin +import Int +import Bytes +import String +import List +import Map import Pair import Array import Prop import Test +import Cardano.Tx +import Cardano.Address +import Cardano.Value +import Cardano.Time ``` Consequences, matching representation.md "Prelude twins": @@ -1057,63 +1066,89 @@ and the per-trait derivations. Both are plain Nash with no `Data` or imports `Prelude`, `Cons`, and the trait modules; neither is imported by anything else in core, so no cycle. -## `Cardano.*` (sketch) - -Big ADTs mirroring the Plutus V3 `ScriptContext`. Ledger types are -`Constr` nodes, so they are declared as **constructors with labeled -fields** (`Constr i [fields]`, representation.md), never as record -aliases (which would encode as `List`). Nothing is decoded until a field -is touched, because Big values are `Data` until `lower`ed. +## `Cardano.*` + +The bundled `Cardano.Tx`, `Cardano.Address`, `Cardano.Time`, and +`Cardano.Value` modules use the Plutus V3 Data encodings. Import these +modules to expose short names; qualified names are available by default. +Their source ships with the compiler like the rest of Base. + +| Module | Public types and operations | +| --- | --- | +| `Cardano.Tx` | `ScriptContext`, `ScriptInfo`, `ScriptPurpose`, `Tx`, `Input`, `OutputReference`, `Output`, `Datum`; certificates, delegatees, representatives, voters, votes, proposals, governance actions, committees, constitutions, protocol versions, and rational wire pairs | +| `Cardano.Address` | `Address`, `Credential`, `StakingCredential` | +| `Cardano.Time` | `Extended`, `IntervalBound`, `Interval`; `interval`, `from`, `to`, `always`, `never`, `member`, `contains`, `isEmpty` | +| `Cardano.Value` | `Value`; `empty`, `singleton`, `insert`, `lovelace`, `quantityOf`, `add`, `contains`, `scale` | + +Ledger constructor types use Big nominal ADTs, including labeled +constructors. Record aliases would introduce the wrong List encoding. +V3 transaction IDs, hashes, credentials wrapped by governance newtypes, +and integer newtypes retain their underlying wire representation. +`Rational` encodes as `Constr 0 [numerator, denominator]`, matching the +Haskell tuple encoding; a native Nash tuple would use Term representation. + +`ScriptContext` contains `tx : Tx`, `redeemer : Data`, and +`scriptInfo : ScriptInfo`, in that order. Its spending info contains an +output reference and an optional datum. The separate `ScriptPurpose` +used as a redeemer-map key contains only the output reference for spending. +Both enums use the ledger's minting, spending, rewarding, certifying, +voting, and proposing order (tags 0 through 5). + +`Tx` fields follow the ledger order: inputs, reference inputs, outputs, +fee, mint, certificates, withdrawals, validity range, extra signatories, +redeemers, datums, ID, votes, proposals, current treasury, treasury donation. +The source declarations in `base/src/Cardano/Tx.nash` define all nested +field types and constructor orders. + +Every Big Cardano type has an explicit Nash `Validate` implementation. +These check constructor tags, exact field counts, and recursive field +representations. `Bool` and `Option` validation lives in their own modules. +This validates the wire shape, not ledger validity: it does not enforce +hash lengths, transaction balancing, governance rules, or normalize a +rational's numerator and denominator. `fromData` remains the universal +unchecked cast. Codegen adds no Cardano decoder. + +### Native values ```elm -module Cardano.Tx exposing (..) - -import Cardano.Address exposing (Address, Credential) -import Cardano.Value exposing (Value) -import Cardano.Time exposing (Interval) - -type ScriptContext = ScriptContext { tx : Tx, redeemer : Data, purpose : ScriptPurpose } - -type ScriptPurpose - = Minting Bytes - | Spending OutputReference (Option Data) - | Withdrawing Credential - | Publishing Int Certificate - | Voting Voter - | Proposing Int Proposal - -type Tx = Tx - { inputs : List Input - , referenceInputs : List Input - , outputs : List Output - , fee : Int - , mint : Value - , certificates : List Certificate - , withdrawals : Map Credential Int - , validityRange : Interval - , extraSignatories : List Bytes - , redeemers : Map ScriptPurpose Data - , datums : Map Bytes Data - , id : Bytes - , votes : Map Voter (Map GovernanceActionId Vote) - , proposals : List Proposal - , currentTreasury : Option Int - , treasuryDonation : Option Int - } - -type Input = Input { outputReference : OutputReference, output : Output } -type OutputReference = OutputReference { txId : Bytes, index : Int } -type Output = Output { address : Address, value : Value, datum : Datum, script : Option Bytes } -type Datum = NoDatum | DatumHash Bytes | InlineDatum Data +type alias Value = Map Bytes (Map Bytes Int) + +impl Lift value Value where + lift value = fromData (Builtin.valueData value) + lower value = Builtin.unValueData (toData value) ``` -`Cardano.Value` wraps the `value` builtin type (`lookupCoin`, `unionValue`, -`valueContains`, `scaleValue`) with `Lift value Value` via -`valueData`/`unValueData`, where `type alias Value = Map Bytes (Map Bytes Int)` -(a `Map`, which is the ledger encoding). `Cardano.Address` and -`Cardano.Time` (`Interval`, `IntervalBound`) follow the same pattern. Field -order and constructor tags must match the ledger's `Data` encoding -exactly; each is covered by a golden test against a real transaction. +Value helpers accept Big or little inputs and return little `value`, +`int`, or `bool`. They call `insertCoin`, `lookupCoin`, `unionValue`, +`valueContains`, and `scaleValue` directly. `insert` replaces a quantity; +zero removes that coin. `add` sums quantities. Signed amounts support mint +and burn values. + +`Validate Value` checks the nested map representation. Converting it with +`lower` also applies `unValueData`'s constraints: sorted unique keys of at +most 32 bytes, nonempty inner maps, and nonzero signed 128-bit quantities. +Native arithmetic can fail on overflow, and `contains` rejects negative +quantities. Builtin availability follows the compiler's target policy; +shipping these helpers is not a statement about mainnet activation. + +### Time intervals + +Time is an integer number of POSIX milliseconds. `interval start end` +includes both endpoints; `from` and `to` include their finite endpoint. +`member`, `contains`, and `isEmpty` return little booleans. Finite open +lower bounds compare as `n + 1`; finite open upper bounds as `n - 1`. +Infinity closure flags have no effect. Every interval contains an empty +interval, including `never`. These predicates follow the Haskell discrete +interval implementation, including equal infinity endpoints. + +The in-process tests compare seven synthetic contexts with CBOR emitted +by the pinned Plutus Haskell API, including exact serialized bytes. They +also compare 100 intervals, 500 membership checks, and 10,000 containment +pairs with executed Haskell results. Native value arithmetic and positive +containment also match the Haskell value oracle; zero-entry normalization and +negative containment follow the native builtin contracts above. The fixture source and regeneration +command are in `crates/nash-driver/tests/fixtures/cardano/reference/`. +These are codec fixtures, not transactions captured from the chain. ## Tracing and failure @@ -1128,6 +1163,5 @@ message emission; silent builds still fail. Failure lowers to UPLC error. 1. **`Prop`/`Test` as default imports.** Elm does not default-import test modules. Alternative: `tests` blocks get their own implicit `import Prop` / `import Test exposing (assert, label)` only. -2. **`value` builtins** (`InsertCoin` .. `ScaleValue`) exist in - `nash-plutus` but not in Plutus V3 mainnet; the table types them and - `nash build` gates them on the target version. +2. Keep **`value` builtin** availability aligned with the compiler target + policy; ledger deployment status is independent of these library wrappers. diff --git a/examples/vesting/README.md b/examples/vesting/README.md index 9ea86d91..8646b432 100644 --- a/examples/vesting/README.md +++ b/examples/vesting/README.md @@ -11,7 +11,7 @@ and a single CBOR byte string containing the Flat bytes under `build/`. `VestingParam` takes a native integer minimum-lock parameter before the three Data arguments. The regular validator takes only the three Data arguments. -`Cardano.Tx` is a test helper. Its context is `Constr 0 [I slot, B signer]`; +`VestingTx` is a test helper. Its context is `Constr 0 [I slot, B signer]`; it does not decode the Cardano ledger's real script context. The executable fixtures in `crates/nash-codegen/tests/vesting.rs` cover successful and failed claims and cancellations and record the unoptimized CPU/memory baseline. diff --git a/examples/vesting/app/src/Vesting.nash b/examples/vesting/app/src/Vesting.nash index 838c53fe..a5eb1974 100644 --- a/examples/vesting/app/src/Vesting.nash +++ b/examples/vesting/app/src/Vesting.nash @@ -2,7 +2,7 @@ validator module Vesting exposing (main) import Builtin exposing (..) import Lift exposing (Lift) -import Cardano.Tx exposing (currentSlot, signedBy) +import VestingTx exposing (currentSlot, signedBy) type Datum = Datum { owner : Bytes, deadline : Int } diff --git a/examples/vesting/app/src/VestingParam.nash b/examples/vesting/app/src/VestingParam.nash index f37484ba..28059d8c 100644 --- a/examples/vesting/app/src/VestingParam.nash +++ b/examples/vesting/app/src/VestingParam.nash @@ -2,7 +2,7 @@ validator module VestingParam exposing (main) import Builtin exposing (..) import Lift exposing (Lift) -import Cardano.Tx exposing (currentSlot, signedBy) +import VestingTx exposing (currentSlot, signedBy) type Datum = Datum { owner : Bytes, deadline : Int } diff --git a/examples/vesting/app/src/Cardano/Tx.nash b/examples/vesting/app/src/VestingTx.nash similarity index 89% rename from examples/vesting/app/src/Cardano/Tx.nash rename to examples/vesting/app/src/VestingTx.nash index 1bba8567..2adb1369 100644 --- a/examples/vesting/app/src/Cardano/Tx.nash +++ b/examples/vesting/app/src/VestingTx.nash @@ -1,4 +1,4 @@ -module Cardano.Tx exposing (currentSlot, signedBy) +module VestingTx exposing (currentSlot, signedBy) import Builtin exposing (..) import Lift exposing (Lift) diff --git a/plans/07-codegen.md b/plans/07-codegen.md index d9b88f20..9079a49b 100644 --- a/plans/07-codegen.md +++ b/plans/07-codegen.md @@ -2088,7 +2088,7 @@ text round-trips to. **Change** No new code. One integration test that compiles the overview's `Vesting` -validator with the stub `Cardano.Tx` helpers written in Nash inside the +validator with the stub `VestingTx` helpers written in Nash inside the fixture, snapshots the pretty UPLC and the `Core`, and evaluates it four ways with `Program::apply`: diff --git a/plans/12-stdlib.md b/plans/12-stdlib.md index bdec6ff3..6b752ab3 100644 --- a/plans/12-stdlib.md +++ b/plans/12-stdlib.md @@ -55,8 +55,8 @@ replace the tested PRNG, replay, label, and assertion protocols. ## Current status -Reconciled with chunk 8 implementation (2026-09-25). Base currently -ships 30 embedded Nash modules. Plan 12 remains incomplete in `SPEC.md`. +Reconciled with chunk 11 implementation (2026-09-25). Base currently +ships 34 embedded Nash modules. Plan 12 remains incomplete in `SPEC.md`. | Chunk | Status | Remaining work | |---|---|---| @@ -70,9 +70,10 @@ ships 30 embedded Nash modules. Plan 12 remains incomplete in `SPEC.md`. | 8 Prop | complete | none | | 9 Test | complete through Plan 10 | preserve existing runner protocol | | 10 Ast / Derive | not implemented | requires Plan 11 | -| 11 Cardano | not implemented in Base | library modules and ledger golden tests | +| 11 Cardano | implemented | Haskell codec fixtures, native value helpers, interval comparisons | -Latest full validation is recorded with chunk 8 below. +Chunk 8 records its historical validation; chunk 11 adds executed Haskell +comparisons and reruns the full workspace checks. Tests use compiler libraries and the evaluator, never a Nash CLI subprocess. ## Chunk 1: package skeleton and embedding — complete @@ -450,61 +451,38 @@ None; see plans/11. --- -## Chunk 11: `Cardano.*` — not implemented in Base +## Chunk 11: `Cardano.*` — implemented + +- [x] Ship Cardano.Tx, Cardano.Address, Cardano.Value and Cardano.Time. +- [x] Add Haskell-generated V3 context fixtures and decoding tests. +- [x] Compare exact CBOR bytes and discrete interval behavior with executed Haskell. + +The four modules live in `crates/nash-driver/base/src/Cardano/`. Big +constructor layouts follow the Plutus V3 API at revision +`39981dd733ae276975958e40b0291ce1b24781d5`. ScriptInfo and ScriptPurpose are +separate; V3 transaction IDs are transparent bytes. Validation is explicit +Nash source and checks recursive wire representations. Native value +helpers use the actual Plutus value builtins, normalize Big/little inputs, +and return little values. + +`crates/nash-driver/tests/cardano.rs` checks seven synthetic Haskell +contexts, exact CBOR serialization, malformed contexts, and the Haskell +interval results (100 intervals, 500 membership checks, 10,000 containment +pairs). Fixtures and the executable Haskell oracle are under +`crates/nash-driver/tests/fixtures/cardano/`. The Base snapshot harness +covers value operations, interval builders, and malformed nested values. +All Nash tests call the driver and evaluator directly. Codegen source fixtures +also use the driver dependency sorter instead of relying on a hand-ordered +module list. + +The vesting example's former Cardano.Tx stub is now VestingTx, preserving +its small testing context without colliding with the bundled ledger module. + +Validation: formatting and strict all-target/all-feature Clippy pass; the full +workspace has 3,451 passing tests and three existing ignored tests. Running +the documented Haskell regeneration command from a fresh pinned checkout +reproduces all ten committed CBOR fixtures byte for byte. -- [ ] Ship Cardano.Tx, Cardano.Address, Cardano.Value and Cardano.Time. -- [ ] Add real ledger context fixtures and decoding tests. - -The vesting example's Cardano helpers are fixtures, not the planned library. - -**Files** - -- `crates/nash-driver/base/src/Cardano/Tx.nash`, `Cardano/Address.nash`, `Cardano/Value.nash`, `Cardano/Time.nash` -- `tests/base/cardano/golden/*.cbor` (real V3 script contexts) -- `tests/base/cardano/src/CardanoTests.nash` - -**Change** - -Big ADTs for the V3 `ScriptContext` per docs/stdlib.md, `Lift value Value`, -interval helpers. Golden tests decode real contexts with -`Validate.validate` and check a few fields. - -**Code** (`crates/nash-driver/base/src/Cardano/Value.nash` excerpt) - -```elm -module Cardano.Value exposing (..) - -import Builtin - -type alias Value = Map Bytes (Map Bytes Int) - -impl Lift value Value where - lift = fromData << Builtin.valueData - lower = Builtin.unValueData << toData - -lovelace : Value -> int -lovelace v = Builtin.lookupCoin #"" #"" (lower v) - -quantityOf : bytes -> bytes -> Value -> int -quantityOf policy name v = Builtin.lookupCoin policy name (lower v) -``` - -Note the naming: `lift : value -> Value` here goes from Const to Big, -matching `Lift 'small 'big`'s direction (little is small). - -**Elm/Aiken reference** - -Aiken `stdlib/lib/cardano/transaction.ak`, `cardano/address.ak`, -`cardano/assets.ak` for field order and constructor tags (they match the -ledger). Plutus `plutus-ledger-api` `V3/Contexts.hs` is the source of -truth for the encoding. - -**Tests** - -`tests/base/cardano/src/CardanoTests.nash`: `validate` on each golden context succeeds with a typed value; `Tx.inputs` length matches; `lovelace` of the first output -matches the fixture. - -**Done when** all fixtures decode and the in-process Base compilation tests stays green. --- From d7301b1f5d833f84301bfdc7e58345701f2c2fd9 Mon Sep 17 00:00:00 2001 From: microproofs Date: Fri, 25 Sep 2026 16:04:29 -0400 Subject: [PATCH 05/10] refactor(data): consolidate conversion traits Signed-off-by: microproofs --- .sampo/changesets/data-conversion-traits.md | 12 + crates/nash-can/src/defaults.rs | 4 +- ..._blanket_without_validation_instances.snap | 2 +- crates/nash-codegen/src/tests/integration.rs | 14 +- crates/nash-driver/base/src/Bool.nash | 9 +- .../nash-driver/base/src/Cardano/Address.nash | 39 ++- crates/nash-driver/base/src/Cardano/Time.nash | 34 +- crates/nash-driver/base/src/Cardano/Tx.nash | 312 +++++++++++++++++- .../nash-driver/base/src/Cardano/Value.nash | 12 +- crates/nash-driver/base/src/Data.nash | 192 ++++++++++- crates/nash-driver/base/src/Data/Decode.nash | 134 -------- crates/nash-driver/base/src/Data/Encode.nash | 35 -- crates/nash-driver/base/src/Option.nash | 8 - crates/nash-driver/tests/base_traits.rs | 5 +- crates/nash-driver/tests/bundled_base.rs | 7 +- crates/nash-driver/tests/cardano.rs | 26 +- .../tests/fixtures/base-traits/Cardano.nash | 10 +- .../fixtures/base-traits/DataConversions.nash | 207 ++++++++++++ .../tests/fixtures/base-traits/Decoders.nash | 244 -------------- .../tests/fixtures/cardano/Context.nash | 1 + .../tests/fixtures/cardano/Decode.nash | 15 + .../base_traits__cardano_helpers.snap | 2 +- .../base_traits__data_conversions.snap | 64 ++++ .../snapshots/base_traits__data_decoders.snap | 96 ------ ...decode_list_rejects_function_elements.snap | 16 + ...ecoder_list_rejects_function_elements.snap | 30 -- .../snapshots/cardano__ledger_contexts.snap | 2 +- .../cardano__malformed_contexts.snap | 42 +-- crates/nash-plutus/src/flat/encode/mod.rs | 134 +++++--- docs/data.md | 94 +++--- docs/overview.md | 4 +- docs/representation.md | 18 +- docs/stdlib.md | 110 +++--- docs/traits.md | 10 +- plans/03-traits.md | 9 +- plans/06-diagnostics.md | 2 +- plans/08-optimizer.md | 4 +- plans/12-stdlib.md | 40 +-- 38 files changed, 1189 insertions(+), 810 deletions(-) create mode 100644 .sampo/changesets/data-conversion-traits.md delete mode 100644 crates/nash-driver/base/src/Data/Decode.nash delete mode 100644 crates/nash-driver/base/src/Data/Encode.nash create mode 100644 crates/nash-driver/tests/fixtures/base-traits/DataConversions.nash delete mode 100644 crates/nash-driver/tests/fixtures/base-traits/Decoders.nash create mode 100644 crates/nash-driver/tests/fixtures/cardano/Decode.nash create mode 100644 crates/nash-driver/tests/snapshots/base_traits__data_conversions.snap delete mode 100644 crates/nash-driver/tests/snapshots/base_traits__data_decoders.snap create mode 100644 crates/nash-driver/tests/snapshots/bundled_base__decode_list_rejects_function_elements.snap delete mode 100644 crates/nash-driver/tests/snapshots/bundled_base__decoder_list_rejects_function_elements.snap diff --git a/.sampo/changesets/data-conversion-traits.md b/.sampo/changesets/data-conversion-traits.md new file mode 100644 index 00000000..c038c128 --- /dev/null +++ b/.sampo/changesets/data-conversion-traits.md @@ -0,0 +1,12 @@ +--- +cargo/nash-driver: minor +cargo/nash-can: minor +cargo/nash-plutus: patch +--- + +Consolidate Data conversion and checking in traits. Allow explicit little-type +ToData and FromData implementations alongside Big-only identity blankets. Add +independent optional Decode instances, including Cardano V3 types, and remove +the separate encoder and decoder combinator modules. + +Encode Flat terms iteratively so large context decoders do not exhaust the host stack. diff --git a/crates/nash-can/src/defaults.rs b/crates/nash-can/src/defaults.rs index 5b05153f..a4c593e0 100644 --- a/crates/nash-can/src/defaults.rs +++ b/crates/nash-can/src/defaults.rs @@ -20,9 +20,7 @@ pub const MODULES: &[(&str, &[&str])] = &[ ("Applicative", &["Applicative"]), ("Monad", &["Monad"]), ("Lift", &["Lift"]), - ("Data", &["ToData", "FromData", "Validate"]), - ("Data.Decode", &[]), - ("Data.Encode", &[]), + ("Data", &["ToData", "FromData", "Validate", "Decode"]), ( "Literal", &["FromInt", "FromString", "FromBytes", "FromBool", "FromUnit"], diff --git a/crates/nash-codegen/src/build/tests/source/snapshots/nash_codegen__build__tests__source__builtins__from_data_blanket_without_validation_instances.snap b/crates/nash-codegen/src/build/tests/source/snapshots/nash_codegen__build__tests__source__builtins__from_data_blanket_without_validation_instances.snap index 4905d64b..eea78b0f 100644 --- a/crates/nash-codegen/src/build/tests/source/snapshots/nash_codegen__build__tests__source__builtins__from_data_blanket_without_validation_instances.snap +++ b/crates/nash-codegen/src/build/tests/source/snapshots/nash_codegen__build__tests__source__builtins__from_data_blanket_without_validation_instances.snap @@ -7,7 +7,7 @@ let fromBool#2 : bool -> bool = \value#16 : bool -> value#16 in let toData#3 : 'erased -> Data = \coerce#17 : 'erased -> coerce#17 in let fromInt#6 : int -> int = \value#21 : int -> value#21 in let fromData#25 : Data -> 'erased = \coerce#29 : 'erased -> coerce#29 in -let unchecked#18 : Data -> 'erased = fromData#25 in +let unchecked#18 : Data -> Data = fromData#25 in let wrapped#4 : { _0 : Main.Token } = unchecked#18 ((\field#19 : list Data -> listData field#19) (mkCons (constrData (fromInt#6 0) (mkCons ((\field#20 : int -> iData field#20) (fromInt#6 42)) (con (list data) []))) (con (list data) []))) in let fromBytes#11 : bytes -> bytes = \value#24 : bytes -> value#24 in let tokens#8 : List Main.Token = unchecked#18 ((\field#22 : list Data -> listData field#22) (mkCons ((\field#23 : bytes -> bData field#23) (fromBytes#11 (con bytestring #aa))) (con (list data) []))) in diff --git a/crates/nash-codegen/src/tests/integration.rs b/crates/nash-codegen/src/tests/integration.rs index ea4fdee4..36f70017 100644 --- a/crates/nash-codegen/src/tests/integration.rs +++ b/crates/nash-codegen/src/tests/integration.rs @@ -57,7 +57,7 @@ fn compile_selected( Some(primitives::BASE), ), ( - "module Option exposing (type option(..))\ntype option 'a = Some 'a | None\n", + include_str!("../../../nash-driver/base/src/Option.nash"), Some(primitives::BASE), ), ( @@ -84,6 +84,18 @@ fn compile_selected( include_str!("../../../nash-driver/base/src/Test.nash"), Some(primitives::BASE), ), + ( + include_str!("../../../nash-driver/base/src/Functor.nash"), + Some(primitives::BASE), + ), + ( + include_str!("../../../nash-driver/base/src/Applicative.nash"), + Some(primitives::BASE), + ), + ( + include_str!("../../../nash-driver/base/src/Monad.nash"), + Some(primitives::BASE), + ), (source, None), ]) { let text = bump.alloc_str(text); diff --git a/crates/nash-driver/base/src/Bool.nash b/crates/nash-driver/base/src/Bool.nash index a3bbef6a..68d634d5 100644 --- a/crates/nash-driver/base/src/Bool.nash +++ b/crates/nash-driver/base/src/Bool.nash @@ -3,7 +3,8 @@ module Bool exposing (Bool(..), not, and, or, xor) import Literal exposing (FromBool) import Primitive import Lift exposing (Lift) -import Data exposing (Validate) +import Data exposing (Validate, Decode) +import Option exposing (type option(..)) import Eq type Bool = False | True @@ -36,3 +37,9 @@ impl Validate Bool where pair(0, []) -> Bool.False pair(1, []) -> Bool.True _ -> fail + +impl Decode Bool where + decode value = case value of + Primitive.Constr pair(0, []) -> Some Bool.False + Primitive.Constr pair(1, []) -> Some Bool.True + _ -> None diff --git a/crates/nash-driver/base/src/Cardano/Address.nash b/crates/nash-driver/base/src/Cardano/Address.nash index f30b01ec..14399445 100644 --- a/crates/nash-driver/base/src/Cardano/Address.nash +++ b/crates/nash-driver/base/src/Cardano/Address.nash @@ -1,9 +1,11 @@ module Cardano.Address exposing (..) -import Data exposing (Validate) +import Primitive exposing (Data(..)) +import Monad exposing (Monad) +import Data exposing (Validate, Decode) import Eq import Literal -import Option exposing (Option) +import Option exposing (Option, type option(..)) type Credential = PubKeyCredential Bytes | ScriptCredential Bytes @@ -30,3 +32,36 @@ impl Validate Address where case Builtin.unConstrData raw of pair(0, [f0, f1]) -> Address (validate f0) (validate f1) _ -> fail + +impl Decode Credential where + decode raw = + case raw of + Constr pair(0, [f0]) -> do + d0 <- decode f0 + Some (PubKeyCredential d0) + Constr pair(1, [f0]) -> do + d0 <- decode f0 + Some (ScriptCredential d0) + _ -> None + +impl Decode StakingCredential where + decode raw = + case raw of + Constr pair(0, [f0]) -> do + d0 <- decode f0 + Some (StakingHash d0) + Constr pair(1, [f0, f1, f2]) -> do + d0 <- decode f0 + d1 <- decode f1 + d2 <- decode f2 + Some (StakingPtr d0 d1 d2) + _ -> None + +impl Decode Address where + decode raw = + case raw of + Constr pair(0, [f0, f1]) -> do + d0 <- decode f0 + d1 <- decode f1 + Some (Address d0 d1) + _ -> None diff --git a/crates/nash-driver/base/src/Cardano/Time.nash b/crates/nash-driver/base/src/Cardano/Time.nash index 0305d55d..0c4e88c9 100644 --- a/crates/nash-driver/base/src/Cardano/Time.nash +++ b/crates/nash-driver/base/src/Cardano/Time.nash @@ -1,9 +1,11 @@ module Cardano.Time exposing (Extended(..), IntervalBound(..), Interval(..), interval, from, to, always, never, member, contains, isEmpty) +import Monad exposing (Monad) +import Option exposing (type option(..)) import Prelude exposing (..) -import Primitive exposing (type bool(..)) +import Primitive exposing (Data(..), type bool(..)) import Num -import Data exposing (Validate) +import Data exposing (Validate, Decode) import Bool exposing (Bool) import Lift exposing (Lift) @@ -83,3 +85,31 @@ always = Interval (IntervalBound NegInf Bool.True) (IntervalBound PosInf Bool.Tr never : Interval never = Interval (IntervalBound PosInf Bool.True) (IntervalBound NegInf Bool.True) + +impl Decode Extended where + decode raw = + case raw of + Constr pair(0, []) -> Some NegInf + Constr pair(1, [f0]) -> do + d0 <- decode f0 + Some (Finite d0) + Constr pair(2, []) -> Some PosInf + _ -> None + +impl Decode IntervalBound where + decode raw = + case raw of + Constr pair(0, [f0, f1]) -> do + d0 <- decode f0 + d1 <- decode f1 + Some (IntervalBound d0 d1) + _ -> None + +impl Decode Interval where + decode raw = + case raw of + Constr pair(0, [f0, f1]) -> do + d0 <- decode f0 + d1 <- decode f1 + Some (Interval d0 d1) + _ -> None diff --git a/crates/nash-driver/base/src/Cardano/Tx.nash b/crates/nash-driver/base/src/Cardano/Tx.nash index be25881e..744febb7 100644 --- a/crates/nash-driver/base/src/Cardano/Tx.nash +++ b/crates/nash-driver/base/src/Cardano/Tx.nash @@ -1,9 +1,11 @@ module Cardano.Tx exposing (..) -import Data exposing (Validate) +import Primitive exposing (Data(..)) +import Monad exposing (Monad) +import Data exposing (Validate, Decode) import Eq import Literal -import Option exposing (Option) +import Option exposing (Option, type option(..)) import Cardano.Address exposing (Address, Credential) import Cardano.Time exposing (Interval) import Cardano.Value exposing (Value) @@ -219,3 +221,309 @@ impl Validate ScriptContext where case Builtin.unConstrData raw of pair(0, [f0, f1, f2]) -> ScriptContext (validate f0) (validate f1) (validate f2) _ -> fail + +impl Decode DRep where + decode raw = + case raw of + Constr pair(0, [f0]) -> do + d0 <- decode f0 + Some (DRep d0) + Constr pair(1, []) -> Some DRepAlwaysAbstain + Constr pair(2, []) -> Some DRepAlwaysNoConfidence + _ -> None + +impl Decode Delegatee where + decode raw = + case raw of + Constr pair(0, [f0]) -> do + d0 <- decode f0 + Some (DelegStake d0) + Constr pair(1, [f0]) -> do + d0 <- decode f0 + Some (DelegVote d0) + Constr pair(2, [f0, f1]) -> do + d0 <- decode f0 + d1 <- decode f1 + Some (DelegStakeVote d0 d1) + _ -> None + +impl Decode Certificate where + decode raw = + case raw of + Constr pair(0, [f0, f1]) -> do + d0 <- decode f0 + d1 <- decode f1 + Some (RegStaking d0 d1) + Constr pair(1, [f0, f1]) -> do + d0 <- decode f0 + d1 <- decode f1 + Some (UnRegStaking d0 d1) + Constr pair(2, [f0, f1]) -> do + d0 <- decode f0 + d1 <- decode f1 + Some (DelegStaking d0 d1) + Constr pair(3, [f0, f1, f2]) -> do + d0 <- decode f0 + d1 <- decode f1 + d2 <- decode f2 + Some (RegDeleg d0 d1 d2) + Constr pair(4, [f0, f1]) -> do + d0 <- decode f0 + d1 <- decode f1 + Some (RegDRep d0 d1) + Constr pair(5, [f0]) -> do + d0 <- decode f0 + Some (UpdateDRep d0) + Constr pair(6, [f0, f1]) -> do + d0 <- decode f0 + d1 <- decode f1 + Some (UnRegDRep d0 d1) + Constr pair(7, [f0, f1]) -> do + d0 <- decode f0 + d1 <- decode f1 + Some (PoolRegister d0 d1) + Constr pair(8, [f0, f1]) -> do + d0 <- decode f0 + d1 <- decode f1 + Some (PoolRetire d0 d1) + Constr pair(9, [f0, f1]) -> do + d0 <- decode f0 + d1 <- decode f1 + Some (AuthHotCommittee d0 d1) + Constr pair(10, [f0]) -> do + d0 <- decode f0 + Some (ResignColdCommittee d0) + _ -> None + +impl Decode Voter where + decode raw = + case raw of + Constr pair(0, [f0]) -> do + d0 <- decode f0 + Some (CommitteeVoter d0) + Constr pair(1, [f0]) -> do + d0 <- decode f0 + Some (DRepVoter d0) + Constr pair(2, [f0]) -> do + d0 <- decode f0 + Some (StakePoolVoter d0) + _ -> None + +impl Decode Vote where + decode raw = + case raw of + Constr pair(0, []) -> Some VoteNo + Constr pair(1, []) -> Some VoteYes + Constr pair(2, []) -> Some Abstain + _ -> None + +impl Decode GovernanceActionId where + decode raw = + case raw of + Constr pair(0, [f0, f1]) -> do + d0 <- decode f0 + d1 <- decode f1 + Some (GovernanceActionId d0 d1) + _ -> None + +impl Decode Rational where + decode raw = + case raw of + Constr pair(0, [f0, f1]) -> do + d0 <- decode f0 + d1 <- decode f1 + Some (Rational d0 d1) + _ -> None + +impl Decode Committee where + decode raw = + case raw of + Constr pair(0, [f0, f1]) -> do + d0 <- decode f0 + d1 <- decode f1 + Some (Committee d0 d1) + _ -> None + +impl Decode Constitution where + decode raw = + case raw of + Constr pair(0, [f0]) -> do + d0 <- decode f0 + Some (Constitution d0) + _ -> None + +impl Decode ProtocolVersion where + decode raw = + case raw of + Constr pair(0, [f0, f1]) -> do + d0 <- decode f0 + d1 <- decode f1 + Some (ProtocolVersion d0 d1) + _ -> None + +impl Decode GovernanceAction where + decode raw = + case raw of + Constr pair(0, [f0, f1, f2]) -> do + d0 <- decode f0 + d1 <- decode f1 + d2 <- decode f2 + Some (ParameterChange d0 d1 d2) + Constr pair(1, [f0, f1]) -> do + d0 <- decode f0 + d1 <- decode f1 + Some (HardForkInitiation d0 d1) + Constr pair(2, [f0, f1]) -> do + d0 <- decode f0 + d1 <- decode f1 + Some (TreasuryWithdrawals d0 d1) + Constr pair(3, [f0]) -> do + d0 <- decode f0 + Some (NoConfidence d0) + Constr pair(4, [f0, f1, f2, f3]) -> do + d0 <- decode f0 + d1 <- decode f1 + d2 <- decode f2 + d3 <- decode f3 + Some (UpdateCommittee d0 d1 d2 d3) + Constr pair(5, [f0, f1]) -> do + d0 <- decode f0 + d1 <- decode f1 + Some (NewConstitution d0 d1) + Constr pair(6, []) -> Some InfoAction + _ -> None + +impl Decode Proposal where + decode raw = + case raw of + Constr pair(0, [f0, f1, f2]) -> do + d0 <- decode f0 + d1 <- decode f1 + d2 <- decode f2 + Some (Proposal d0 d1 d2) + _ -> None + +impl Decode OutputReference where + decode raw = + case raw of + Constr pair(0, [f0, f1]) -> do + d0 <- decode f0 + d1 <- decode f1 + Some (OutputReference d0 d1) + _ -> None + +impl Decode Datum where + decode raw = + case raw of + Constr pair(0, []) -> Some NoDatum + Constr pair(1, [f0]) -> do + d0 <- decode f0 + Some (DatumHash d0) + Constr pair(2, [f0]) -> do + d0 <- decode f0 + Some (InlineDatum d0) + _ -> None + +impl Decode Output where + decode raw = + case raw of + Constr pair(0, [f0, f1, f2, f3]) -> do + d0 <- decode f0 + d1 <- decode f1 + d2 <- decode f2 + d3 <- decode f3 + Some (Output d0 d1 d2 d3) + _ -> None + +impl Decode Input where + decode raw = + case raw of + Constr pair(0, [f0, f1]) -> do + d0 <- decode f0 + d1 <- decode f1 + Some (Input d0 d1) + _ -> None + +impl Decode ScriptPurpose where + decode raw = + case raw of + Constr pair(0, [f0]) -> do + d0 <- decode f0 + Some (Minting d0) + Constr pair(1, [f0]) -> do + d0 <- decode f0 + Some (Spending d0) + Constr pair(2, [f0]) -> do + d0 <- decode f0 + Some (Rewarding d0) + Constr pair(3, [f0, f1]) -> do + d0 <- decode f0 + d1 <- decode f1 + Some (Certifying d0 d1) + Constr pair(4, [f0]) -> do + d0 <- decode f0 + Some (Voting d0) + Constr pair(5, [f0, f1]) -> do + d0 <- decode f0 + d1 <- decode f1 + Some (Proposing d0 d1) + _ -> None + +impl Decode ScriptInfo where + decode raw = + case raw of + Constr pair(0, [f0]) -> do + d0 <- decode f0 + Some (MintingScript d0) + Constr pair(1, [f0, f1]) -> do + d0 <- decode f0 + d1 <- decode f1 + Some (SpendingScript d0 d1) + Constr pair(2, [f0]) -> do + d0 <- decode f0 + Some (RewardingScript d0) + Constr pair(3, [f0, f1]) -> do + d0 <- decode f0 + d1 <- decode f1 + Some (CertifyingScript d0 d1) + Constr pair(4, [f0]) -> do + d0 <- decode f0 + Some (VotingScript d0) + Constr pair(5, [f0, f1]) -> do + d0 <- decode f0 + d1 <- decode f1 + Some (ProposingScript d0 d1) + _ -> None + +impl Decode Tx where + decode raw = + case raw of + Constr pair(0, [f0, f1, f2, f3, f4, f5, f6, f7, f8, f9, f10, f11, f12, f13, f14, f15]) -> do + d0 <- decode f0 + d1 <- decode f1 + d2 <- decode f2 + d3 <- decode f3 + d4 <- decode f4 + d5 <- decode f5 + d6 <- decode f6 + d7 <- decode f7 + d8 <- decode f8 + d9 <- decode f9 + d10 <- decode f10 + d11 <- decode f11 + d12 <- decode f12 + d13 <- decode f13 + d14 <- decode f14 + d15 <- decode f15 + Some (Tx d0 d1 d2 d3 d4 d5 d6 d7 d8 d9 d10 d11 d12 d13 d14 d15) + _ -> None + +impl Decode ScriptContext where + decode raw = + case raw of + Constr pair(0, [f0, f1, f2]) -> do + d0 <- decode f0 + d1 <- decode f1 + d2 <- decode f2 + Some (ScriptContext d0 d1 d2) + _ -> None diff --git a/crates/nash-driver/base/src/Cardano/Value.nash b/crates/nash-driver/base/src/Cardano/Value.nash index 1fcc1874..add3e596 100644 --- a/crates/nash-driver/base/src/Cardano/Value.nash +++ b/crates/nash-driver/base/src/Cardano/Value.nash @@ -1,7 +1,8 @@ module Cardano.Value exposing (Value, empty, singleton, insert, lovelace, quantityOf, add, contains, scale) +import Option exposing (type option(..)) import Lift exposing (Lift) -import Data exposing (FromData, ToData, Validate) +import Data exposing (FromData, ToData, Validate, Decode) type alias Value = Map Bytes (Map Bytes Int) @@ -43,3 +44,12 @@ contains left right = Builtin.valueContains (lower left) (lower right) scale : (Lift int 'n, Lift value 'v) => 'n -> 'v -> value scale amount value = Builtin.scaleValue (lower amount) (lower value) + +impl Decode Value where + decode raw = + let checked : option (Map Bytes (Map Bytes Int)) + checked = decode raw + in + case checked of + Some value -> Some (fromData (toData value)) + None -> None diff --git a/crates/nash-driver/base/src/Data.nash b/crates/nash-driver/base/src/Data.nash index 14a9d5f4..bc45d300 100644 --- a/crates/nash-driver/base/src/Data.nash +++ b/crates/nash-driver/base/src/Data.nash @@ -1,16 +1,20 @@ -module Data exposing (ToData, FromData, Validate, serialise, tag, fields) +module Data exposing (ToData, FromData, Validate, Decode, serialise, tag, fields) import Builtin +import Literal +import Eq exposing (Eq) +import Option exposing (Option, type option(..)) +import Monad exposing (Monad) -import Primitive exposing (Data(..)) +import Primitive exposing (Big, Data(..), type bool(..)) -trait ToData ('a : Big) where +trait ToData 'a where toData : 'a -> Data impl ToData ('a : Big) where toData = Primitive.coerce -trait FromData ('a : Big) where +trait FromData 'a where fromData : Data -> 'a impl FromData ('a : Big) where @@ -73,3 +77,183 @@ mapList f values = pairValidate : (Validate 'k, Validate 'v) => pair Data Data -> pair 'k 'v pairValidate pair(key, value) = Builtin.mkPairData (validate key) (validate value) + +impl Validate 'a => Validate (Option 'a) where + validate value = + case Builtin.unConstrData value of + pair(0, [item]) -> Option.Some (validate item) + pair(1, []) -> Option.None + _ -> fail + +trait Decode 'a where + decode : Data -> option 'a + +impl ToData int where + toData value = Primitive.coerce (Builtin.iData value) + +impl FromData int where + fromData value = Builtin.unIData (Primitive.coerce value) + +impl Decode int where + decode value = case value of + I number -> Some number + _ -> None + +impl ToData bytes where + toData value = Primitive.coerce (Builtin.bData value) + +impl FromData bytes where + fromData value = Builtin.unBData (Primitive.coerce value) + +impl Decode bytes where + decode value = case value of + B raw -> Some raw + _ -> None + +impl ToData string where + toData value = toData (Builtin.encodeUtf8 value) + +impl FromData string where + fromData value = Builtin.decodeUtf8 (fromData value) + +impl Decode Data where + decode value = Some value + +impl Decode Int where + decode value = case value of + I _ -> Some (fromData value) + _ -> None + +impl Decode Bytes where + decode value = case value of + B _ -> Some (fromData value) + _ -> None + +impl ToData bool where + toData value = Builtin.constrData (if value then 1 else 0) [] + +impl FromData bool where + fromData value = case Builtin.unConstrData value of + pair(0, []) -> False + pair(1, []) -> True + _ -> fail + +impl Decode bool where + decode value = case value of + Constr pair(0, []) -> Some False + Constr pair(1, []) -> Some True + _ -> None + +impl ToData unit where + toData _ = Builtin.constrData 0 [] + +impl FromData unit where + fromData value = case Builtin.unConstrData value of + pair(0, []) -> () + _ -> fail + +impl Decode unit where + decode value = case value of + Constr pair(0, []) -> Some () + _ -> None + +impl ToData 'a => ToData (list 'a) where + toData values = Primitive.coerce (Builtin.listData (mapList toData values)) + +impl FromData 'a => FromData (list 'a) where + fromData value = mapList fromData (Builtin.unListData (Primitive.coerce value)) + +impl Decode 'a => Decode (list 'a) where + decode value = case value of + List items -> decodeList items + _ -> None + +decodeList : Decode 'a => list Data -> option (list 'a) +decodeList values = case values of + [] -> Some [] + first :: rest -> do + item <- decode first + items <- decodeList rest + Some (Builtin.mkCons item items) + +impl (Big 'a, Decode 'a) => Decode (List 'a) where + decode value = case value of + List items -> do + decoded <- decodeList items + Some (Builtin.listData decoded) + _ -> None + +impl (Big 'k, Big 'v, Decode 'k, Decode 'v) => Decode (Map 'k 'v) where + decode value = case value of + Map entries -> do + decoded <- decodeEntries entries + Some (Builtin.mapData decoded) + _ -> None + +decodeEntries : (Big 'k, Big 'v, Decode 'k, Decode 'v) => list (pair Data Data) -> option (list (pair 'k 'v)) +decodeEntries entries = case entries of + [] -> Some [] + pair(key, value) :: rest -> do + k <- decode key + v <- decode value + tail <- decodeEntries rest + Some (Builtin.mkCons (Builtin.mkPairData k v) tail) + +impl ToData 'a => ToData (option 'a) where + toData value = case value of + Some item -> Builtin.constrData 0 [toData item] + None -> Builtin.constrData 1 [] + +impl FromData 'a => FromData (option 'a) where + fromData value = case Builtin.unConstrData value of + pair(0, [item]) -> Some (fromData item) + pair(1, []) -> None + _ -> fail + +impl Decode 'a => Decode (option 'a) where + decode value = case value of + Constr pair(0, [item]) -> do + decoded <- decode item + Some (Some decoded) + Constr pair(1, []) -> Some None + _ -> None + +impl (Big 'a, Decode 'a) => Decode (Option 'a) where + decode value = case value of + Constr pair(0, [item]) -> do + decoded <- decode item + Some (Option.Some decoded) + Constr pair(1, []) -> Some Option.None + _ -> None + +impl Decode string where + decode value = case value of + B raw -> + if utf8 raw (Builtin.lengthOfByteString raw) 0 then Some (Builtin.decodeUtf8 raw) + else None + _ -> None + +utf8 : bytes -> int -> int -> bool +utf8 raw size index = + if Builtin.equalsInteger index size then True + else + let first = Builtin.indexByteString raw index in + if Builtin.lessThanInteger first 128 then utf8 raw size (Builtin.addInteger index 1) + else if (if Builtin.lessThanEqualsInteger 194 first then Builtin.lessThanEqualsInteger first 223 else False) then continuation raw size (Builtin.addInteger index 1) 1 128 191 + else if Builtin.equalsInteger first 224 then continuation raw size (Builtin.addInteger index 1) 2 160 191 + else if (if Builtin.lessThanEqualsInteger 225 first then Builtin.lessThanEqualsInteger first 236 else False) then continuation raw size (Builtin.addInteger index 1) 2 128 191 + else if Builtin.equalsInteger first 237 then continuation raw size (Builtin.addInteger index 1) 2 128 159 + else if (if Builtin.lessThanEqualsInteger 238 first then Builtin.lessThanEqualsInteger first 239 else False) then continuation raw size (Builtin.addInteger index 1) 2 128 191 + else if Builtin.equalsInteger first 240 then continuation raw size (Builtin.addInteger index 1) 3 144 191 + else if (if Builtin.lessThanEqualsInteger 241 first then Builtin.lessThanEqualsInteger first 243 else False) then continuation raw size (Builtin.addInteger index 1) 3 128 191 + else if Builtin.equalsInteger first 244 then continuation raw size (Builtin.addInteger index 1) 3 128 143 + else False + +continuation : bytes -> int -> int -> int -> int -> int -> bool +continuation raw size index remaining low high = + if Builtin.equalsInteger remaining 0 then utf8 raw size index + else if Builtin.lessThanEqualsInteger size index then False + else + let next = Builtin.indexByteString raw index in + if (if Builtin.lessThanInteger next low then True else Builtin.lessThanInteger high next) then False + else continuation raw size (Builtin.addInteger index 1) (Builtin.subtractInteger remaining 1) 128 191 diff --git a/crates/nash-driver/base/src/Data/Decode.nash b/crates/nash-driver/base/src/Data/Decode.nash deleted file mode 100644 index 13e63ce7..00000000 --- a/crates/nash-driver/base/src/Data/Decode.nash +++ /dev/null @@ -1,134 +0,0 @@ -module Data.Decode exposing (type decoder, run, int, bytes, string, bool, data, list, map, constr, field, oneOf, reject) - -import Primitive exposing (Data(..), type bool(..)) -import Builtin -import Prelude exposing (..) -import Literal -import Option exposing (type option(..)) -import Cons exposing (type cons(..)) -import List -import Functor -import Applicative -import Monad - -type alias decoder 'a = Data -> option 'a - -run : decoder 'a -> Data -> option 'a -run dec value = dec value - -impl Functor.Functor decoder where - map f dec value = Option.map f (dec value) - -impl Applicative.Applicative decoder where - pure value _ = Some value - apply functions values input = do - f <- functions input - value <- values input - Some (f value) - -impl Monad.Monad decoder where - bind dec next input = do - value <- dec input - next value input - -reject : decoder 'a -reject _ = None - -int : decoder int -int value = case value of - I number -> Some number - _ -> None - -bytes : decoder bytes -bytes value = case value of - B raw -> Some raw - _ -> None - -string : decoder string -string value = case value of - B raw -> - if utf8 raw (Builtin.lengthOfByteString raw) 0 then Some (Builtin.decodeUtf8 raw) - else None - _ -> None - -utf8 : bytes -> int -> int -> bool -utf8 raw size index = - if index == size then True - else - let first = Builtin.indexByteString raw index in - if first < 128 then utf8 raw size (index + 1) - else if first >= 194 && first <= 223 then continuation raw size (index + 1) 1 128 191 - else if first == 224 then continuation raw size (index + 1) 2 160 191 - else if first >= 225 && first <= 236 then continuation raw size (index + 1) 2 128 191 - else if first == 237 then continuation raw size (index + 1) 2 128 159 - else if first >= 238 && first <= 239 then continuation raw size (index + 1) 2 128 191 - else if first == 240 then continuation raw size (index + 1) 3 144 191 - else if first >= 241 && first <= 243 then continuation raw size (index + 1) 3 128 191 - else if first == 244 then continuation raw size (index + 1) 3 128 143 - else False - -continuation : bytes -> int -> int -> int -> int -> int -> bool -continuation raw size index remaining low high = - if remaining == 0 then utf8 raw size index - else if index >= size then False - else - let next = Builtin.indexByteString raw index in - if next < low || next > high then False - else continuation raw size (index + 1) (remaining - 1) 128 191 - -bool : decoder bool -bool value = case value of - Constr pair(tag, []) -> - if tag == 0 then Some False - else if tag == 1 then Some True - else None - _ -> None - -data : decoder Data -data value = Some value - -list : decoder 'a -> decoder (list 'a) -list item value = case value of - List values -> decodeList item values - _ -> None - -decodeList : decoder 'a -> list Data -> option (list 'a) -decodeList item values = case values of - [] -> Some [] - first :: rest -> do - value <- item first - tail <- decodeList item rest - Some (Builtin.mkCons value tail) - -map : decoder 'k -> decoder 'v -> decoder (cons ('k, 'v)) -map key item value = case value of - Map entries -> decodeEntries key item entries - _ -> None - -decodeEntries : decoder 'k -> decoder 'v -> list (pair Data Data) -> option (cons ('k, 'v)) -decodeEntries key item entries = case entries of - [] -> Some Nil - pair(k, v) :: rest -> do - decodedKey <- key k - decodedValue <- item v - tail <- decodeEntries key item rest - Some (Cons (decodedKey, decodedValue) tail) - -constr : int -> decoder 'a -> decoder 'a -constr expected inner value = case value of - Constr pair(tag, _) -> if tag == expected then inner value else None - _ -> None - -field : int -> decoder 'a -> decoder 'a -field index inner value = case value of - Constr pair(_, items) -> do - item <- List.at index items - inner item - _ -> None - -oneOf : cons (decoder 'a) -> decoder 'a -oneOf decoders value = case decoders of - Nil -> None - Cons first rest -> case first value of - Some item -> Some item - None -> oneOf rest value diff --git a/crates/nash-driver/base/src/Data/Encode.nash b/crates/nash-driver/base/src/Data/Encode.nash deleted file mode 100644 index 06c53ffa..00000000 --- a/crates/nash-driver/base/src/Data/Encode.nash +++ /dev/null @@ -1,35 +0,0 @@ -module Data.Encode exposing (int, bytes, string, bool, list, constr, map) - -import Primitive -import Builtin -import Literal -import Lift exposing (Lift) -import Data exposing (ToData) -import List -import Cons exposing (type cons(..)) - -int : Lift int 'a => 'a -> Data -int value = toData (Builtin.iData (lower value)) - -bytes : Lift bytes 'a => 'a -> Data -bytes value = toData (Builtin.bData (lower value)) - -string : string -> Data -string value = toData (Builtin.bData (Builtin.encodeUtf8 value)) - -bool : Lift bool 'a => 'a -> Data -bool value = Builtin.constrData (if lower value then 1 else 0) [] - -list : Lift (list 'a) ('f 'a) => ('a -> Data) -> 'f 'a -> Data -list encode values = toData (Builtin.listData (List.map encode values)) - -constr : (Lift int 'n, Lift (list Data) ('f Data)) => 'n -> 'f Data -> Data -constr tag fields = Builtin.constrData (lower tag) (lower fields) - -map : ('k -> Data) -> ('v -> Data) -> cons ('k, 'v) -> Data -map key value entries = toData (Builtin.mapData (encodeEntries key value entries)) - -encodeEntries : ('k -> Data) -> ('v -> Data) -> cons ('k, 'v) -> list (pair Data Data) -encodeEntries key value entries = case entries of - Nil -> [] - Cons (k, v) rest -> Builtin.mkCons (Builtin.mkPairData (key k) (value v)) (encodeEntries key value rest) diff --git a/crates/nash-driver/base/src/Option.nash b/crates/nash-driver/base/src/Option.nash index acb0ff31..ce0e3c49 100644 --- a/crates/nash-driver/base/src/Option.nash +++ b/crates/nash-driver/base/src/Option.nash @@ -6,7 +6,6 @@ import Lift exposing (Lift) import Functor import Applicative exposing (Applicative) import Monad exposing (Monad) -import Data exposing (Validate) type option 'a = Some 'a | None type Option 'a = Some 'a | None @@ -85,10 +84,3 @@ unwrap value = lowerOuter : Lift (option 'a) ('f 'a) => 'f 'a -> option 'a lowerOuter = lower - -impl Validate 'a => Validate (Option 'a) where - validate value = - case Builtin.unConstrData value of - pair(0, [item]) -> Option.Some (validate item) - pair(1, []) -> Option.None - _ -> fail diff --git a/crates/nash-driver/tests/base_traits.rs b/crates/nash-driver/tests/base_traits.rs index 9c094fbb..03d942df 100644 --- a/crates/nash-driver/tests/base_traits.rs +++ b/crates/nash-driver/tests/base_traits.rs @@ -119,7 +119,10 @@ base_snapshot!( base_snapshot!(generate_bounds, "fixtures/base-traits/PropBounds.nash"); -base_snapshot!(data_decoders, "fixtures/base-traits/Decoders.nash"); +base_snapshot!( + data_conversions, + "fixtures/base-traits/DataConversions.nash" +); base_snapshot!(map_helpers, "fixtures/base-traits/Maps.nash"); base_snapshot!(representation_classes, "fixtures/base-traits/Classed.nash"); diff --git a/crates/nash-driver/tests/bundled_base.rs b/crates/nash-driver/tests/bundled_base.rs index 6e4e3214..b90c5c90 100644 --- a/crates/nash-driver/tests/bundled_base.rs +++ b/crates/nash-driver/tests/bundled_base.rs @@ -266,13 +266,12 @@ async fn map_singleton_rejects_little_components() { } #[tokio::test] -async fn decoder_list_rejects_function_elements() { +async fn decode_list_rejects_function_elements() { assert_base_type_error_snapshot!( r#" module Main exposing (..) - functions : Data.Decode.decoder (int -> int) - functions = pure (\x -> x) - bad = Data.Decode.list functions + bad : Data -> option (list (int -> int)) + bad = decode "# ); } diff --git a/crates/nash-driver/tests/cardano.rs b/crates/nash-driver/tests/cardano.rs index 95e7056a..bebd42ee 100644 --- a/crates/nash-driver/tests/cardano.rs +++ b/crates/nash-driver/tests/cardano.rs @@ -97,6 +97,8 @@ async fn ledger_interval_semantics() { async fn malformed_contexts() { let source = include_str!("fixtures/cardano/Validate.nash"); let output = support::compile_validator(source).await; + let decode_source = include_str!("fixtures/cardano/Decode.nash"); + let decoder = support::compile_validator(decode_source).await; let arena = Arena::new(); let bytes = include_bytes!("fixtures/cardano/golden/context-0.cbor"); let valid = PlutusData::from_cbor(&arena, bytes).unwrap(); @@ -174,9 +176,29 @@ async fn malformed_contexts() { .apply(&evaluation_arena, Term::data(&evaluation_arena, data)) .eval(&evaluation_arena); assert_eq!(result.term.is_ok(), expected, "{name}: {:?}", result.term); - results.push_str(&format!("{name}: {:?}\n", result.term)); + let input = PlutusData::constr( + &evaluation_arena, + 0, + evaluation_arena.alloc_slice_copy(&[ + PlutusData::integer_from(&evaluation_arena, i128::from(expected)), + data, + ]), + ); + let decode_result = syn::parse_program(&evaluation_arena, &decoder.uplc) + .unwrap() + .apply(&evaluation_arena, Term::data(&evaluation_arena, input)) + .eval(&evaluation_arena); + assert!( + decode_result.term.is_ok(), + "decode {name}: {:?}", + decode_result.term + ); + results.push_str(&format!( + "{name}: validate {:?}; decode matches expected: {:?}\n", + result.term, decode_result.term + )); } - insta::with_settings!({description => source, omit_expression => true}, { + insta::with_settings!({description => format!("{source}\n{decode_source}"), omit_expression => true}, { insta::assert_snapshot!(results); }); } diff --git a/crates/nash-driver/tests/fixtures/base-traits/Cardano.nash b/crates/nash-driver/tests/fixtures/base-traits/Cardano.nash index 19382c11..797f87a9 100644 --- a/crates/nash-driver/tests/fixtures/base-traits/Cardano.nash +++ b/crates/nash-driver/tests/fixtures/base-traits/Cardano.nash @@ -44,20 +44,20 @@ tests test "Bool invalid tag" fail = do let _ = checkBool (Builtin.constrData 2 []) in () test "Bool extra field" fail = do - let _ = checkBool (Builtin.constrData 0 [Data.Encode.int 1]) in () + let _ = checkBool (Builtin.constrData 0 [toData 1]) in () test "Option missing field" fail = do let _ = checkOption (Builtin.constrData 0 []) in () test "Option wrong element" fail = do - let _ = checkOption (Builtin.constrData 0 [Data.Encode.bytes #"aa"]) in () + let _ = checkOption (Builtin.constrData 0 [toData #"aa"]) in () test "Option extra field" fail = do - let _ = checkOption (Builtin.constrData 1 [Data.Encode.int 1]) in () + let _ = checkOption (Builtin.constrData 1 [toData 1]) in () test "record rejects wrong tag" fail = do let _ = checkAddress (Builtin.constrData 1 [toData (Address.PubKeyCredential (lift #"aa")), Builtin.constrData 1 []]) in () test "record rejects missing field" fail = do let _ = checkAddress (Builtin.constrData 0 []) in () test "record rejects extra field" fail = do - let _ = checkAddress (Builtin.constrData 0 [toData (Address.PubKeyCredential (lift #"aa")), Builtin.constrData 1 [], Data.Encode.int 1]) in () + let _ = checkAddress (Builtin.constrData 0 [toData (Address.PubKeyCredential (lift #"aa")), Builtin.constrData 1 [], toData 1]) in () test "record rejects invalid nested credential" fail = do let _ = checkAddress (Builtin.constrData 0 [Builtin.constrData 2 [], Builtin.constrData 1 []]) in () test "Value rejects non-map" fail = do - let _ = checkValue (Data.Encode.int 1) in () + let _ = checkValue (toData 1) in () diff --git a/crates/nash-driver/tests/fixtures/base-traits/DataConversions.nash b/crates/nash-driver/tests/fixtures/base-traits/DataConversions.nash new file mode 100644 index 00000000..271821c1 --- /dev/null +++ b/crates/nash-driver/tests/fixtures/base-traits/DataConversions.nash @@ -0,0 +1,207 @@ +module Main exposing (..) + +type count = Count int + +impl ToData count where + toData (Count n) = toData n + +impl FromData count where + fromData raw = Count (fromData raw) + +impl Decode count where + decode raw = do + n <- decode raw + Some (Count n) + +asInt : Data -> option int +asInt = decode +asBytes : Data -> option bytes +asBytes = decode +asString : Data -> option string +asString = decode +asBool : Data -> option bool +asBool = decode +asUnit : Data -> option unit +asUnit = decode +asList : Data -> option (list int) +asList = decode +asBigList : Data -> option (List Int) +asBigList = decode +asMap : Data -> option (Map Int Bytes) +asMap = decode +asOption : Data -> option (option int) +asOption = decode +asBigOption : Data -> option (Option Int) +asBigOption = decode +asData : Data -> option Data +asData = decode +unchecked : Data -> Int +unchecked = fromData +checked : Data -> Int +checked = validate +little : Data -> int +little = fromData +text : Data -> string +text = fromData +countValue : count +countValue = fromData (I 42) +countDecoded : option count +countDecoded = decode (I 42) + +entries = [Builtin.mkPairData (I 1) (B #"aa"), Builtin.mkPairData (I 1) (B #"bb")] + +number : int +number = 42 +rawBytes : bytes +rawBytes = #"aaff" +yes : bool +yes = True +no : bool +no = False +numbers : list int +numbers = [1, 2] +bigOne : Int +bigOne = 1 +firstBytes : Bytes +firstBytes = #"aa" +secondBytes : Bytes +secondBytes = #"bb" +three : int +three = 3 + +fromList : Data -> list int +fromList = fromData +fromOption : Data -> option int +fromOption = fromData +fromBool : Data -> bool +fromBool = fromData +fromUnit : Data -> unit +fromUnit = fromData +fromBytes : Data -> bytes +fromBytes = fromData +bigBool : Data -> option Bool +bigBool = decode + +tests + test "little list unpacking" = do + assert (fromList (toData numbers) == numbers) + test "little option unpacking" = do + assert (fromOption (toData (Some number)) == Some number) + test "little bool unpacking" = do + assert (fromBool (toData yes) == yes) + test "little unit unpacking" = do + assert (fromUnit (toData ()) == ()) + test "little bytes unpacking" = do + assert (fromBytes (toData rawBytes) == rawBytes) + test "Big Bool optional decoding" = do + assert (bigBool (toData yes) == Some Bool.True) + test "Big Bool optional rejection" = do + assert (bigBool (Builtin.constrData 0 [I 1]) == None) + test "little string unpacking rejects bad UTF8" fail = do + let _ = text (B #"80") in () + test "little scalar conversions 1" = do + assert (toData number == I 42) + test "little scalar conversions 2" = do + assert (little (I 42) == 42) + test "little scalar conversions 3" = do + assert (asBytes (toData rawBytes) == Some #"aaff") + test "little scalar conversions 4" = do + assert (asString (toData "é") == Some "é") + test "little scalar conversions 5" = do + assert (text (toData "é") == "é") + test "little scalar conversions 6" = do + assert (asBool (toData yes) == Some True) + test "little scalar conversions 7" = do + assert (asBool (toData no) == Some False) + test "little scalar conversions 8" = do + assert (asUnit (toData ()) == Some ()) + test "user little conversions and ordinary option composition 1" = do + assert (toData countValue == I 42) + test "user little conversions and ordinary option composition 2" = do + assert (case countDecoded of + Some (Count n) -> n == 42 + None -> False) + test "Big blanket conversion remains unchecked 1" = do + assert (toData (unchecked (B #"aa")) == B #"aa") + test "Big blanket conversion remains unchecked 2" = do + assert (toData (checked (I 42)) == I 42) + test "independent validation fails" fail = do + let _ = checked (B #"aa") in () + test "little unpacking fails" fail = do + let _ = little (B #"aa") in () + test "scalar decode wrong shapes 1" = do + assert (asInt (B #"aa") == None) + test "scalar decode wrong shapes 2" = do + assert (asBytes (I 1) == None) + test "scalar decode wrong shapes 3" = do + assert (asString (I 1) == None) + test "scalar decode wrong shapes 4" = do + assert (asInt (List []) == None) + test "scalar decode wrong shapes 5" = do + assert (asInt (Map []) == None) + test "scalar decode wrong shapes 6" = do + assert (asInt (Builtin.constrData 0 []) == None) + test "boolean and unit exact shape 1" = do + assert (asBool (Builtin.constrData 2 []) == None) + test "boolean and unit exact shape 2" = do + assert (asBool (Builtin.constrData 0 [I 1]) == None) + test "boolean and unit exact shape 3" = do + assert (asBool (I 0) == None) + test "boolean and unit exact shape 4" = do + assert (asUnit (Builtin.constrData 1 []) == None) + test "boolean and unit exact shape 5" = do + assert (asUnit (Builtin.constrData 0 [I 1]) == None) + test "UTF8 accepts boundaries 1" = do + assert (asString (B #"00c280e0a080eda080") == None) + test "UTF8 accepts boundaries 2" = do + assert (asString (B #"00c280e0a080ed9fbff0908080f48fbfbf") /= None) + test "UTF8 rejects malformed sequences 1" = do + assert (asString (B #"80") == None) + test "UTF8 rejects malformed sequences 2" = do + assert (asString (B #"c080") == None) + test "UTF8 rejects malformed sequences 3" = do + assert (asString (B #"c2") == None) + test "UTF8 rejects malformed sequences 4" = do + assert (asString (B #"e08080") == None) + test "UTF8 rejects malformed sequences 5" = do + assert (asString (B #"eda080") == None) + test "UTF8 rejects malformed sequences 6" = do + assert (asString (B #"f0808080") == None) + test "UTF8 rejects malformed sequences 7" = do + assert (asString (B #"f4908080") == None) + test "UTF8 rejects malformed sequences 8" = do + assert (asString (B #"f5808080") == None) + test "little collection conversion 1" = do + assert (asList (toData numbers) == Some [1, 2]) + test "little collection conversion 2" = do + assert (asList (List []) == Some []) + test "little collection conversion 3" = do + assert (asList (List [I 1, B #"aa"]) == None) + test "little collection conversion 4" = do + assert (asList (I 1) == None) + test "Big collection decode validates children 1" = do + assert (asBigList (List [I 1, I 2]) == Some (lift [1, 2])) + test "Big collection decode validates children 2" = do + assert (asBigList (List [B #"aa"]) == None) + test "Big collection decode validates children 3" = do + assert (asMap (Map entries) == Some (lift [Builtin.mkPairData bigOne firstBytes, Builtin.mkPairData bigOne secondBytes])) + test "Big collection decode validates children 4" = do + assert (asMap (Map [Builtin.mkPairData (B #"aa") (B #"bb")]) == None) + test "Big collection decode validates children 5" = do + assert (asMap (Map [Builtin.mkPairData (I 1) (I 2)]) == None) + test "optional values exact shape 1" = do + assert (asOption (toData (Some three)) == Some (Some 3)) + test "optional values exact shape 2" = do + assert (asOption (Builtin.constrData 1 []) == Some None) + test "optional values exact shape 3" = do + assert (asOption (Builtin.constrData 0 []) == None) + test "optional values exact shape 4" = do + assert (asOption (Builtin.constrData 0 [B #"aa"]) == None) + test "optional values exact shape 5" = do + assert (asOption (Builtin.constrData 1 [I 1]) == None) + test "optional values exact shape 6" = do + assert (asBigOption (Builtin.constrData 0 [I 3]) == Some (Option.Some 3)) + test "optional values exact shape 7" = do + assert (asBigOption (Builtin.constrData 0 [B #"aa"]) == None) + test "Data decode preserves any shape 1" = do + assert (asData (Map entries) == Some (Map entries)) diff --git a/crates/nash-driver/tests/fixtures/base-traits/Decoders.nash b/crates/nash-driver/tests/fixtures/base-traits/Decoders.nash deleted file mode 100644 index d3f47849..00000000 --- a/crates/nash-driver/tests/fixtures/base-traits/Decoders.nash +++ /dev/null @@ -1,244 +0,0 @@ -module Main exposing (..) - -import Data.Decode as Decode -import Data.Encode as Encode -import Cons exposing (type cons(..)) - -record : Decode.decoder (int, bytes) -record = Decode.constr 7 <| do - first <- Decode.field 0 Decode.int - second <- Decode.field 1 Decode.bytes - pure (first, second) - -applied : Decode.decoder int -applied = pure (\a b -> a + b) <*> Decode.field 0 Decode.int <*> Decode.field 1 Decode.int - -constant : Decode.decoder int -constant = pure 42 - -emptyAlternatives : Decode.decoder int -emptyAlternatives = Decode.oneOf Nil - -bad : Decode.decoder int -bad _ = fail - -function : Decode.decoder (int -> int) -function = map (\n -> \x -> n + x) Decode.int - -emptyPairs : cons (int, int) -emptyPairs = Nil - -encoded = Encode.constr 7 [Encode.int 42, Encode.bytes #"ff"] - -bigInt : Int -bigInt = 42 -bigBytes : Bytes -bigBytes = lift #"ff" -bigBool : Bool -bigBool = True -bigList : List Int -bigList = lift [bigInt] -bigFields : List Data -bigFields = lift [Encode.int bigInt] -rejected : Decode.decoder int -rejected = Decode.reject -rejectBind : Decode.decoder int -rejectBind = do - _ <- rejected - bad -rejectApply : Decode.decoder int -rejectApply = pure (\a b -> a + b) <*> rejected <*> bad - -tests - test "implicit qualified decoder modules" = do - assert (Data.Decode.run Data.Decode.int (Data.Encode.int 5) == Some 5) - test "bind rejection skips continuation" = do - assert (Decode.run rejectBind encoded == None) - test "applicative rejection skips argument" = do - assert (Decode.run rejectApply encoded == None) - test "empty map decoding" = do - assert (Decode.run (Decode.map Decode.int Decode.int) (Encode.map Encode.int Encode.int emptyPairs) == Some Nil) - test "map key failure" = do - assert (Decode.run (Decode.map Decode.bytes Decode.int) (Encode.map Encode.int Encode.int (Cons (1, 2) Nil)) == None) - test "extra constructor fields accepted" = do - assert (Decode.run record (Encode.constr 7 [Encode.int 42, Encode.bytes #"ff", Encode.int 9]) == Some (42, #"ff")) - test "Big int encoding" = do - assert (Decode.run Decode.int (Encode.int bigInt) == Some 42) - test "Big bytes encoding" = do - assert (Decode.run Decode.bytes (Encode.bytes bigBytes) == Some #"ff") - test "Big bool encoding" = do - assert (Decode.run Decode.bool (Encode.bool bigBool) == Some True) - test "Big list encoding" = do - assert (Decode.run (Decode.list Decode.int) (Encode.list Encode.int bigList) == Some [42]) - test "Big constructor inputs" = do - assert (Data.tag (Encode.constr bigInt bigFields) == 42) - test "primitive decoding and wrong shapes 1" = do - assert (Decode.run Decode.int (Encode.int -42) == Some -42) - test "primitive decoding and wrong shapes 2" = do - assert (Decode.run Decode.bytes (Encode.bytes #"00ff") == Some #"00ff") - test "primitive decoding and wrong shapes 3" = do - assert (Decode.run Decode.data encoded == Some encoded) - test "primitive decoding and wrong shapes 4" = do - assert (Decode.run Decode.int (Encode.bytes #"") == None) - test "primitive decoding and wrong shapes 5" = do - assert (Decode.run Decode.bytes (Encode.int 1) == None) - test "primitive decoding and wrong shapes 6" = do - assert (Decode.run Decode.int (Encode.list Encode.int [1]) == None) - test "primitive decoding and wrong shapes 7" = do - assert (Decode.run Decode.int (Encode.map Encode.int Encode.int emptyPairs) == None) - test "primitive decoding and wrong shapes 8" = do - assert (Decode.run Decode.int encoded == None) - test "primitive decoding and wrong shapes 9" = do - assert (Decode.run Decode.string (Encode.int 1) == None) - test "booleans require exact constructor shape 1" = do - assert (Decode.run Decode.bool (Encode.bool True) == Some True) - test "booleans require exact constructor shape 2" = do - assert (Decode.run Decode.bool (Encode.bool False) == Some False) - test "booleans require exact constructor shape 3" = do - assert (Decode.run Decode.bool (Encode.constr 2 []) == None) - test "booleans require exact constructor shape 4" = do - assert (Decode.run Decode.bool (Encode.constr 0 [Encode.int 1]) == None) - test "booleans require exact constructor shape 5" = do - assert (Decode.run Decode.bool (Encode.constr 1 [Encode.int 1]) == None) - test "booleans require exact constructor shape 6" = do - assert (Decode.run Decode.bool (Encode.int 1) == None) - test "UTF8 accepts empty" = do - assert (Option.isSome (Decode.run Decode.string (Encode.bytes #""))) - test "UTF8 accepts 00" = do - assert (Option.isSome (Decode.run Decode.string (Encode.bytes #"00"))) - test "UTF8 accepts 7f" = do - assert (Option.isSome (Decode.run Decode.string (Encode.bytes #"7f"))) - test "UTF8 accepts c280" = do - assert (Option.isSome (Decode.run Decode.string (Encode.bytes #"c280"))) - test "UTF8 accepts dfbf" = do - assert (Option.isSome (Decode.run Decode.string (Encode.bytes #"dfbf"))) - test "UTF8 accepts e0a080" = do - assert (Option.isSome (Decode.run Decode.string (Encode.bytes #"e0a080"))) - test "UTF8 accepts ed9fbf" = do - assert (Option.isSome (Decode.run Decode.string (Encode.bytes #"ed9fbf"))) - test "UTF8 accepts ee8080" = do - assert (Option.isSome (Decode.run Decode.string (Encode.bytes #"ee8080"))) - test "UTF8 accepts efbfbf" = do - assert (Option.isSome (Decode.run Decode.string (Encode.bytes #"efbfbf"))) - test "UTF8 accepts f0908080" = do - assert (Option.isSome (Decode.run Decode.string (Encode.bytes #"f0908080"))) - test "UTF8 accepts f48fbfbf" = do - assert (Option.isSome (Decode.run Decode.string (Encode.bytes #"f48fbfbf"))) - test "UTF8 accepts 61c28062" = do - assert (Option.isSome (Decode.run Decode.string (Encode.bytes #"61c28062"))) - test "UTF8 accepts e18080" = do - assert (Option.isSome (Decode.run Decode.string (Encode.bytes #"e18080"))) - test "UTF8 accepts ecbfbf" = do - assert (Option.isSome (Decode.run Decode.string (Encode.bytes #"ecbfbf"))) - test "UTF8 accepts f1808080" = do - assert (Option.isSome (Decode.run Decode.string (Encode.bytes #"f1808080"))) - test "UTF8 accepts f3bfbfbf" = do - assert (Option.isSome (Decode.run Decode.string (Encode.bytes #"f3bfbfbf"))) - test "UTF8 rejects 80" = do - assert (Decode.run Decode.string (Encode.bytes #"80") == None) - test "UTF8 rejects bf" = do - assert (Decode.run Decode.string (Encode.bytes #"bf") == None) - test "UTF8 rejects c080" = do - assert (Decode.run Decode.string (Encode.bytes #"c080") == None) - test "UTF8 rejects c1bf" = do - assert (Decode.run Decode.string (Encode.bytes #"c1bf") == None) - test "UTF8 rejects e09fbf" = do - assert (Decode.run Decode.string (Encode.bytes #"e09fbf") == None) - test "UTF8 rejects eda080" = do - assert (Decode.run Decode.string (Encode.bytes #"eda080") == None) - test "UTF8 rejects f08fbfbf" = do - assert (Decode.run Decode.string (Encode.bytes #"f08fbfbf") == None) - test "UTF8 rejects f4908080" = do - assert (Decode.run Decode.string (Encode.bytes #"f4908080") == None) - test "UTF8 rejects f5808080" = do - assert (Decode.run Decode.string (Encode.bytes #"f5808080") == None) - test "UTF8 rejects ff" = do - assert (Decode.run Decode.string (Encode.bytes #"ff") == None) - test "UTF8 rejects c2" = do - assert (Decode.run Decode.string (Encode.bytes #"c2") == None) - test "UTF8 rejects e0" = do - assert (Decode.run Decode.string (Encode.bytes #"e0") == None) - test "UTF8 rejects e0a0" = do - assert (Decode.run Decode.string (Encode.bytes #"e0a0") == None) - test "UTF8 rejects f0" = do - assert (Decode.run Decode.string (Encode.bytes #"f0") == None) - test "UTF8 rejects f090" = do - assert (Decode.run Decode.string (Encode.bytes #"f090") == None) - test "UTF8 rejects f09080" = do - assert (Decode.run Decode.string (Encode.bytes #"f09080") == None) - test "UTF8 rejects c27f" = do - assert (Decode.run Decode.string (Encode.bytes #"c27f") == None) - test "UTF8 rejects c2c0" = do - assert (Decode.run Decode.string (Encode.bytes #"c2c0") == None) - test "UTF8 rejects e0a07f" = do - assert (Decode.run Decode.string (Encode.bytes #"e0a07f") == None) - test "UTF8 rejects f090807f" = do - assert (Decode.run Decode.string (Encode.bytes #"f090807f") == None) - test "UTF8 rejects 61ff" = do - assert (Decode.run Decode.string (Encode.bytes #"61ff") == None) - test "UTF8 rejects e0a0c0" = do - assert (Decode.run Decode.string (Encode.bytes #"e0a0c0") == None) - test "UTF8 rejects f09080c0" = do - assert (Decode.run Decode.string (Encode.bytes #"f09080c0") == None) - test "UTF8 text roundtrip" = do - assert (Decode.run Decode.string (Encode.string "hello λ 😀") == Some "hello λ 😀") - test "constructor decoder do" = do - assert (Decode.run record encoded == Some (42, #"ff")) - test "constructor wrong tag" = do - assert (Decode.run record (Encode.constr 8 []) == None) - test "constructor wrong shape" = do - assert (Decode.run record (Encode.int 7) == None) - test "negative field index" = do - assert (Decode.run (Decode.field -1 Decode.int) encoded == None) - test "missing field" = do - assert (Decode.run (Decode.field 2 Decode.int) encoded == None) - test "field on nonconstructor" = do - assert (Decode.run (Decode.field 0 Decode.int) (Encode.int 0) == None) - test "direct tag" = do - assert (Data.tag encoded == 7) - test "direct fields" = do - assert (Data.fields encoded == [Encode.int 42, Encode.bytes #"ff"]) - test "applicative and bind share original input 1" = do - assert (Decode.run applied (Encode.constr 0 [Encode.int 2, Encode.int 3]) == Some 5) - test "applicative and bind share original input 2" = do - assert (Decode.run constant encoded == Some 42) - test "applicative and bind share original input 3" = do - assert (Decode.run (Decode.oneOf (Cons Decode.reject (Cons Decode.int (Cons bad Nil)))) (Encode.int 5) == Some 5) - test "applicative and bind share original input 4" = do - assert (Decode.run emptyAlternatives encoded == None) - test "applicative and bind share original input 5" = do - assert (Decode.run (Decode.oneOf (Cons Decode.int Nil)) encoded == None) - test "list decoding" = do - assert (Decode.run (Decode.list Decode.int) (Encode.list Encode.int [1, 2, 3]) == Some [1, 2, 3]) - test "empty list decoding" = do - assert (Decode.run (Decode.list Decode.int) (Encode.list identity []) == Some []) - test "list item failure" = do - assert (Decode.run (Decode.list Decode.int) (Encode.list identity [Encode.int 1, Encode.bytes #"", Encode.int 3]) == None) - test "list wrong shape" = do - assert (Decode.run (Decode.list Decode.int) (Encode.int 1) == None) - test "map order and duplicates" = do - assert (Decode.run (Decode.map Decode.int Decode.bytes) (Encode.map Encode.int Encode.bytes (Cons (1, #"aa") (Cons (1, #"bb") Nil))) == Some (Cons (1, #"aa") (Cons (1, #"bb") Nil))) - test "map value failure" = do - assert (Decode.run (Decode.map Decode.int Decode.bytes) (Encode.map Encode.int Encode.int (Cons (1, 2) Nil)) == None) - test "map wrong shape" = do - assert (Decode.run (Decode.map Decode.int Decode.bytes) (Encode.int 1) == None) - test "decoded maps can contain functions" = do - case Decode.run (Decode.map Decode.int function) (Encode.map Encode.int Encode.int (Cons (1, 7) Nil)) of - Some (Cons (key, f) Nil) -> assert (key == 1 && f 3 == 10) - _ -> fail - test "tag expects constructor" fail = do - let _ = Data.tag (Encode.int 1) in () - test "fields expects constructor" fail = do - let _ = Data.fields (Encode.bytes #"") in () - prop "integer roundtrip" = - let value via Prop.int in - do - assert (Decode.run Decode.int (Encode.int value) == Some value) - prop "bytes roundtrip" = - let value via Prop.bytes in - do - assert (Decode.run Decode.bytes (Encode.bytes value) == Some value) - prop "integer list roundtrip" = - let values via Prop.listBetween 0 10 Prop.int in - do - assert (Decode.run (Decode.list Decode.int) (Encode.list Encode.int values) == Some values) diff --git a/crates/nash-driver/tests/fixtures/cardano/Context.nash b/crates/nash-driver/tests/fixtures/cardano/Context.nash index 16d49051..3c0b992c 100644 --- a/crates/nash-driver/tests/fixtures/cardano/Context.nash +++ b/crates/nash-driver/tests/fixtures/cardano/Context.nash @@ -100,6 +100,7 @@ check raw encoded = (SpendingScript ref Option.None) _ -> ScriptContext tx (toData (integer 27)) (List.at index infos |> Option.unwrap) _ = assert (context == expected) + _ = assert (decode raw == Some expected) _ = assert (Data.serialise (toData expected) == encoded) _ = assert (Value.lovelace (List.at 0 context.tx.outputs |> Option.unwrap).value == 100) _ = assert (Value.quantityOf #"706f6c696379" #"6173736574" context.tx.mint == -2) diff --git a/crates/nash-driver/tests/fixtures/cardano/Decode.nash b/crates/nash-driver/tests/fixtures/cardano/Decode.nash new file mode 100644 index 00000000..afb9844d --- /dev/null +++ b/crates/nash-driver/tests/fixtures/cardano/Decode.nash @@ -0,0 +1,15 @@ +validator module DecodeContext exposing (main) + +import Cardano.Tx exposing (ScriptContext) + +checked : Data -> option ScriptContext +checked = decode + +main : Data -> unit +main input = + case input of + Constr pair(0, [I expected, raw]) -> + case checked raw of + Some _ -> if expected == 1 then () else fail + None -> if expected == 0 then () else fail + _ -> fail diff --git a/crates/nash-driver/tests/snapshots/base_traits__cardano_helpers.snap b/crates/nash-driver/tests/snapshots/base_traits__cardano_helpers.snap index 7ec4e354..fdcc0135 100644 --- a/crates/nash-driver/tests/snapshots/base_traits__cardano_helpers.snap +++ b/crates/nash-driver/tests/snapshots/base_traits__cardano_helpers.snap @@ -1,6 +1,6 @@ --- source: crates/nash-driver/tests/base_traits.rs -description: "module Main exposing (..)\n\nimport Cardano.Time as Time\nimport Cardano.Tx as Tx\nimport Cardano.Address as Address\nimport Cardano.Value as Value exposing (Value)\n\ncheckBool : Data -> bool\ncheckBool raw = let checked : Bool\n checked = validate raw\n in lower checked\ncheckOption : Data -> option Int\ncheckOption raw = let checked : Option Int\n checked = validate raw\n in lower checked\ncheckCommittee raw = let checked : Tx.Committee\n checked = validate raw\n in toData checked\ncheckAddress raw = let checked : Address.Address\n checked = validate raw\n in toData checked\ncheckValue raw = let checked : Value\n checked = validate raw\n in toData checked\n\ncommittee = Tx.Committee (Builtin.mapData []) (Tx.Rational 2 3)\n\nbigStart : Int\nbigStart = lift (-10)\n\ntests\n test \"closed interval builders accept Big endpoints\" = do\n assert (Time.member bigStart (Time.interval bigStart 20))\n assert (Time.member 20 (Time.interval bigStart 20))\n assert (Bool.not (Time.member 21 (Time.interval bigStart 20)))\n assert (Time.contains Time.always (Time.interval bigStart 20))\n assert (Time.contains (Time.from bigStart) (Time.to -20) == False)\n assert (Time.contains (Time.interval 0 1) Time.never)\n assert (Time.isEmpty Time.never)\n assert (Time.member -999 (Time.to 20))\n assert (Time.member 999 (Time.from 20))\n test \"committee validates rational tuple representation\" = do\n assert (checkCommittee (toData committee) == toData committee)\n test \"Bool invalid tag\" fail = do\n let _ = checkBool (Builtin.constrData 2 []) in ()\n test \"Bool extra field\" fail = do\n let _ = checkBool (Builtin.constrData 0 [Data.Encode.int 1]) in ()\n test \"Option missing field\" fail = do\n let _ = checkOption (Builtin.constrData 0 []) in ()\n test \"Option wrong element\" fail = do\n let _ = checkOption (Builtin.constrData 0 [Data.Encode.bytes #\"aa\"]) in ()\n test \"Option extra field\" fail = do\n let _ = checkOption (Builtin.constrData 1 [Data.Encode.int 1]) in ()\n test \"record rejects wrong tag\" fail = do\n let _ = checkAddress (Builtin.constrData 1 [toData (Address.PubKeyCredential (lift #\"aa\")), Builtin.constrData 1 []]) in ()\n test \"record rejects missing field\" fail = do\n let _ = checkAddress (Builtin.constrData 0 []) in ()\n test \"record rejects extra field\" fail = do\n let _ = checkAddress (Builtin.constrData 0 [toData (Address.PubKeyCredential (lift #\"aa\")), Builtin.constrData 1 [], Data.Encode.int 1]) in ()\n test \"record rejects invalid nested credential\" fail = do\n let _ = checkAddress (Builtin.constrData 0 [Builtin.constrData 2 [], Builtin.constrData 1 []]) in ()\n test \"Value rejects non-map\" fail = do\n let _ = checkValue (Data.Encode.int 1) in ()\n" +description: "module Main exposing (..)\n\nimport Cardano.Time as Time\nimport Cardano.Tx as Tx\nimport Cardano.Address as Address\nimport Cardano.Value as Value exposing (Value)\n\ncheckBool : Data -> bool\ncheckBool raw = let checked : Bool\n checked = validate raw\n in lower checked\ncheckOption : Data -> option Int\ncheckOption raw = let checked : Option Int\n checked = validate raw\n in lower checked\ncheckCommittee raw = let checked : Tx.Committee\n checked = validate raw\n in toData checked\ncheckAddress raw = let checked : Address.Address\n checked = validate raw\n in toData checked\ncheckValue raw = let checked : Value\n checked = validate raw\n in toData checked\n\ncommittee = Tx.Committee (Builtin.mapData []) (Tx.Rational 2 3)\n\nbigStart : Int\nbigStart = lift (-10)\n\ntests\n test \"closed interval builders accept Big endpoints\" = do\n assert (Time.member bigStart (Time.interval bigStart 20))\n assert (Time.member 20 (Time.interval bigStart 20))\n assert (Bool.not (Time.member 21 (Time.interval bigStart 20)))\n assert (Time.contains Time.always (Time.interval bigStart 20))\n assert (Time.contains (Time.from bigStart) (Time.to -20) == False)\n assert (Time.contains (Time.interval 0 1) Time.never)\n assert (Time.isEmpty Time.never)\n assert (Time.member -999 (Time.to 20))\n assert (Time.member 999 (Time.from 20))\n test \"committee validates rational tuple representation\" = do\n assert (checkCommittee (toData committee) == toData committee)\n test \"Bool invalid tag\" fail = do\n let _ = checkBool (Builtin.constrData 2 []) in ()\n test \"Bool extra field\" fail = do\n let _ = checkBool (Builtin.constrData 0 [toData 1]) in ()\n test \"Option missing field\" fail = do\n let _ = checkOption (Builtin.constrData 0 []) in ()\n test \"Option wrong element\" fail = do\n let _ = checkOption (Builtin.constrData 0 [toData #\"aa\"]) in ()\n test \"Option extra field\" fail = do\n let _ = checkOption (Builtin.constrData 1 [toData 1]) in ()\n test \"record rejects wrong tag\" fail = do\n let _ = checkAddress (Builtin.constrData 1 [toData (Address.PubKeyCredential (lift #\"aa\")), Builtin.constrData 1 []]) in ()\n test \"record rejects missing field\" fail = do\n let _ = checkAddress (Builtin.constrData 0 []) in ()\n test \"record rejects extra field\" fail = do\n let _ = checkAddress (Builtin.constrData 0 [toData (Address.PubKeyCredential (lift #\"aa\")), Builtin.constrData 1 [], toData 1]) in ()\n test \"record rejects invalid nested credential\" fail = do\n let _ = checkAddress (Builtin.constrData 0 [Builtin.constrData 2 [], Builtin.constrData 1 []]) in ()\n test \"Value rejects non-map\" fail = do\n let _ = checkValue (toData 1) in ()\n" --- closed interval builders accept Big endpoints: Pass; traces: [] committee validates rational tuple representation: Pass; traces: [] diff --git a/crates/nash-driver/tests/snapshots/base_traits__data_conversions.snap b/crates/nash-driver/tests/snapshots/base_traits__data_conversions.snap new file mode 100644 index 00000000..f78c50db --- /dev/null +++ b/crates/nash-driver/tests/snapshots/base_traits__data_conversions.snap @@ -0,0 +1,64 @@ +--- +source: crates/nash-driver/tests/base_traits.rs +description: "module Main exposing (..)\n\ntype count = Count int\n\nimpl ToData count where\n toData (Count n) = toData n\n\nimpl FromData count where\n fromData raw = Count (fromData raw)\n\nimpl Decode count where\n decode raw = do\n n <- decode raw\n Some (Count n)\n\nasInt : Data -> option int\nasInt = decode\nasBytes : Data -> option bytes\nasBytes = decode\nasString : Data -> option string\nasString = decode\nasBool : Data -> option bool\nasBool = decode\nasUnit : Data -> option unit\nasUnit = decode\nasList : Data -> option (list int)\nasList = decode\nasBigList : Data -> option (List Int)\nasBigList = decode\nasMap : Data -> option (Map Int Bytes)\nasMap = decode\nasOption : Data -> option (option int)\nasOption = decode\nasBigOption : Data -> option (Option Int)\nasBigOption = decode\nasData : Data -> option Data\nasData = decode\nunchecked : Data -> Int\nunchecked = fromData\nchecked : Data -> Int\nchecked = validate\nlittle : Data -> int\nlittle = fromData\ntext : Data -> string\ntext = fromData\ncountValue : count\ncountValue = fromData (I 42)\ncountDecoded : option count\ncountDecoded = decode (I 42)\n\nentries = [Builtin.mkPairData (I 1) (B #\"aa\"), Builtin.mkPairData (I 1) (B #\"bb\")]\n\nnumber : int\nnumber = 42\nrawBytes : bytes\nrawBytes = #\"aaff\"\nyes : bool\nyes = True\nno : bool\nno = False\nnumbers : list int\nnumbers = [1, 2]\nbigOne : Int\nbigOne = 1\nfirstBytes : Bytes\nfirstBytes = #\"aa\"\nsecondBytes : Bytes\nsecondBytes = #\"bb\"\nthree : int\nthree = 3\n\nfromList : Data -> list int\nfromList = fromData\nfromOption : Data -> option int\nfromOption = fromData\nfromBool : Data -> bool\nfromBool = fromData\nfromUnit : Data -> unit\nfromUnit = fromData\nfromBytes : Data -> bytes\nfromBytes = fromData\nbigBool : Data -> option Bool\nbigBool = decode\n\ntests\n test \"little list unpacking\" = do\n assert (fromList (toData numbers) == numbers)\n test \"little option unpacking\" = do\n assert (fromOption (toData (Some number)) == Some number)\n test \"little bool unpacking\" = do\n assert (fromBool (toData yes) == yes)\n test \"little unit unpacking\" = do\n assert (fromUnit (toData ()) == ())\n test \"little bytes unpacking\" = do\n assert (fromBytes (toData rawBytes) == rawBytes)\n test \"Big Bool optional decoding\" = do\n assert (bigBool (toData yes) == Some Bool.True)\n test \"Big Bool optional rejection\" = do\n assert (bigBool (Builtin.constrData 0 [I 1]) == None)\n test \"little string unpacking rejects bad UTF8\" fail = do\n let _ = text (B #\"80\") in ()\n test \"little scalar conversions 1\" = do\n assert (toData number == I 42)\n test \"little scalar conversions 2\" = do\n assert (little (I 42) == 42)\n test \"little scalar conversions 3\" = do\n assert (asBytes (toData rawBytes) == Some #\"aaff\")\n test \"little scalar conversions 4\" = do\n assert (asString (toData \"é\") == Some \"é\")\n test \"little scalar conversions 5\" = do\n assert (text (toData \"é\") == \"é\")\n test \"little scalar conversions 6\" = do\n assert (asBool (toData yes) == Some True)\n test \"little scalar conversions 7\" = do\n assert (asBool (toData no) == Some False)\n test \"little scalar conversions 8\" = do\n assert (asUnit (toData ()) == Some ())\n test \"user little conversions and ordinary option composition 1\" = do\n assert (toData countValue == I 42)\n test \"user little conversions and ordinary option composition 2\" = do\n assert (case countDecoded of\n Some (Count n) -> n == 42\n None -> False)\n test \"Big blanket conversion remains unchecked 1\" = do\n assert (toData (unchecked (B #\"aa\")) == B #\"aa\")\n test \"Big blanket conversion remains unchecked 2\" = do\n assert (toData (checked (I 42)) == I 42)\n test \"independent validation fails\" fail = do\n let _ = checked (B #\"aa\") in ()\n test \"little unpacking fails\" fail = do\n let _ = little (B #\"aa\") in ()\n test \"scalar decode wrong shapes 1\" = do\n assert (asInt (B #\"aa\") == None)\n test \"scalar decode wrong shapes 2\" = do\n assert (asBytes (I 1) == None)\n test \"scalar decode wrong shapes 3\" = do\n assert (asString (I 1) == None)\n test \"scalar decode wrong shapes 4\" = do\n assert (asInt (List []) == None)\n test \"scalar decode wrong shapes 5\" = do\n assert (asInt (Map []) == None)\n test \"scalar decode wrong shapes 6\" = do\n assert (asInt (Builtin.constrData 0 []) == None)\n test \"boolean and unit exact shape 1\" = do\n assert (asBool (Builtin.constrData 2 []) == None)\n test \"boolean and unit exact shape 2\" = do\n assert (asBool (Builtin.constrData 0 [I 1]) == None)\n test \"boolean and unit exact shape 3\" = do\n assert (asBool (I 0) == None)\n test \"boolean and unit exact shape 4\" = do\n assert (asUnit (Builtin.constrData 1 []) == None)\n test \"boolean and unit exact shape 5\" = do\n assert (asUnit (Builtin.constrData 0 [I 1]) == None)\n test \"UTF8 accepts boundaries 1\" = do\n assert (asString (B #\"00c280e0a080eda080\") == None)\n test \"UTF8 accepts boundaries 2\" = do\n assert (asString (B #\"00c280e0a080ed9fbff0908080f48fbfbf\") /= None)\n test \"UTF8 rejects malformed sequences 1\" = do\n assert (asString (B #\"80\") == None)\n test \"UTF8 rejects malformed sequences 2\" = do\n assert (asString (B #\"c080\") == None)\n test \"UTF8 rejects malformed sequences 3\" = do\n assert (asString (B #\"c2\") == None)\n test \"UTF8 rejects malformed sequences 4\" = do\n assert (asString (B #\"e08080\") == None)\n test \"UTF8 rejects malformed sequences 5\" = do\n assert (asString (B #\"eda080\") == None)\n test \"UTF8 rejects malformed sequences 6\" = do\n assert (asString (B #\"f0808080\") == None)\n test \"UTF8 rejects malformed sequences 7\" = do\n assert (asString (B #\"f4908080\") == None)\n test \"UTF8 rejects malformed sequences 8\" = do\n assert (asString (B #\"f5808080\") == None)\n test \"little collection conversion 1\" = do\n assert (asList (toData numbers) == Some [1, 2])\n test \"little collection conversion 2\" = do\n assert (asList (List []) == Some [])\n test \"little collection conversion 3\" = do\n assert (asList (List [I 1, B #\"aa\"]) == None)\n test \"little collection conversion 4\" = do\n assert (asList (I 1) == None)\n test \"Big collection decode validates children 1\" = do\n assert (asBigList (List [I 1, I 2]) == Some (lift [1, 2]))\n test \"Big collection decode validates children 2\" = do\n assert (asBigList (List [B #\"aa\"]) == None)\n test \"Big collection decode validates children 3\" = do\n assert (asMap (Map entries) == Some (lift [Builtin.mkPairData bigOne firstBytes, Builtin.mkPairData bigOne secondBytes]))\n test \"Big collection decode validates children 4\" = do\n assert (asMap (Map [Builtin.mkPairData (B #\"aa\") (B #\"bb\")]) == None)\n test \"Big collection decode validates children 5\" = do\n assert (asMap (Map [Builtin.mkPairData (I 1) (I 2)]) == None)\n test \"optional values exact shape 1\" = do\n assert (asOption (toData (Some three)) == Some (Some 3))\n test \"optional values exact shape 2\" = do\n assert (asOption (Builtin.constrData 1 []) == Some None)\n test \"optional values exact shape 3\" = do\n assert (asOption (Builtin.constrData 0 []) == None)\n test \"optional values exact shape 4\" = do\n assert (asOption (Builtin.constrData 0 [B #\"aa\"]) == None)\n test \"optional values exact shape 5\" = do\n assert (asOption (Builtin.constrData 1 [I 1]) == None)\n test \"optional values exact shape 6\" = do\n assert (asBigOption (Builtin.constrData 0 [I 3]) == Some (Option.Some 3))\n test \"optional values exact shape 7\" = do\n assert (asBigOption (Builtin.constrData 0 [B #\"aa\"]) == None)\n test \"Data decode preserves any shape 1\" = do\n assert (asData (Map entries) == Some (Map entries))\n" +--- +little list unpacking: Pass; traces: [] +little option unpacking: Pass; traces: [] +little bool unpacking: Pass; traces: [] +little unit unpacking: Pass; traces: [] +little bytes unpacking: Pass; traces: [] +Big Bool optional decoding: Pass; traces: [] +Big Bool optional rejection: Pass; traces: [] +little string unpacking rejects bad UTF8: Pass; traces: [] +little scalar conversions 1: Pass; traces: [] +little scalar conversions 2: Pass; traces: [] +little scalar conversions 3: Pass; traces: [] +little scalar conversions 4: Pass; traces: [] +little scalar conversions 5: Pass; traces: [] +little scalar conversions 6: Pass; traces: [] +little scalar conversions 7: Pass; traces: [] +little scalar conversions 8: Pass; traces: [] +user little conversions and ordinary option composition 1: Pass; traces: [] +user little conversions and ordinary option composition 2: Pass; traces: [] +Big blanket conversion remains unchecked 1: Pass; traces: [] +Big blanket conversion remains unchecked 2: Pass; traces: [] +independent validation fails: Pass; traces: [] +little unpacking fails: Pass; traces: [] +scalar decode wrong shapes 1: Pass; traces: [] +scalar decode wrong shapes 2: Pass; traces: [] +scalar decode wrong shapes 3: Pass; traces: [] +scalar decode wrong shapes 4: Pass; traces: [] +scalar decode wrong shapes 5: Pass; traces: [] +scalar decode wrong shapes 6: Pass; traces: [] +boolean and unit exact shape 1: Pass; traces: [] +boolean and unit exact shape 2: Pass; traces: [] +boolean and unit exact shape 3: Pass; traces: [] +boolean and unit exact shape 4: Pass; traces: [] +boolean and unit exact shape 5: Pass; traces: [] +UTF8 accepts boundaries 1: Pass; traces: [] +UTF8 accepts boundaries 2: Pass; traces: [] +UTF8 rejects malformed sequences 1: Pass; traces: [] +UTF8 rejects malformed sequences 2: Pass; traces: [] +UTF8 rejects malformed sequences 3: Pass; traces: [] +UTF8 rejects malformed sequences 4: Pass; traces: [] +UTF8 rejects malformed sequences 5: Pass; traces: [] +UTF8 rejects malformed sequences 6: Pass; traces: [] +UTF8 rejects malformed sequences 7: Pass; traces: [] +UTF8 rejects malformed sequences 8: Pass; traces: [] +little collection conversion 1: Pass; traces: [] +little collection conversion 2: Pass; traces: [] +little collection conversion 3: Pass; traces: [] +little collection conversion 4: Pass; traces: [] +Big collection decode validates children 1: Pass; traces: [] +Big collection decode validates children 2: Pass; traces: [] +Big collection decode validates children 3: Pass; traces: [] +Big collection decode validates children 4: Pass; traces: [] +Big collection decode validates children 5: Pass; traces: [] +optional values exact shape 1: Pass; traces: [] +optional values exact shape 2: Pass; traces: [] +optional values exact shape 3: Pass; traces: [] +optional values exact shape 4: Pass; traces: [] +optional values exact shape 5: Pass; traces: [] +optional values exact shape 6: Pass; traces: [] +optional values exact shape 7: Pass; traces: [] +Data decode preserves any shape 1: Pass; traces: [] diff --git a/crates/nash-driver/tests/snapshots/base_traits__data_decoders.snap b/crates/nash-driver/tests/snapshots/base_traits__data_decoders.snap deleted file mode 100644 index 2c9855dd..00000000 --- a/crates/nash-driver/tests/snapshots/base_traits__data_decoders.snap +++ /dev/null @@ -1,96 +0,0 @@ ---- -source: crates/nash-driver/tests/base_traits.rs -description: "module Main exposing (..)\n\nimport Data.Decode as Decode\nimport Data.Encode as Encode\nimport Cons exposing (type cons(..))\n\nrecord : Decode.decoder (int, bytes)\nrecord = Decode.constr 7 <| do\n first <- Decode.field 0 Decode.int\n second <- Decode.field 1 Decode.bytes\n pure (first, second)\n\napplied : Decode.decoder int\napplied = pure (\\a b -> a + b) <*> Decode.field 0 Decode.int <*> Decode.field 1 Decode.int\n\nconstant : Decode.decoder int\nconstant = pure 42\n\nemptyAlternatives : Decode.decoder int\nemptyAlternatives = Decode.oneOf Nil\n\nbad : Decode.decoder int\nbad _ = fail\n\nfunction : Decode.decoder (int -> int)\nfunction = map (\\n -> \\x -> n + x) Decode.int\n\nemptyPairs : cons (int, int)\nemptyPairs = Nil\n\nencoded = Encode.constr 7 [Encode.int 42, Encode.bytes #\"ff\"]\n\nbigInt : Int\nbigInt = 42\nbigBytes : Bytes\nbigBytes = lift #\"ff\"\nbigBool : Bool\nbigBool = True\nbigList : List Int\nbigList = lift [bigInt]\nbigFields : List Data\nbigFields = lift [Encode.int bigInt]\nrejected : Decode.decoder int\nrejected = Decode.reject\nrejectBind : Decode.decoder int\nrejectBind = do\n _ <- rejected\n bad\nrejectApply : Decode.decoder int\nrejectApply = pure (\\a b -> a + b) <*> rejected <*> bad\n\ntests\n test \"implicit qualified decoder modules\" = do\n assert (Data.Decode.run Data.Decode.int (Data.Encode.int 5) == Some 5)\n test \"bind rejection skips continuation\" = do\n assert (Decode.run rejectBind encoded == None)\n test \"applicative rejection skips argument\" = do\n assert (Decode.run rejectApply encoded == None)\n test \"empty map decoding\" = do\n assert (Decode.run (Decode.map Decode.int Decode.int) (Encode.map Encode.int Encode.int emptyPairs) == Some Nil)\n test \"map key failure\" = do\n assert (Decode.run (Decode.map Decode.bytes Decode.int) (Encode.map Encode.int Encode.int (Cons (1, 2) Nil)) == None)\n test \"extra constructor fields accepted\" = do\n assert (Decode.run record (Encode.constr 7 [Encode.int 42, Encode.bytes #\"ff\", Encode.int 9]) == Some (42, #\"ff\"))\n test \"Big int encoding\" = do\n assert (Decode.run Decode.int (Encode.int bigInt) == Some 42)\n test \"Big bytes encoding\" = do\n assert (Decode.run Decode.bytes (Encode.bytes bigBytes) == Some #\"ff\")\n test \"Big bool encoding\" = do\n assert (Decode.run Decode.bool (Encode.bool bigBool) == Some True)\n test \"Big list encoding\" = do\n assert (Decode.run (Decode.list Decode.int) (Encode.list Encode.int bigList) == Some [42])\n test \"Big constructor inputs\" = do\n assert (Data.tag (Encode.constr bigInt bigFields) == 42)\n test \"primitive decoding and wrong shapes 1\" = do\n assert (Decode.run Decode.int (Encode.int -42) == Some -42)\n test \"primitive decoding and wrong shapes 2\" = do\n assert (Decode.run Decode.bytes (Encode.bytes #\"00ff\") == Some #\"00ff\")\n test \"primitive decoding and wrong shapes 3\" = do\n assert (Decode.run Decode.data encoded == Some encoded)\n test \"primitive decoding and wrong shapes 4\" = do\n assert (Decode.run Decode.int (Encode.bytes #\"\") == None)\n test \"primitive decoding and wrong shapes 5\" = do\n assert (Decode.run Decode.bytes (Encode.int 1) == None)\n test \"primitive decoding and wrong shapes 6\" = do\n assert (Decode.run Decode.int (Encode.list Encode.int [1]) == None)\n test \"primitive decoding and wrong shapes 7\" = do\n assert (Decode.run Decode.int (Encode.map Encode.int Encode.int emptyPairs) == None)\n test \"primitive decoding and wrong shapes 8\" = do\n assert (Decode.run Decode.int encoded == None)\n test \"primitive decoding and wrong shapes 9\" = do\n assert (Decode.run Decode.string (Encode.int 1) == None)\n test \"booleans require exact constructor shape 1\" = do\n assert (Decode.run Decode.bool (Encode.bool True) == Some True)\n test \"booleans require exact constructor shape 2\" = do\n assert (Decode.run Decode.bool (Encode.bool False) == Some False)\n test \"booleans require exact constructor shape 3\" = do\n assert (Decode.run Decode.bool (Encode.constr 2 []) == None)\n test \"booleans require exact constructor shape 4\" = do\n assert (Decode.run Decode.bool (Encode.constr 0 [Encode.int 1]) == None)\n test \"booleans require exact constructor shape 5\" = do\n assert (Decode.run Decode.bool (Encode.constr 1 [Encode.int 1]) == None)\n test \"booleans require exact constructor shape 6\" = do\n assert (Decode.run Decode.bool (Encode.int 1) == None)\n test \"UTF8 accepts empty\" = do\n assert (Option.isSome (Decode.run Decode.string (Encode.bytes #\"\")))\n test \"UTF8 accepts 00\" = do\n assert (Option.isSome (Decode.run Decode.string (Encode.bytes #\"00\")))\n test \"UTF8 accepts 7f\" = do\n assert (Option.isSome (Decode.run Decode.string (Encode.bytes #\"7f\")))\n test \"UTF8 accepts c280\" = do\n assert (Option.isSome (Decode.run Decode.string (Encode.bytes #\"c280\")))\n test \"UTF8 accepts dfbf\" = do\n assert (Option.isSome (Decode.run Decode.string (Encode.bytes #\"dfbf\")))\n test \"UTF8 accepts e0a080\" = do\n assert (Option.isSome (Decode.run Decode.string (Encode.bytes #\"e0a080\")))\n test \"UTF8 accepts ed9fbf\" = do\n assert (Option.isSome (Decode.run Decode.string (Encode.bytes #\"ed9fbf\")))\n test \"UTF8 accepts ee8080\" = do\n assert (Option.isSome (Decode.run Decode.string (Encode.bytes #\"ee8080\")))\n test \"UTF8 accepts efbfbf\" = do\n assert (Option.isSome (Decode.run Decode.string (Encode.bytes #\"efbfbf\")))\n test \"UTF8 accepts f0908080\" = do\n assert (Option.isSome (Decode.run Decode.string (Encode.bytes #\"f0908080\")))\n test \"UTF8 accepts f48fbfbf\" = do\n assert (Option.isSome (Decode.run Decode.string (Encode.bytes #\"f48fbfbf\")))\n test \"UTF8 accepts 61c28062\" = do\n assert (Option.isSome (Decode.run Decode.string (Encode.bytes #\"61c28062\")))\n test \"UTF8 accepts e18080\" = do\n assert (Option.isSome (Decode.run Decode.string (Encode.bytes #\"e18080\")))\n test \"UTF8 accepts ecbfbf\" = do\n assert (Option.isSome (Decode.run Decode.string (Encode.bytes #\"ecbfbf\")))\n test \"UTF8 accepts f1808080\" = do\n assert (Option.isSome (Decode.run Decode.string (Encode.bytes #\"f1808080\")))\n test \"UTF8 accepts f3bfbfbf\" = do\n assert (Option.isSome (Decode.run Decode.string (Encode.bytes #\"f3bfbfbf\")))\n test \"UTF8 rejects 80\" = do\n assert (Decode.run Decode.string (Encode.bytes #\"80\") == None)\n test \"UTF8 rejects bf\" = do\n assert (Decode.run Decode.string (Encode.bytes #\"bf\") == None)\n test \"UTF8 rejects c080\" = do\n assert (Decode.run Decode.string (Encode.bytes #\"c080\") == None)\n test \"UTF8 rejects c1bf\" = do\n assert (Decode.run Decode.string (Encode.bytes #\"c1bf\") == None)\n test \"UTF8 rejects e09fbf\" = do\n assert (Decode.run Decode.string (Encode.bytes #\"e09fbf\") == None)\n test \"UTF8 rejects eda080\" = do\n assert (Decode.run Decode.string (Encode.bytes #\"eda080\") == None)\n test \"UTF8 rejects f08fbfbf\" = do\n assert (Decode.run Decode.string (Encode.bytes #\"f08fbfbf\") == None)\n test \"UTF8 rejects f4908080\" = do\n assert (Decode.run Decode.string (Encode.bytes #\"f4908080\") == None)\n test \"UTF8 rejects f5808080\" = do\n assert (Decode.run Decode.string (Encode.bytes #\"f5808080\") == None)\n test \"UTF8 rejects ff\" = do\n assert (Decode.run Decode.string (Encode.bytes #\"ff\") == None)\n test \"UTF8 rejects c2\" = do\n assert (Decode.run Decode.string (Encode.bytes #\"c2\") == None)\n test \"UTF8 rejects e0\" = do\n assert (Decode.run Decode.string (Encode.bytes #\"e0\") == None)\n test \"UTF8 rejects e0a0\" = do\n assert (Decode.run Decode.string (Encode.bytes #\"e0a0\") == None)\n test \"UTF8 rejects f0\" = do\n assert (Decode.run Decode.string (Encode.bytes #\"f0\") == None)\n test \"UTF8 rejects f090\" = do\n assert (Decode.run Decode.string (Encode.bytes #\"f090\") == None)\n test \"UTF8 rejects f09080\" = do\n assert (Decode.run Decode.string (Encode.bytes #\"f09080\") == None)\n test \"UTF8 rejects c27f\" = do\n assert (Decode.run Decode.string (Encode.bytes #\"c27f\") == None)\n test \"UTF8 rejects c2c0\" = do\n assert (Decode.run Decode.string (Encode.bytes #\"c2c0\") == None)\n test \"UTF8 rejects e0a07f\" = do\n assert (Decode.run Decode.string (Encode.bytes #\"e0a07f\") == None)\n test \"UTF8 rejects f090807f\" = do\n assert (Decode.run Decode.string (Encode.bytes #\"f090807f\") == None)\n test \"UTF8 rejects 61ff\" = do\n assert (Decode.run Decode.string (Encode.bytes #\"61ff\") == None)\n test \"UTF8 rejects e0a0c0\" = do\n assert (Decode.run Decode.string (Encode.bytes #\"e0a0c0\") == None)\n test \"UTF8 rejects f09080c0\" = do\n assert (Decode.run Decode.string (Encode.bytes #\"f09080c0\") == None)\n test \"UTF8 text roundtrip\" = do\n assert (Decode.run Decode.string (Encode.string \"hello λ 😀\") == Some \"hello λ 😀\")\n test \"constructor decoder do\" = do\n assert (Decode.run record encoded == Some (42, #\"ff\"))\n test \"constructor wrong tag\" = do\n assert (Decode.run record (Encode.constr 8 []) == None)\n test \"constructor wrong shape\" = do\n assert (Decode.run record (Encode.int 7) == None)\n test \"negative field index\" = do\n assert (Decode.run (Decode.field -1 Decode.int) encoded == None)\n test \"missing field\" = do\n assert (Decode.run (Decode.field 2 Decode.int) encoded == None)\n test \"field on nonconstructor\" = do\n assert (Decode.run (Decode.field 0 Decode.int) (Encode.int 0) == None)\n test \"direct tag\" = do\n assert (Data.tag encoded == 7)\n test \"direct fields\" = do\n assert (Data.fields encoded == [Encode.int 42, Encode.bytes #\"ff\"])\n test \"applicative and bind share original input 1\" = do\n assert (Decode.run applied (Encode.constr 0 [Encode.int 2, Encode.int 3]) == Some 5)\n test \"applicative and bind share original input 2\" = do\n assert (Decode.run constant encoded == Some 42)\n test \"applicative and bind share original input 3\" = do\n assert (Decode.run (Decode.oneOf (Cons Decode.reject (Cons Decode.int (Cons bad Nil)))) (Encode.int 5) == Some 5)\n test \"applicative and bind share original input 4\" = do\n assert (Decode.run emptyAlternatives encoded == None)\n test \"applicative and bind share original input 5\" = do\n assert (Decode.run (Decode.oneOf (Cons Decode.int Nil)) encoded == None)\n test \"list decoding\" = do\n assert (Decode.run (Decode.list Decode.int) (Encode.list Encode.int [1, 2, 3]) == Some [1, 2, 3])\n test \"empty list decoding\" = do\n assert (Decode.run (Decode.list Decode.int) (Encode.list identity []) == Some [])\n test \"list item failure\" = do\n assert (Decode.run (Decode.list Decode.int) (Encode.list identity [Encode.int 1, Encode.bytes #\"\", Encode.int 3]) == None)\n test \"list wrong shape\" = do\n assert (Decode.run (Decode.list Decode.int) (Encode.int 1) == None)\n test \"map order and duplicates\" = do\n assert (Decode.run (Decode.map Decode.int Decode.bytes) (Encode.map Encode.int Encode.bytes (Cons (1, #\"aa\") (Cons (1, #\"bb\") Nil))) == Some (Cons (1, #\"aa\") (Cons (1, #\"bb\") Nil)))\n test \"map value failure\" = do\n assert (Decode.run (Decode.map Decode.int Decode.bytes) (Encode.map Encode.int Encode.int (Cons (1, 2) Nil)) == None)\n test \"map wrong shape\" = do\n assert (Decode.run (Decode.map Decode.int Decode.bytes) (Encode.int 1) == None)\n test \"decoded maps can contain functions\" = do\n case Decode.run (Decode.map Decode.int function) (Encode.map Encode.int Encode.int (Cons (1, 7) Nil)) of\n Some (Cons (key, f) Nil) -> assert (key == 1 && f 3 == 10)\n _ -> fail\n test \"tag expects constructor\" fail = do\n let _ = Data.tag (Encode.int 1) in ()\n test \"fields expects constructor\" fail = do\n let _ = Data.fields (Encode.bytes #\"\") in ()\n prop \"integer roundtrip\" =\n let value via Prop.int in\n do\n assert (Decode.run Decode.int (Encode.int value) == Some value)\n prop \"bytes roundtrip\" =\n let value via Prop.bytes in\n do\n assert (Decode.run Decode.bytes (Encode.bytes value) == Some value)\n prop \"integer list roundtrip\" =\n let values via Prop.listBetween 0 10 Prop.int in\n do\n assert (Decode.run (Decode.list Decode.int) (Encode.list Encode.int values) == Some values)\n" ---- -implicit qualified decoder modules: Pass; traces: [] -bind rejection skips continuation: Pass; traces: [] -applicative rejection skips argument: Pass; traces: [] -empty map decoding: Pass; traces: [] -map key failure: Pass; traces: [] -extra constructor fields accepted: Pass; traces: [] -Big int encoding: Pass; traces: [] -Big bytes encoding: Pass; traces: [] -Big bool encoding: Pass; traces: [] -Big list encoding: Pass; traces: [] -Big constructor inputs: Pass; traces: [] -primitive decoding and wrong shapes 1: Pass; traces: [] -primitive decoding and wrong shapes 2: Pass; traces: [] -primitive decoding and wrong shapes 3: Pass; traces: [] -primitive decoding and wrong shapes 4: Pass; traces: [] -primitive decoding and wrong shapes 5: Pass; traces: [] -primitive decoding and wrong shapes 6: Pass; traces: [] -primitive decoding and wrong shapes 7: Pass; traces: [] -primitive decoding and wrong shapes 8: Pass; traces: [] -primitive decoding and wrong shapes 9: Pass; traces: [] -booleans require exact constructor shape 1: Pass; traces: [] -booleans require exact constructor shape 2: Pass; traces: [] -booleans require exact constructor shape 3: Pass; traces: [] -booleans require exact constructor shape 4: Pass; traces: [] -booleans require exact constructor shape 5: Pass; traces: [] -booleans require exact constructor shape 6: Pass; traces: [] -UTF8 accepts empty: Pass; traces: [] -UTF8 accepts 00: Pass; traces: [] -UTF8 accepts 7f: Pass; traces: [] -UTF8 accepts c280: Pass; traces: [] -UTF8 accepts dfbf: Pass; traces: [] -UTF8 accepts e0a080: Pass; traces: [] -UTF8 accepts ed9fbf: Pass; traces: [] -UTF8 accepts ee8080: Pass; traces: [] -UTF8 accepts efbfbf: Pass; traces: [] -UTF8 accepts f0908080: Pass; traces: [] -UTF8 accepts f48fbfbf: Pass; traces: [] -UTF8 accepts 61c28062: Pass; traces: [] -UTF8 accepts e18080: Pass; traces: [] -UTF8 accepts ecbfbf: Pass; traces: [] -UTF8 accepts f1808080: Pass; traces: [] -UTF8 accepts f3bfbfbf: Pass; traces: [] -UTF8 rejects 80: Pass; traces: [] -UTF8 rejects bf: Pass; traces: [] -UTF8 rejects c080: Pass; traces: [] -UTF8 rejects c1bf: Pass; traces: [] -UTF8 rejects e09fbf: Pass; traces: [] -UTF8 rejects eda080: Pass; traces: [] -UTF8 rejects f08fbfbf: Pass; traces: [] -UTF8 rejects f4908080: Pass; traces: [] -UTF8 rejects f5808080: Pass; traces: [] -UTF8 rejects ff: Pass; traces: [] -UTF8 rejects c2: Pass; traces: [] -UTF8 rejects e0: Pass; traces: [] -UTF8 rejects e0a0: Pass; traces: [] -UTF8 rejects f0: Pass; traces: [] -UTF8 rejects f090: Pass; traces: [] -UTF8 rejects f09080: Pass; traces: [] -UTF8 rejects c27f: Pass; traces: [] -UTF8 rejects c2c0: Pass; traces: [] -UTF8 rejects e0a07f: Pass; traces: [] -UTF8 rejects f090807f: Pass; traces: [] -UTF8 rejects 61ff: Pass; traces: [] -UTF8 rejects e0a0c0: Pass; traces: [] -UTF8 rejects f09080c0: Pass; traces: [] -UTF8 text roundtrip: Pass; traces: [] -constructor decoder do: Pass; traces: [] -constructor wrong tag: Pass; traces: [] -constructor wrong shape: Pass; traces: [] -negative field index: Pass; traces: [] -missing field: Pass; traces: [] -field on nonconstructor: Pass; traces: [] -direct tag: Pass; traces: [] -direct fields: Pass; traces: [] -applicative and bind share original input 1: Pass; traces: [] -applicative and bind share original input 2: Pass; traces: [] -applicative and bind share original input 3: Pass; traces: [] -applicative and bind share original input 4: Pass; traces: [] -applicative and bind share original input 5: Pass; traces: [] -list decoding: Pass; traces: [] -empty list decoding: Pass; traces: [] -list item failure: Pass; traces: [] -list wrong shape: Pass; traces: [] -map order and duplicates: Pass; traces: [] -map value failure: Pass; traces: [] -map wrong shape: Pass; traces: [] -decoded maps can contain functions: Pass; traces: [] -tag expects constructor: Pass; traces: [] -fields expects constructor: Pass; traces: [] -integer roundtrip: Pass; traces: [] -bytes roundtrip: Pass; traces: [] -integer list roundtrip: Pass; traces: [] diff --git a/crates/nash-driver/tests/snapshots/bundled_base__decode_list_rejects_function_elements.snap b/crates/nash-driver/tests/snapshots/bundled_base__decode_list_rejects_function_elements.snap new file mode 100644 index 00000000..8f66bddf --- /dev/null +++ b/crates/nash-driver/tests/snapshots/bundled_base__decode_list_rejects_function_elements.snap @@ -0,0 +1,16 @@ +--- +source: crates/nash-driver/tests/bundled_base.rs +description: "module Main exposing (..)\nbad : Data -> option (list (int -> int))\nbad = decode\n" +info: diagnostic +--- +nash::names::representation_mismatch + + × This position requires `Storable` in the annotation for `bad`: + ╭─[/app/src/Main.nash:2:29] + 1 │ module Main exposing (..) + 2 │ bad : Data -> option (list (int -> int)) + · ────────── + 3 │ bad = decode + ╰──── + help: The type has `Term` representation, but `Storable` admits Big or Const types. + Change the type or the representation requirement. diff --git a/crates/nash-driver/tests/snapshots/bundled_base__decoder_list_rejects_function_elements.snap b/crates/nash-driver/tests/snapshots/bundled_base__decoder_list_rejects_function_elements.snap deleted file mode 100644 index 4419b196..00000000 --- a/crates/nash-driver/tests/snapshots/bundled_base__decoder_list_rejects_function_elements.snap +++ /dev/null @@ -1,30 +0,0 @@ ---- -source: crates/nash-driver/tests/bundled_base.rs -description: "module Main exposing (..)\nfunctions : Data.Decode.decoder (int -> int)\nfunctions = pure (\\x -> x)\nbad = Data.Decode.list functions\n" -info: diagnostic ---- -nash::type::missing_impl - - × Unsatisfied representation constraint `Storable (int -> int)`. - ╭─[/app/src/Main.nash:4:7] - 3 │ functions = pure (\x -> x) - 4 │ bad = Data.Decode.list functions - · ────────┬─────── - · ╰── required by `type application` - ╰──── - help: Requirement chain: - - forming list (int -> int) - - Use a type with Big or Const representation; an impl cannot change representation. - -nash::type::missing_impl - - × Unsatisfied representation constraint `Storable (int -> int)`. - ╭─[/app/src/Main.nash:4:7] - 3 │ functions = pure (\x -> x) - 4 │ bad = Data.Decode.list functions - · ────────┬─────── - · ╰── required by `list` - ╰──── - help: Use a type with Big or Const representation; an impl cannot change representation. diff --git a/crates/nash-driver/tests/snapshots/cardano__ledger_contexts.snap b/crates/nash-driver/tests/snapshots/cardano__ledger_contexts.snap index b5f1e077..db6a545a 100644 --- a/crates/nash-driver/tests/snapshots/cardano__ledger_contexts.snap +++ b/crates/nash-driver/tests/snapshots/cardano__ledger_contexts.snap @@ -1,6 +1,6 @@ --- source: crates/nash-driver/tests/cardano.rs -description: "validator module Context exposing (main)\n\nimport Cardano.Tx exposing (..)\nimport Cardano.Address exposing (..)\nimport Cardano.Time as Time\nimport Cardano.Value as Value exposing (Value)\n\ninteger : int -> Int\ninteger = lift\n\nbytes : bytes -> Bytes\nbytes = lift\n\nkey = PubKeyCredential (lift #\"6b6579\")\nscript = ScriptCredential (lift #\"736372697074\")\nref = OutputReference (lift #\"7478\") 2\nrep = DRep key\n\ncertificates : List Certificate\ncertificates = lift\n [ RegStaking key Option.None\n , UnRegStaking script (Option.Some 12)\n , DelegStaking key (DelegStake (lift #\"706f6f6c\"))\n , RegDeleg key (DelegVote DRepAlwaysAbstain) 13\n , RegDRep key 14\n , UpdateDRep script\n , UnRegDRep key 15\n , PoolRegister (lift #\"706f6f6c\") (lift #\"767266\")\n , PoolRetire (lift #\"706f6f6c\") 16\n , AuthHotCommittee key script\n , ResignColdCommittee script\n , DelegStaking key (DelegStakeVote (lift #\"706f6f6c\") rep)\n , DelegStaking key (DelegVote DRepAlwaysNoConfidence)\n ]\nactionId = GovernanceActionId (lift #\"70726576696f7573\") 3\n\nactions : list GovernanceAction\nactions =\n [ ParameterChange Option.None (toData (Builtin.mapData (Map.singleton (integer 0) (integer 42)))) (Option.Some (lift #\"6775617264\"))\n , HardForkInitiation (Option.Some actionId) (ProtocolVersion 11 0)\n , TreasuryWithdrawals (lift (Map.singleton key (integer 17))) Option.None\n , NoConfidence (Option.Some actionId)\n , UpdateCommittee Option.None (lift [key]) (lift (Map.singleton script (integer 18))) (Rational 2 3)\n , NewConstitution (Option.Some actionId) (Constitution Option.None)\n , InfoAction\n , NewConstitution Option.None (Constitution (Option.Some (lift #\"636f6e737469747574696f6e\")))\n ]\nproposals : List Proposal\nproposals = lift (List.map (Proposal 19 key) actions)\nproposal = Proposal 19 key InfoAction\nvoters = [CommitteeVoter key, DRepVoter script, StakePoolVoter (lift #\"706f6f6c\")]\npurposes = [Minting (lift #\"706f6c696379\"), Spending ref, Rewarding key, Certifying 0 (RegStaking key Option.None), Voting (CommitteeVoter key), Proposing 6 proposal]\ninfos = [MintingScript (lift #\"706f6c696379\"), SpendingScript ref (Option.Some (toData (integer 99))), RewardingScript key, CertifyingScript 0 (RegStaking key Option.None), VotingScript (CommitteeVoter key), ProposingScript 6 proposal]\ncoin : Value\ncoin = lift (Value.singleton #\"\" #\"\" 100)\nmint : Value\nmint = lift (Value.singleton #\"706f6c696379\" #\"6173736574\" -2)\naddresses = [Address key Option.None, Address script (Option.Some (StakingHash key)), Address key (Option.Some (StakingPtr 20 21 22))]\ndatums = [NoDatum, DatumHash (lift #\"646174756d\"), InlineDatum (toData (integer 23))]\noutputs = List.map2 (\\address datum -> Output address coin datum (Option.Some (lift #\"7265662d736372697074\"))) addresses datums\nfirstOutput = Output (Address key Option.None) coin NoDatum (Option.Some (lift #\"7265662d736372697074\"))\nlastOutput = Output (Address key (Option.Some (StakingPtr 20 21 22))) coin (InlineDatum (toData (integer 23))) (Option.Some (lift #\"7265662d736372697074\"))\n\ntx = Tx\n (lift [Input ref firstOutput])\n (lift [Input (OutputReference (lift #\"7265666572656e6365\") 1) lastOutput])\n (lift outputs)\n 2\n mint\n certificates\n (lift (Map.singleton key (integer 24)))\n (Time.Interval (Time.IntervalBound (Time.Finite 10) Bool.False) (Time.IntervalBound (Time.Finite 30) Bool.True))\n (lift [lift #\"6b6579\"])\n (lift (List.map (\\purpose -> Builtin.mkPairData purpose (toData (integer 25))) purposes))\n (lift (Map.singleton (bytes #\"646174756d\") (toData (integer 23))))\n (lift #\"7472616e73616374696f6e\")\n (lift (List.map2 (\\voter vote -> Builtin.mkPairData voter (Builtin.mapData (Map.singleton actionId vote))) voters [VoteNo, VoteYes, Abstain]))\n proposals\n (Option.Some 26)\n Option.None\n\nmain : Data -> unit\nmain packet =\n case packet of\n Constr pair(0, [raw, B encoded]) -> check raw encoded\n _ -> fail\n\ncheck : Data -> bytes -> unit\ncheck raw encoded =\n let\n context : ScriptContext\n context = validate raw\n index = Data.tag (toData context.scriptInfo)\n expected =\n case context.scriptInfo of\n SpendingScript _ Option.None ->\n ScriptContext\n { tx | currentTreasury = Option.None, treasuryDonation = (Option.Some 28), outputs = (lift [Output (Address key Option.None) coin NoDatum Option.None]) }\n (toData (integer 27))\n (SpendingScript ref Option.None)\n _ -> ScriptContext tx (toData (integer 27)) (List.at index infos |> Option.unwrap)\n _ = assert (context == expected)\n _ = assert (Data.serialise (toData expected) == encoded)\n _ = assert (Value.lovelace (List.at 0 context.tx.outputs |> Option.unwrap).value == 100)\n _ = assert (Value.quantityOf #\"706f6c696379\" #\"6173736574\" context.tx.mint == -2)\n _ = assert (Time.member 11 context.tx.validityRange)\n _ = assert (Bool.not (Time.member 10 context.tx.validityRange))\n in\n ()\n" +description: "validator module Context exposing (main)\n\nimport Cardano.Tx exposing (..)\nimport Cardano.Address exposing (..)\nimport Cardano.Time as Time\nimport Cardano.Value as Value exposing (Value)\n\ninteger : int -> Int\ninteger = lift\n\nbytes : bytes -> Bytes\nbytes = lift\n\nkey = PubKeyCredential (lift #\"6b6579\")\nscript = ScriptCredential (lift #\"736372697074\")\nref = OutputReference (lift #\"7478\") 2\nrep = DRep key\n\ncertificates : List Certificate\ncertificates = lift\n [ RegStaking key Option.None\n , UnRegStaking script (Option.Some 12)\n , DelegStaking key (DelegStake (lift #\"706f6f6c\"))\n , RegDeleg key (DelegVote DRepAlwaysAbstain) 13\n , RegDRep key 14\n , UpdateDRep script\n , UnRegDRep key 15\n , PoolRegister (lift #\"706f6f6c\") (lift #\"767266\")\n , PoolRetire (lift #\"706f6f6c\") 16\n , AuthHotCommittee key script\n , ResignColdCommittee script\n , DelegStaking key (DelegStakeVote (lift #\"706f6f6c\") rep)\n , DelegStaking key (DelegVote DRepAlwaysNoConfidence)\n ]\nactionId = GovernanceActionId (lift #\"70726576696f7573\") 3\n\nactions : list GovernanceAction\nactions =\n [ ParameterChange Option.None (toData (Builtin.mapData (Map.singleton (integer 0) (integer 42)))) (Option.Some (lift #\"6775617264\"))\n , HardForkInitiation (Option.Some actionId) (ProtocolVersion 11 0)\n , TreasuryWithdrawals (lift (Map.singleton key (integer 17))) Option.None\n , NoConfidence (Option.Some actionId)\n , UpdateCommittee Option.None (lift [key]) (lift (Map.singleton script (integer 18))) (Rational 2 3)\n , NewConstitution (Option.Some actionId) (Constitution Option.None)\n , InfoAction\n , NewConstitution Option.None (Constitution (Option.Some (lift #\"636f6e737469747574696f6e\")))\n ]\nproposals : List Proposal\nproposals = lift (List.map (Proposal 19 key) actions)\nproposal = Proposal 19 key InfoAction\nvoters = [CommitteeVoter key, DRepVoter script, StakePoolVoter (lift #\"706f6f6c\")]\npurposes = [Minting (lift #\"706f6c696379\"), Spending ref, Rewarding key, Certifying 0 (RegStaking key Option.None), Voting (CommitteeVoter key), Proposing 6 proposal]\ninfos = [MintingScript (lift #\"706f6c696379\"), SpendingScript ref (Option.Some (toData (integer 99))), RewardingScript key, CertifyingScript 0 (RegStaking key Option.None), VotingScript (CommitteeVoter key), ProposingScript 6 proposal]\ncoin : Value\ncoin = lift (Value.singleton #\"\" #\"\" 100)\nmint : Value\nmint = lift (Value.singleton #\"706f6c696379\" #\"6173736574\" -2)\naddresses = [Address key Option.None, Address script (Option.Some (StakingHash key)), Address key (Option.Some (StakingPtr 20 21 22))]\ndatums = [NoDatum, DatumHash (lift #\"646174756d\"), InlineDatum (toData (integer 23))]\noutputs = List.map2 (\\address datum -> Output address coin datum (Option.Some (lift #\"7265662d736372697074\"))) addresses datums\nfirstOutput = Output (Address key Option.None) coin NoDatum (Option.Some (lift #\"7265662d736372697074\"))\nlastOutput = Output (Address key (Option.Some (StakingPtr 20 21 22))) coin (InlineDatum (toData (integer 23))) (Option.Some (lift #\"7265662d736372697074\"))\n\ntx = Tx\n (lift [Input ref firstOutput])\n (lift [Input (OutputReference (lift #\"7265666572656e6365\") 1) lastOutput])\n (lift outputs)\n 2\n mint\n certificates\n (lift (Map.singleton key (integer 24)))\n (Time.Interval (Time.IntervalBound (Time.Finite 10) Bool.False) (Time.IntervalBound (Time.Finite 30) Bool.True))\n (lift [lift #\"6b6579\"])\n (lift (List.map (\\purpose -> Builtin.mkPairData purpose (toData (integer 25))) purposes))\n (lift (Map.singleton (bytes #\"646174756d\") (toData (integer 23))))\n (lift #\"7472616e73616374696f6e\")\n (lift (List.map2 (\\voter vote -> Builtin.mkPairData voter (Builtin.mapData (Map.singleton actionId vote))) voters [VoteNo, VoteYes, Abstain]))\n proposals\n (Option.Some 26)\n Option.None\n\nmain : Data -> unit\nmain packet =\n case packet of\n Constr pair(0, [raw, B encoded]) -> check raw encoded\n _ -> fail\n\ncheck : Data -> bytes -> unit\ncheck raw encoded =\n let\n context : ScriptContext\n context = validate raw\n index = Data.tag (toData context.scriptInfo)\n expected =\n case context.scriptInfo of\n SpendingScript _ Option.None ->\n ScriptContext\n { tx | currentTreasury = Option.None, treasuryDonation = (Option.Some 28), outputs = (lift [Output (Address key Option.None) coin NoDatum Option.None]) }\n (toData (integer 27))\n (SpendingScript ref Option.None)\n _ -> ScriptContext tx (toData (integer 27)) (List.at index infos |> Option.unwrap)\n _ = assert (context == expected)\n _ = assert (decode raw == Some expected)\n _ = assert (Data.serialise (toData expected) == encoded)\n _ = assert (Value.lovelace (List.at 0 context.tx.outputs |> Option.unwrap).value == 100)\n _ = assert (Value.quantityOf #\"706f6c696379\" #\"6173736574\" context.tx.mint == -2)\n _ = assert (Time.member 11 context.tx.validityRange)\n _ = assert (Bool.not (Time.member 10 context.tx.validityRange))\n in\n ()\n" --- context-0.cbor: Ok(Constant(Unit)) context-1.cbor: Ok(Constant(Unit)) diff --git a/crates/nash-driver/tests/snapshots/cardano__malformed_contexts.snap b/crates/nash-driver/tests/snapshots/cardano__malformed_contexts.snap index c0dd7882..de19f6a4 100644 --- a/crates/nash-driver/tests/snapshots/cardano__malformed_contexts.snap +++ b/crates/nash-driver/tests/snapshots/cardano__malformed_contexts.snap @@ -1,24 +1,24 @@ --- source: crates/nash-driver/tests/cardano.rs -description: "validator module Validate exposing (main)\n\nimport Cardano.Tx exposing (ScriptContext)\n\nmain : Data -> unit\nmain raw =\n let\n context : ScriptContext\n context = validate raw\n in\n ()\n" +description: "validator module Validate exposing (main)\n\nimport Cardano.Tx exposing (ScriptContext)\n\nmain : Data -> unit\nmain raw =\n let\n context : ScriptContext\n context = validate raw\n in\n ()\n\nvalidator module DecodeContext exposing (main)\n\nimport Cardano.Tx exposing (ScriptContext)\n\nchecked : Data -> option ScriptContext\nchecked = decode\n\nmain : Data -> unit\nmain input =\n case input of\n Constr pair(0, [I expected, raw]) ->\n case checked raw of\n Some _ -> if expected == 1 then () else fail\n None -> if expected == 0 then () else fail\n _ -> fail\n" --- -valid context: Ok(Constant(Unit)) -wrong context tag: Err(ExplicitErrorTerm) -missing context field: Err(ExplicitErrorTerm) -extra context field: Err(ExplicitErrorTerm) -wrong inputs shape: Err(Runtime(MalFormedData(Integer(0)))) -wrong reference inputs shape: Err(Runtime(MalFormedData(Integer(0)))) -wrong outputs shape: Err(Runtime(MalFormedData(Integer(0)))) -wrong fee shape: Err(Runtime(MalFormedData(ByteString([119, 114, 111, 110, 103])))) -wrong mint shape: Err(Runtime(MalFormedData(Integer(0)))) -wrong certificates shape: Err(Runtime(MalFormedData(Integer(0)))) -wrong withdrawals shape: Err(Runtime(MalFormedData(Integer(0)))) -wrong validity range shape: Err(Runtime(MalFormedData(Integer(0)))) -wrong signatories shape: Err(Runtime(MalFormedData(Integer(0)))) -wrong redeemers shape: Err(Runtime(MalFormedData(Integer(0)))) -wrong datums shape: Err(Runtime(MalFormedData(Integer(0)))) -wrong transaction id shape: Err(Runtime(MalFormedData(Integer(0)))) -wrong votes shape: Err(Runtime(MalFormedData(Integer(0)))) -wrong proposals shape: Err(Runtime(MalFormedData(Integer(0)))) -wrong current treasury shape: Err(Runtime(MalFormedData(Integer(0)))) -wrong treasury donation shape: Err(Runtime(MalFormedData(Integer(0)))) +valid context: validate Ok(Constant(Unit)); decode matches expected: Ok(Constant(Unit)) +wrong context tag: validate Err(ExplicitErrorTerm); decode matches expected: Ok(Constant(Unit)) +missing context field: validate Err(ExplicitErrorTerm); decode matches expected: Ok(Constant(Unit)) +extra context field: validate Err(ExplicitErrorTerm); decode matches expected: Ok(Constant(Unit)) +wrong inputs shape: validate Err(Runtime(MalFormedData(Integer(0)))); decode matches expected: Ok(Constant(Unit)) +wrong reference inputs shape: validate Err(Runtime(MalFormedData(Integer(0)))); decode matches expected: Ok(Constant(Unit)) +wrong outputs shape: validate Err(Runtime(MalFormedData(Integer(0)))); decode matches expected: Ok(Constant(Unit)) +wrong fee shape: validate Err(Runtime(MalFormedData(ByteString([119, 114, 111, 110, 103])))); decode matches expected: Ok(Constant(Unit)) +wrong mint shape: validate Err(Runtime(MalFormedData(Integer(0)))); decode matches expected: Ok(Constant(Unit)) +wrong certificates shape: validate Err(Runtime(MalFormedData(Integer(0)))); decode matches expected: Ok(Constant(Unit)) +wrong withdrawals shape: validate Err(Runtime(MalFormedData(Integer(0)))); decode matches expected: Ok(Constant(Unit)) +wrong validity range shape: validate Err(Runtime(MalFormedData(Integer(0)))); decode matches expected: Ok(Constant(Unit)) +wrong signatories shape: validate Err(Runtime(MalFormedData(Integer(0)))); decode matches expected: Ok(Constant(Unit)) +wrong redeemers shape: validate Err(Runtime(MalFormedData(Integer(0)))); decode matches expected: Ok(Constant(Unit)) +wrong datums shape: validate Err(Runtime(MalFormedData(Integer(0)))); decode matches expected: Ok(Constant(Unit)) +wrong transaction id shape: validate Err(Runtime(MalFormedData(Integer(0)))); decode matches expected: Ok(Constant(Unit)) +wrong votes shape: validate Err(Runtime(MalFormedData(Integer(0)))); decode matches expected: Ok(Constant(Unit)) +wrong proposals shape: validate Err(Runtime(MalFormedData(Integer(0)))); decode matches expected: Ok(Constant(Unit)) +wrong current treasury shape: validate Err(Runtime(MalFormedData(Integer(0)))); decode matches expected: Ok(Constant(Unit)) +wrong treasury donation shape: validate Err(Runtime(MalFormedData(Integer(0)))); decode matches expected: Ok(Constant(Unit)) diff --git a/crates/nash-plutus/src/flat/encode/mod.rs b/crates/nash-plutus/src/flat/encode/mod.rs index 8110ff0c..dc2c2558 100644 --- a/crates/nash-plutus/src/flat/encode/mod.rs +++ b/crates/nash-plutus/src/flat/encode/mod.rs @@ -44,62 +44,70 @@ fn encode_term<'a, V>(encoder: &mut Encoder, term: &'a Term<'a, V>) -> Result<() where V: Binder<'a>, { - match term { - Term::Var(name) => { - encode_term_tag(encoder, tag::VAR)?; - - name.var_encode(encoder)?; - } - Term::Lambda { parameter, body } => { - encode_term_tag(encoder, tag::LAMBDA)?; - - parameter.parameter_encode(encoder)?; - - encode_term(encoder, body)?; - } - Term::Apply { function, argument } => { - encode_term_tag(encoder, tag::APPLY)?; - - encode_term(encoder, function)?; - - encode_term(encoder, argument)?; - } - Term::Delay(body) => { - encode_term_tag(encoder, tag::DELAY)?; - - encode_term(encoder, body)?; - } - Term::Force(body) => { - encode_term_tag(encoder, tag::FORCE)?; - - encode_term(encoder, body)?; - } - Term::Case { constr, branches } => { - encode_term_tag(encoder, tag::CASE)?; - - encode_term(encoder, constr)?; - - encoder.list_with(branches, |e, t| encode_term(e, t))?; - } - Term::Constr { tag, fields } => { - encode_term_tag(encoder, tag::CONSTR)?; - - encoder.word(*tag); - - encoder.list_with(fields, |e, t| encode_term(e, t))?; - } - Term::Constant(c) => { - encode_term_tag(encoder, tag::CONSTANT)?; - - encode_constant(encoder, c)?; - } - Term::Builtin(b) => { - encode_term_tag(encoder, tag::BUILTIN)?; + enum Work<'a, V> { + Term(&'a Term<'a, V>), + List(&'a [&'a Term<'a, V>]), + } - encoder.bits(tag::BUILTIN_TAG_WIDTH as i64, **b as u8); - } - Term::Error => { - encode_term_tag(encoder, tag::ERROR)?; + let mut pending = vec![Work::Term(term)]; + while let Some(work) = pending.pop() { + let term = match work { + Work::Term(term) => term, + Work::List(items) => { + if let Some((first, rest)) = items.split_first() { + encoder.one(); + pending.push(Work::List(rest)); + pending.push(Work::Term(first)); + } else { + encoder.zero(); + } + continue; + } + }; + match term { + Term::Var(name) => { + encode_term_tag(encoder, tag::VAR)?; + name.var_encode(encoder)?; + } + Term::Lambda { parameter, body } => { + encode_term_tag(encoder, tag::LAMBDA)?; + parameter.parameter_encode(encoder)?; + pending.push(Work::Term(body)); + } + Term::Apply { function, argument } => { + encode_term_tag(encoder, tag::APPLY)?; + pending.push(Work::Term(argument)); + pending.push(Work::Term(function)); + } + Term::Delay(body) => { + encode_term_tag(encoder, tag::DELAY)?; + pending.push(Work::Term(body)); + } + Term::Force(body) => { + encode_term_tag(encoder, tag::FORCE)?; + pending.push(Work::Term(body)); + } + Term::Case { constr, branches } => { + encode_term_tag(encoder, tag::CASE)?; + pending.push(Work::List(branches)); + pending.push(Work::Term(constr)); + } + Term::Constr { tag, fields } => { + encode_term_tag(encoder, tag::CONSTR)?; + encoder.word(*tag); + pending.push(Work::List(fields)); + } + Term::Constant(c) => { + encode_term_tag(encoder, tag::CONSTANT)?; + encode_constant(encoder, c)?; + } + Term::Builtin(b) => { + encode_term_tag(encoder, tag::BUILTIN)?; + encoder.bits(tag::BUILTIN_TAG_WIDTH as i64, **b as u8); + } + Term::Error => { + encode_term_tag(encoder, tag::ERROR)?; + } } } @@ -310,6 +318,22 @@ mod tests { use crate::binder::DeBruijn; use crate::flat::decode; + #[test] + fn deeply_nested_delays_encode_without_call_stack_growth() { + let arena = Arena::new(); + let mut term = Term::::error(&arena); + for _ in 0..20_000 { + term = term.delay(&arena); + } + let program = Program::new(&arena, crate::program::Version::plutus_v3(&arena), term); + let bytes = encode(program).unwrap(); + // Version 1.1.0, 20,000 four-bit delay tags, then error and filler. + let mut expected = vec![1, 1, 0]; + expected.extend(std::iter::repeat_n(0x11, 10_000)); + expected.push(0x61); + assert_eq!(bytes, expected); + } + #[test] fn cbor_wraps_flat_bytes_once() { let arena = Arena::new(); diff --git a/docs/data.md b/docs/data.md index a1d22c36..7fe7144b 100644 --- a/docs/data.md +++ b/docs/data.md @@ -3,7 +3,7 @@ `Data` is the Plutus `Data` value: the on-chain wire format for datums, redeemers and the script context. In Nash it is an ordinary Big type with five constructors that can be pattern matched, plus a small set of traits -and a stdlib decoder library on top. Decisions follow +for conversion and independent trapping or optional checks. Decisions follow [overview.md](overview.md); the representation of every other type is in [representation.md](representation.md); builtin lowering is in [codegen.md](codegen.md). @@ -102,14 +102,14 @@ usually inspected when a full `validate` is too expensive. ## Traits ```elm -trait ToData ('a : Big) where +trait ToData 'a where toData : 'a -> Data impl ToData ('a : Big) where toData = Primitive.coerce -trait FromData ('a : Big) where - fromData : Data -> 'a -- unchecked identity +trait FromData 'a where + fromData : Data -> 'a -- Big blanket is unchecked identity impl FromData ('a : Big) where fromData = Primitive.coerce @@ -117,31 +117,32 @@ impl FromData ('a : Big) where trait Validate ('a : Big) where validate : Data -> 'a -- required recursive validation +trait Decode 'a where + decode : Data -> option 'a + trait Lift 'small 'big where lift : 'small -> 'big lower : 'big -> 'small ``` -`ToData` and `FromData` apply to Big types. Core supplies an ordinary blanket -impl for each trait, covering every Big type, including user ADTs, nominal -record aliases, lists and maps. The impl bodies define the coercion methods directly; -collection elements need no conversion or validation constraints. Neither -conversion trait needs derivation, and concrete impls would overlap the -blanket impls. `Validate` is separate and opt-in: core provides impls for -`Int`, `Bytes`, `Data`, `List 'a` and `Map 'k 'v`; user types need a source -`Validate` impl. `@derive(Validate)` remains future macro work. +`ToData` and `FromData` accept all types. Their ordinary blanket implementations +cover only Big types, including user ADTs, nominal record aliases, lists and +maps. Big values need no traversal or element constraints. Little types use +explicit implementations; user-defined little types may supply their own. +`Validate` remains Big-only and opt-in. `Decode` accepts any type and returns +`option`; it is independent of Validate. Neither is implemented through the other. -`fromData` uses `Primitive.coerce`, an unchecked identity. It checks +For Big types, `fromData` uses `Primitive.coerce`, an unchecked identity. It checks neither the outer Data shape nor nested fields. Malformed data fails only if a later operation needs the expected shape; a value that is never inspected can pass through unchanged. `Validate.validate` is the required method of a separate trait: core impls check the shape and recursively validate collection elements. -`Data` itself accepts every Data shape. `Data.Decode` supplies non-failing +`Data` itself accepts every Data shape. `Data.decode` supplies non-failing option-based decoding. -`toData` uses `Primitive.coerce`: every Big value already has its Data +For Big types, `toData` uses `Primitive.coerce`: every Big value already has its Data representation. Conversion preserves that value and its wire encoding without -traversing or rebuilding it. Concrete `ToData` impls would overlap the blanket +traversing or rebuilding it. Concrete Big `ToData` impls would overlap the blanket impl and are rejected. For example, validation is an ordinary source impl: @@ -195,50 +196,37 @@ on actual builtin applications and preserve errors and evaluation order. ## Encoding and decoding -`Data.Encode` contains ordinary Nash functions for integers, bytes, UTF-8 -strings, booleans, lists, maps and constructors. See [stdlib.md](stdlib.md#datadecode-dataencode) -for their signatures. Encoders accept Big/little outer representations where -applicable, and element encoders are explicit. `Data.tag` and `Data.fields` -assume constructor Data and fail on other shapes. - -Labeled constructor fields encode flat: `type Datum = Datum { owner : Bytes, -deadline : Int }` has two positional fields under tag 0. Alias records use a -Data list instead. - -`Data.Decode.decoder 'a` is the function alias `Data -> option 'a`, with -ordinary Functor, Applicative and Monad instances. Primitive decoders return -`None` for the wrong shape; string decoding also rejects malformed UTF-8. -`constr` checks a tag and keeps the original node; `field` selects a constructor -field. Missing/negative indices return `None`; extra fields are allowed. +The Data module owns ToData, FromData, Validate and Decode. See +[stdlib.md](stdlib.md#data-conversion-and-checking) for their contracts. +Use `toData` to encode little values or expose an existing Big representation; +use `decode` for optional checks and `validate` for checks that fail execution. ```nash -import Data.Decode as Decode - -type alias datum = { owner : bytes, deadline : int } - -datumDecoder : Decode.decoder datum -datumDecoder = Decode.constr 0 <| do - owner <- Decode.field 0 Decode.bytes - deadline <- Decode.field 1 Decode.int - pure { owner = owner, deadline = deadline } +import Data exposing (Decode) + +type datum = Datum bytes int + +impl Decode datum where + decode raw = + case raw of + Constr pair(0, [owner, deadline]) -> do + key <- decode owner + time <- decode deadline + Some (Datum key time) + _ -> None ``` -Use ordinary `map`, `pure`, application and `do` instead of custom mapping or -binding helpers. `reject` always returns `None`; `oneOf` tries a `Cons` sequence -of decoders, stopping at the first success. Function values cannot inhabit a -native list. Similarly decoded maps use `Cons` of tuples so decoded components -can be arbitrary little values. Native `Decode.list` retains its Storable -constraint. Map encoding/decoding preserves duplicates and order. - -The library is implemented in Nash. There is no compiler-generated decoder or -special codegen treatment of decoder combinators. A user-written decoder that -calls `fail` still aborts evaluation; `None` is the recoverable failure result. +This is normal source code using option's Monad instance. No decoder-specific +combinators or codegen behavior are involved. Labeled Big constructor fields +encode flat; Big alias records use Data lists instead. Raw constructor Data +is constructed with Builtin.constrData. Data.tag and Data.fields assume that +shape and fail on other shapes. ## Error cases | Situation | Where reported | |---|---| -| `impl ToData` / `impl FromData` / `impl Validate` for a non-Big type | representation superclass check | +| `impl Validate` for a non-Big type | representation superclass check | | `Constr` pattern with a Big field type (e.g. `Constr pair(0, [x : Int])`) | type check (fields of `Data` are `Const`) | | `fromData d` where the node shape is wrong | no check; a later operation requiring that shape can fail | | `validate d` where the node shape is wrong | runtime failure in source validation | @@ -251,7 +239,7 @@ calls `fail` still aborts evaluation; `None` is the recoverable failure result. - **Kinds and representation** ([kinds.md](kinds.md)): `Data` has kind `Type` and representation `Big`; its constructor fields are `Const`. -- **Traits** ([traits.md](traits.md)): `ToData`, `FromData`, `Validate`, `Lift` are +- **Traits** ([traits.md](traits.md)): `ToData`, `FromData`, `Validate`, `Decode`, `Lift` are ordinary traits with stdlib impls; deriving is a macro. - **Codegen** ([codegen.md](codegen.md)): typed builtins, Data patterns, the `Case(Data)` lowering. @@ -259,5 +247,5 @@ calls `fail` still aborts evaluation; `None` is the recoverable failure result. or Const; for the Big ones, `Data` patterns and `validate` are how their shape is checked. - **Macros** ([macros.md](macros.md)): `@derive(Validate)`; conversion traits need no derivation. - A `field "owner"` form of `Data.Decode.field` that resolves the label + A future source macro that resolves a constructor field label through an alias in scope would be a macro, not a library function. diff --git a/docs/overview.md b/docs/overview.md index 3e3507a3..8777640e 100644 --- a/docs/overview.md +++ b/docs/overview.md @@ -64,8 +64,8 @@ implementation, default imports, Prop and the full validator example. | Tuples | Always `Term` (`constr 0 [..]`), fields of any representation (each has kind `Type`). No Big tuple. | | Named ctor fields | `type Datum = Datum { owner : Bytes, deadline : Int }` is a constructor with *labeled fields* (Aiken style), not a nested record: encoded flat (`Constr 0 [B, I]` / `constr 0 [..]`), fields follow the enclosing type's representation rule, `.field` access allowed on single-constructor types, record-style construction `Datum { owner = o, deadline = d }` and pattern `Datum { owner, deadline }` allowed; record updates on alias records and single-constructor labeled types. Anonymous record *types* are not allowed anywhere else; use `type alias`. | | Prelude twins | `bool`/`Bool`, `unit`/`Unit`, `option`/`Option`, `ordering`/`Ordering`. Little constructors are exposed unqualified by the prelude (`True`, `Some`, `LT`...); Big twins only qualified (`Bool.True`, `Option.Some`). `if` takes `bool`. | -| `Data` | Big type with constructors `Constr payload | Map kvs | List xs | I n | B bs`; pattern-matchable. Decoder combinators built on top in the stdlib. | -| Big <-> little | Explicit. `ToData`/`FromData` on Big types (`toData` implemented with `Primitive.coerce` through an ordinary blanket impl for all Big types, unchecked `fromData` implemented with `Primitive.coerce` through its own blanket impl, and opt-in `Validate.validate` for recursive checking in Nash source). `Lift 'small 'big` multi-param trait (`lift`/`lower`) between reprs; built-in reflexive `impl Lift 'a 'a` for every type (exempt from head rules). Lift/lower preserve container payloads and change only the outer representation; `Lift (list ('a : Big)) (List 'a)` wraps directly without traversal. `validate : Data -> 'a` traps on mismatch; `Data.Decode` is the non-failing path. `Primitive.coerce : 'a -> 'b` is explicit unchecked runtime identity for any value types, including functions; it changes no representation and validates no shape. No implicit coercion. | +| `Data` | Big type with constructors `Constr payload | Map kvs | List xs | I n | B bs`; pattern-matchable. Independent Validate and Decode traits perform source-level checking. | +| Big <-> little | Explicit. `ToData`/`FromData` on any type, with explicit little conversions and Big-only blanket implementations (`toData` implemented with `Primitive.coerce` through an ordinary blanket impl for all Big types, unchecked `fromData` implemented with `Primitive.coerce` through its own blanket impl, and opt-in `Validate.validate` for recursive checking in Nash source). `Lift 'small 'big` multi-param trait (`lift`/`lower`) between reprs; built-in reflexive `impl Lift 'a 'a` for every type (exempt from head rules). Lift/lower preserve container payloads and change only the outer representation; `Lift (list ('a : Big)) (List 'a)` wraps directly without traversal. `validate : Data -> 'a` traps on mismatch; `Data.decode` is the non-failing path. `Primitive.coerce : 'a -> 'b` is explicit unchecked runtime identity for any value types, including functions; it changes no representation and validates no shape. No implicit coercion. | | Literals | Polymorphic via `FromInt` / `FromString` / `FromBytes` / `FromBool` / `FromUnit` traits. Ambiguous literals default to little (`int`, `string`, `bytes`, `bool`, `unit`). | | Operators | Trait methods (`Num`, `Integral`, `Eq`, `Ord`, `Semigroup`, ...). Elm's `number`/`comparable`/`appendable` supertypes removed. | | Operator sections | Whole `(+)` plus partial `(> 5)` / `(5 >)`, canonicalized to hygienic lambdas; `(-x)` stays negation. | diff --git a/docs/representation.md b/docs/representation.md index 57efb494..3100003a 100644 --- a/docs/representation.md +++ b/docs/representation.md @@ -266,18 +266,19 @@ Rules: - String encoding is explicit through `String.toBytes` and `String.fromBytes`, not a Lift instance. -### `ToData`, `FromData` and `Validate` +### `ToData`, `FromData`, `Validate` and `Decode` -Defined only for Big types: +ToData, FromData and Decode accept any type. Validate requires Big; only the +conversion blanket implementations require Big: ```elm -trait ToData ('a : Big) where +trait ToData 'a where toData : 'a -> Data impl ToData ('a : Big) where toData = Primitive.coerce -trait FromData ('a : Big) where +trait FromData 'a where fromData : Data -> 'a impl FromData ('a : Big) where @@ -287,7 +288,10 @@ trait Validate ('a : Big) where validate : Data -> 'a ``` -- `toData` uses `Primitive.coerce`. The ordinary blanket impl covers +trait Decode 'a where + decode : Data -> option 'a + +- For Big types, `toData` uses `Primitive.coerce`. The ordinary blanket impl covers every Big type, including user ADTs, nominal aliases, lists and maps, without element `ToData` constraints. It preserves the existing runtime Data value and wire encoding without traversal or reconstruction. @@ -298,12 +302,12 @@ trait Validate ('a : Big) where Malformed data fails only when a later operation needs its expected shape. - `Validate.validate` is the required method of a separate opt-in trait. Base Int and Bytes impls discard the result of a direct unwrapper call, relying on its failure behavior, then return the original coerced value. - List and Map impls unwrap directly and retain recursive source validation. Non-failing decoding uses `Data.Decode`. + List and Map impls unwrap directly and retain recursive source validation. Non-failing decoding uses `Data.decode`. Core provides explicit `Validate` impls for primitive and collection Big types. User Big ADTs opt into validation with `Validate` source impls; future `@derive(Validate)` macros will generate checked recursive `validate`. -Neither conversion trait requires derivation: generated concrete impls would +For Big types, neither conversion trait requires derivation: concrete impls would overlap their blanket impls and be rejected. There is no automatic compiler codec synthesis. Real UPLC builtin signatures carry nominal types: `iData : int -> Int`, `unIData : Int -> int`, and diff --git a/docs/stdlib.md b/docs/stdlib.md index 1ff83d25..66ea51b1 100644 --- a/docs/stdlib.md +++ b/docs/stdlib.md @@ -51,8 +51,6 @@ crates/nash-driver/base/ Pair.nash pair Array.nash array Map.nash Map (Big; its little form is `list (pair 'k 'v)`) - Data/Decode.nash decoders - Data/Encode.nash encoders Prop.nash generators Test.nash label, property preparation and assertion reporting Ast.nash macro AST: little ADTs over cons (see macros.md) @@ -74,8 +72,7 @@ Trait methods such as `Functor.map` retain their trait's container contract. `lift` and `lower` change only the outer representation; both have identity conversion for already-matching types. Recursive element conversion requires explicit mapping. There is no Big String: use `String.toBytes` / `fromBytes` -for UTF-8 and `lift` / `lower` for bytes/Bytes. `Data`, `Data.Decode` and -`Data.Encode` operate on Data itself. Map collection operations return little +for UTF-8 and `lift` / `lower` for bytes/Bytes. `Data` owns conversion, validation and optional decoding traits. Map collection operations return little lists and preserve element types; `keys` and `values` also normalize Big inputs. ## Default imports @@ -101,8 +98,6 @@ import Applicative exposing (Applicative) import Monad exposing (Monad) import Lift exposing (Lift) import Data exposing (ToData, FromData, Validate) -import Data.Decode -import Data.Encode import Literal exposing (FromInt, FromString, FromBytes, FromBool, FromUnit) import Bool exposing (Bool, not, and, or, xor) import Unit exposing (Unit) @@ -415,17 +410,18 @@ identity when both Lift arguments are already the same type. Map wrapping requires Big keys and values and does not convert its entries. ```elm -module Data exposing (ToData, FromData, Validate, serialise, tag, fields) +module Data exposing (ToData, FromData, Validate, Decode, serialise, tag, fields) import Primitive exposing (Data(..)) +import Option exposing (type option(..)) -trait ToData ('a : Big) where +trait ToData 'a where toData : 'a -> Data impl ToData ('a : Big) where toData = Primitive.coerce -trait FromData ('a : Big) where +trait FromData 'a where fromData : Data -> 'a impl FromData ('a : Big) where @@ -434,6 +430,9 @@ impl FromData ('a : Big) where trait Validate ('a : Big) where validate : Data -> 'a +trait Decode 'a where + decode : Data -> option 'a + -- Data itself requires no decoding. impl Validate Data where validate value = value @@ -469,9 +468,8 @@ validation constraints. The required the original value, while List and Map retain recursive source validation. `toData` uses `Primitive.coerce` directly in one ordinary blanket impl for every Big type. User ADTs, nominal aliases, lists and maps all qualify, -without element `ToData` constraints or reconstruction. Additional concrete -impls overlap this blanket impl and are rejected. Data.Decode provides the -non-failing decoder API. +without element `ToData` constraints or reconstruction. Concrete Big +impls overlap this blanket impl and are rejected; little types use explicit impls. Data.decode provides optional checking through an independent Decode trait. ## Twin modules @@ -897,61 +895,43 @@ Data pairs. Other helpers do not impose that restriction. There is no redundant `fromList`: a native pair list is already the little map representation. Use `lift` explicitly when a Big map is required. -## `Data.Decode`, `Data.Encode` - -`Data.tag : Data -> int` and `Data.fields : Data -> list Data` assume constructor -Data and use `unConstrData`; other shapes fail. Recoverable decoding lives in -`Data.Decode`, separately from the trapping `validate` trait. - -```elm -type alias decoder 'a = Data -> option 'a -run : decoder 'a -> Data -> option 'a -int : decoder int -bytes : decoder bytes -string : decoder string -bool : decoder bool -data : decoder Data -list : decoder 'a -> decoder (list 'a) -map : decoder 'k -> decoder 'v -> decoder (Cons.cons ('k, 'v)) -constr : int -> decoder 'a -> decoder 'a -field : int -> decoder 'a -> decoder 'a -oneOf : Cons.cons (decoder 'a) -> decoder 'a -reject : decoder 'a -``` +## Data conversion and checking -The decoder function alias implements `Functor`, `Applicative`, and `Monad`. -Use `map`, `pure`, application or `do` for composition; no numbered mapping or -field helpers are needed. Both application and bind decode against the same -original input, and stop on the first `None`. `constr` checks the tag and passes -the original node to its inner decoder. `field` selects a zero-based constructor -field; negative/missing indices or non-constructor input return `None`. Extra -fields are permitted. `oneOf` tries decoders in order and stops at first success; -an empty sequence returns `None`. `reject` always returns `None` (`fail` is a -language keyword). - -`list` requires Storable decoded elements. Decoded maps use `Cons` of ordinary -tuples so keys and values may be little types or functions; order and duplicates -are retained. `oneOf` also uses `Cons` because decoders are functions. - -`bool` accepts only constructor tags 0 (False) and 1 (True), with no fields. -`string` validates UTF-8 in Nash before calling `decodeUtf8`; malformed byte -sequences return `None`. User-supplied decoder functions can still deliberately -fail; the library does not catch VM failures. +All four traits are defined in `Data.nash` and implicitly available to application modules: -```elm --- Data.Encode -int : Lift int 'a => 'a -> Data -bytes : Lift bytes 'a => 'a -> Data -string : string -> Data -bool : Lift bool 'a => 'a -> Data -list : Lift (list 'a) ('f 'a) => ('a -> Data) -> 'f 'a -> Data -constr : (Lift int 'n, Lift (list Data) ('f Data)) => 'n -> 'f Data -> Data -map : ('k -> Data) -> ('v -> Data) -> Cons.cons ('k, 'v) -> Data -``` - -Encoders normalize outer inputs only; explicit element encoders decide how -contents are represented. Strings use UTF-8 bytes, and booleans use the exact -constructor shapes above. Map encoding preserves order and duplicates. +| Trait | Method | Behavior | +|---|---|---| +| `ToData 'a` | `toData : 'a -> Data` | Encode a value | +| `FromData 'a` | `fromData : Data -> 'a` | Convert without recursive validation guarantees | +| `Validate ('a : Big)` | `validate : Data -> 'a` | Explicit recursive checks; fail on mismatch | +| `Decode 'a` | `decode : Data -> option 'a` | Explicit checks; return None on mismatch | + +ToData and FromData accept any type. Only Big types receive blanket identity +implementations. Little int, bytes, string, bool, unit, list and option have +explicit conversions. Little collections convert their elements recursively; +Big collections pass through unchanged. This does not change Lift/lower, which +still preserve inner elements. User-defined little types can implement either +conversion trait. Concrete Big conversion impls overlap the blanket and are rejected. + +Decode and Validate are independent; neither calls or requires the other. +Decode covers the little types above, Data, Int, Bytes, Bool, Option, List, Map +and the Cardano types. Generic collection instances require their element's +Decode instance. Native lists retain the Storable element restriction; native +Map keys and values must be Big. No unchecked blanket Decode is provided. + +Use normal pattern matching and option's existing `do`, map and bind to combine +optional results. There is no decoder alias, run function, or separate encoder +or decoder module. A user implementation that calls fail still aborts execution; +option does not catch VM errors. + +String conversion uses UTF-8 bytes. Decode checks UTF-8 before decodeUtf8; +FromData string invokes decodeUtf8 directly and can fail. Bool accepts exactly +constructor 0/1 with no fields; unit accepts constructor 0 with no fields. +Map decoding retains ordering and duplicate keys. + +`Data.tag` and `Data.fields` assume constructor Data and fail on other shapes. +Use `Builtin.constrData` to construct raw constructor Data. `Data.serialise` +converts Data to CBOR bytes; it is separate from ToData. ## `Prop` diff --git a/docs/traits.md b/docs/traits.md index 0d0e8cee..11b08506 100644 --- a/docs/traits.md +++ b/docs/traits.md @@ -628,13 +628,13 @@ trait Functor 'f => Applicative 'f where -- map is (<$>) trait Applicative 'm => Monad 'm where bind : 'm 'a -> ('a -> 'm 'b) -> 'm 'b -- (>>=), `do` -trait ToData ('a : Big) where +trait ToData 'a where toData : 'a -> Data impl ToData ('a : Big) where toData = Primitive.coerce -trait FromData ('a : Big) where +trait FromData 'a where fromData : Data -> 'a -- unchecked identity impl FromData ('a : Big) where @@ -643,6 +643,9 @@ impl FromData ('a : Big) where trait Validate ('a : Big) where validate : Data -> 'a -- required recursive validation; traps on bad data +trait Decode 'a where + decode : Data -> option 'a + trait Lift 'small 'big where lift : 'small -> 'big lower : 'big -> 'small @@ -688,7 +691,8 @@ Notes: the module that declares the operator. Operator values and sections use the same scheme; each operator node owns its solved evidence. - Kinds: `ToData`/`FromData`/`Validate` and both `Lift` parameters have kind `Type`. - `ToData`/`FromData`/`Validate` require `Big` through their superclass predicates. + `Validate` requires `Big` through its superclass predicate. `ToData` and + `FromData` accept any type, with Big-only blanket implementations. `Lift` relates its concrete impl heads, with a compiler-owned reflexive rule for all types. `Functor`/`Applicative`/`Monad` parameters have the fixed kind `Type -> Type`; their method formation contexts enforce each diff --git a/plans/03-traits.md b/plans/03-traits.md index a72473e3..0c526039 100644 --- a/plans/03-traits.md +++ b/plans/03-traits.md @@ -2833,13 +2833,14 @@ bool to Unit reports MissingImpl at the original outer `lift` call. Option's Functor/Applicative/Monad impls and remaining helpers are still pending; this does not complete the hierarchy. Formatting, strict Clippy, 1,912 tests and snapshot hygiene pass; no Rust crate changes in this source-only step. -Data defines Big-bounded ToData, FromData and Validate traits, plus +Data defines unrestricted ToData/FromData traits and Big-bounded Validate, plus serialise/tag/fields. ToData and FromData each have an ordinary blanket Big -impl defining its method as Primitive.coerce. Both conversions preserve the +impl defining its method as Primitive.coerce. Both Big conversions preserve the runtime Data value without traversal or shape checks. Every Big type is covered, including user ADTs, nominal record aliases and collections, with -no element conversion or validation prerequisites. Little unit remains -ineligible; nominal Int is covered automatically. +no element conversion or validation prerequisites. Plan 12 adds explicit little +conversions, including unit, and independent optional Decode; nominal Int is +covered automatically. Validate is separate and opt-in. Core Data accepts every shape; Int and Bytes check shape, while List and Map recursively validate their contents through Validate constraints. User ADTs provide source Validate impls until future diff --git a/plans/06-diagnostics.md b/plans/06-diagnostics.md index 06dd7ac9..8944fe40 100644 --- a/plans/06-diagnostics.md +++ b/plans/06-diagnostics.md @@ -1026,7 +1026,7 @@ this chunk too (they are small and `ModuleBadEnd` needs `toDeclStartReport`). Nash edits: "Elm" → "Nash" in prose; the example module names -(`Html.Attributes`, `Json.Decode`) become `Cardano.Tx`, `Data.Decoder`; +(`Html.Attributes`, `Json.Decode`) become `Cardano.Tx`, `Cardano.Address`; the `(..)` note stays. **Code** diff --git a/plans/08-optimizer.md b/plans/08-optimizer.md index bffb3674..f966709f 100644 --- a/plans/08-optimizer.md +++ b/plans/08-optimizer.md @@ -259,7 +259,7 @@ Benchmarks (`tests/budgets.rs`), each a fixture in `tests/fixtures/`: | `sum_static` | `replicate` / `sumTo` from plan 07 chunk 8 | | `data_match` | the four-clause `Data` match from plan 07 chunk 6 | | `validate_datum` | `validate` on a nested record | -| `decoder_datum` | `Data.Decode` example from docs/data.md | +| `decoder_datum` | `Decode` trait example from docs/data.md | **Aiken reference**: none; Aiken measures in `aiken-project` benchmarks and acceptance tests, not in unit tests. @@ -735,7 +735,7 @@ CI runs `cargo test` including the budget gate. 2. **`INLINE_LAMBDA_SIZE` and `CURRY_MIN_USES`** are constants. Making them `Options` fields is trivial if a project needs a size/cost trade-off knob. -3. **Fusion of `Data.Decode` combinators** (docs/data.md) is not in this +3. **Fusion of source decoding functions** (docs/data.md) is not in this plan. It would be a fifth pass after chunk 6, recognizing the monomorphized stdlib names. 4. **Budget baselines on cost-model changes.** A nash-plutus cost-model diff --git a/plans/12-stdlib.md b/plans/12-stdlib.md index 6b752ab3..bac6a458 100644 --- a/plans/12-stdlib.md +++ b/plans/12-stdlib.md @@ -56,7 +56,7 @@ replace the tested PRNG, replay, label, and assertion protocols. ## Current status Reconciled with chunk 11 implementation (2026-09-25). Base currently -ships 34 embedded Nash modules. Plan 12 remains incomplete in `SPEC.md`. +ships 32 embedded Nash modules. Plan 12 remains incomplete in `SPEC.md`. | Chunk | Status | Remaining work | |---|---|---| @@ -251,10 +251,10 @@ Read-only review covered API semantics, conversion evidence, and source snapshot - [x] Ship Data.serialise, Data.tag and Data.fields alongside the Data traits. - [x] Make tag/fields direct constructor accessors; non-constructor input fails. -- [x] Ship Data.Decode and Data.Encode with source snapshot and round-trip tests. -- [x] Use decoder function aliases with Functor/Applicative/Monad composition. -- [x] Decode map entries into Cons tuples; use Cons for decoder alternatives. -- [x] Reject malformed UTF-8 through the recoverable string decoder. +- [x] Consolidate conversion and checking traits in Data.nash; remove decoder combinators. +- [x] Keep Big-only conversion blankets; add explicit little-type conversions. +- [x] Provide independent Decode and Validate implementations. +- [x] Reject malformed UTF-8 through optional string decoding. - [x] Ship right-biased Map.union returning a little list of pairs. - [x] Ship Map construction, lookup, update, removal, folding and collection helpers. - [x] Make Map.keys/values normalize Big input internally through Lift; relational @@ -262,27 +262,29 @@ Read-only review covered API semantics, conversion evidence, and source snapshot - [x] Embed nested Base modules and include them in implicit qualified imports. - [x] Complete full workspace validation. -Implementation: `base/src/Data.nash`, `base/src/Data/Decode.nash`, -`base/src/Data/Encode.nash`, `base/src/Map.nash` under `crates/nash-driver`. -Authoritative APIs and behavior are in `docs/stdlib.md` and `docs/data.md`. +Implementation: `base/src/Data.nash` and `base/src/Map.nash` under +`crates/nash-driver`, with type-specific instances in their owning modules. +Authoritative APIs and behavior are in docs/stdlib.md and docs/data.md. +Decode uses ordinary pattern matching and option composition. There is no +separate decoder alias or combinator module. ToData/FromData accept all types, +with blanket identity conversions for Big types only. Validate fails directly; +Decode independently returns None for malformed values. -Decoder composition uses the existing language traits and `do`, with no custom -numbered mapping/field helpers and no new compiler decoder behavior. `constr` -checks the tag and decodes the original node; `field` selects its zero-based -field. Missing fields return None, extra fields are allowed. Recoverable -UTF-8 validation is implemented in Nash. Functions supplied by the user can -still fail explicitly. - -Decoded maps use Cons tuples because native pair construction requires Data -components. Map operations preserve elements and return little collections; +Map operations preserve elements and return little collections; construction uses native Data pairs. Construction/encoding does not deduplicate; get finds the first match, remove deletes all matches, insert appends one new entry after removing existing matches, and union is right-biased. A native pair list needs no fromList wrapper; explicit lift constructs its Big representation. Tests use the shared in-process Base snapshot runner, including integer/bytes/list -round-trip properties, invalid UTF-8, decoder composition, function-valued map -entries, duplicate keys, map ordering, and constructor accessor failures. +round trips, invalid UTF-8, option composition, user little conversions, +duplicate keys, map ordering, and constructor accessor failures. + +The Data trait consolidation also checks all seven Haskell context fixtures +through independent Decode instances and rejects malformed contexts without +trapping. Flat term encoding uses an iterative traversal for large decoders. +Validation: formatting, strict all-target/all-feature Clippy, and the full +workspace suite passed (3,452 tests, 3 ignored). Validation: formatting and strict Clippy passed; 3,432 workspace tests passed, 3 ignored. Snapshot tests run without a custom Rust stack setting. From bec1d2accff3476ada1195d93c473f68f5434a08 Mon Sep 17 00:00:00 2001 From: microproofs Date: Fri, 25 Sep 2026 17:58:37 -0400 Subject: [PATCH 06/10] feat(base): add math and crypto helpers Signed-off-by: microproofs --- .sampo/changesets/math-crypto-base.md | 9 + SPEC.md | 2 +- crates/nash-can/src/defaults.rs | 2 + crates/nash-driver/base/src/Bytes.nash | 19 +- crates/nash-driver/base/src/Crypto.nash | 31 +++ crates/nash-driver/base/src/Int.nash | 76 +++++- crates/nash-driver/base/src/Rational.nash | 97 ++++++++ crates/nash-driver/tests/base_traits.rs | 4 + .../tests/fixtures/base-traits/Crypto.nash | 155 ++++++++++++ .../fixtures/base-traits/IntegerMath.nash | 232 ++++++++++++++++++ .../tests/fixtures/base-traits/Rational.nash | 170 +++++++++++++ .../fixtures/base-traits/TypeHelpers.nash | 8 - .../tests/fixtures/crypto/README.md | 26 ++ .../nash-driver/tests/fixtures/math/README.md | 7 + .../tests/fixtures/math/reference.py | 15 ++ .../base_traits__crypto_helpers.snap | 55 +++++ .../snapshots/base_traits__integer_math.snap | 115 +++++++++ .../snapshots/base_traits__rational_math.snap | 83 +++++++ .../snapshots/base_traits__type_helpers.snap | 4 +- ...testing_base__integer_predicate_costs.snap | 20 ++ crates/nash-driver/tests/testing_base.rs | 101 ++++++-- docs/stdlib.md | 92 ++++++- plans/12-stdlib.md | 64 ++++- 23 files changed, 1322 insertions(+), 65 deletions(-) create mode 100644 .sampo/changesets/math-crypto-base.md create mode 100644 crates/nash-driver/base/src/Crypto.nash create mode 100644 crates/nash-driver/base/src/Rational.nash create mode 100644 crates/nash-driver/tests/fixtures/base-traits/Crypto.nash create mode 100644 crates/nash-driver/tests/fixtures/base-traits/IntegerMath.nash create mode 100644 crates/nash-driver/tests/fixtures/base-traits/Rational.nash create mode 100644 crates/nash-driver/tests/fixtures/crypto/README.md create mode 100644 crates/nash-driver/tests/fixtures/math/README.md create mode 100644 crates/nash-driver/tests/fixtures/math/reference.py create mode 100644 crates/nash-driver/tests/snapshots/base_traits__crypto_helpers.snap create mode 100644 crates/nash-driver/tests/snapshots/base_traits__integer_math.snap create mode 100644 crates/nash-driver/tests/snapshots/base_traits__rational_math.snap create mode 100644 crates/nash-driver/tests/snapshots/testing_base__integer_predicate_costs.snap diff --git a/.sampo/changesets/math-crypto-base.md b/.sampo/changesets/math-crypto-base.md new file mode 100644 index 00000000..99aaeaee --- /dev/null +++ b/.sampo/changesets/math-crypto-base.md @@ -0,0 +1,9 @@ +--- +cargo/nash-driver: minor +cargo/nash-can: minor +--- + +Add integer gcd/lcm/square-root operations and result-checking predicates, +normalized little rational arithmetic, and Big/little Crypto wrappers over +Plutus hash and signature builtins. Move the Bytes hash helpers into Crypto. Embed Rational and Crypto with implicit +qualified imports, exact reference snapshots and predicate budget comparisons. diff --git a/SPEC.md b/SPEC.md index 96a167fb..c348e2b9 100644 --- a/SPEC.md +++ b/SPEC.md @@ -67,7 +67,7 @@ Implementation plans (Plan 08 is deferred; Plans 09 and 10 are complete): - [x] 09 Validators + `nash build` — [plans/09-validators-build.md](plans/09-validators-build.md) - [x] 10 Testing: `tests` block, props, generators, shrinking, power-assert, `nash test` — [plans/10-testing.md](plans/10-testing.md) - [ ] 11 Macros + comptime — [plans/11-macros-comptime.md](plans/11-macros-comptime.md) -- [ ] 12 Stdlib `nash/base` — [plans/12-stdlib.md](plans/12-stdlib.md) (chunks 1–9 and 11 complete; chunk 10 Ast/Derive deferred) +- [ ] 12 Stdlib `nash/base` — [plans/12-stdlib.md](plans/12-stdlib.md) (chunks 1–9 and 11–13 complete; chunk 10 Ast/Derive deferred) - [ ] 13 `nash fmt`, `nash docs` — [plans/13-fmt-docs.md](plans/13-fmt-docs.md) - [x] Representation-classed impl heads: `Big`/`Little` blankets and concrete little impls coexist — [plans/repr-classed-impl-heads.md](plans/repr-classed-impl-heads.md) diff --git a/crates/nash-can/src/defaults.rs b/crates/nash-can/src/defaults.rs index a4c593e0..22b74274 100644 --- a/crates/nash-can/src/defaults.rs +++ b/crates/nash-can/src/defaults.rs @@ -31,6 +31,8 @@ pub const MODULES: &[(&str, &[&str])] = &[ ("Ordering", &["Ordering", "type ordering"]), ("Cons", &["type cons"]), ("Int", &[]), + ("Rational", &[]), + ("Crypto", &[]), ("Bytes", &[]), ("String", &[]), ("List", &[]), diff --git a/crates/nash-driver/base/src/Bytes.nash b/crates/nash-driver/base/src/Bytes.nash index 5f371fc6..d41b7fea 100644 --- a/crates/nash-driver/base/src/Bytes.nash +++ b/crates/nash-driver/base/src/Bytes.nash @@ -1,4 +1,4 @@ -module Bytes exposing (append, compare, min, max, length, at, slice, take, drop, concat, sha2_256, sha3_256, blake2b_256, blake2b_224, keccak_256, ripemd_160, and, or, xor, complement, readBit, writeBits, shift, rotate, countSetBits, findFirstSetBit, toHex) +module Bytes exposing (append, compare, min, max, length, at, slice, take, drop, concat, and, or, xor, complement, readBit, writeBits, shift, rotate, countSetBits, findFirstSetBit, toHex) import Literal import Lift exposing (Lift) @@ -49,23 +49,6 @@ drop count value = concat : (Lift (list 'b) ('f 'b), Lift bytes 'b) => ('f 'b) -> bytes concat values = List.foldr (\x acc -> Builtin.appendByteString (lower x) acc) #"" values -sha2_256 : Lift bytes 'b => 'b -> bytes -sha2_256 value = Builtin.sha2_256 (lower value) - -sha3_256 : Lift bytes 'b => 'b -> bytes -sha3_256 value = Builtin.sha3_256 (lower value) - -blake2b_256 : Lift bytes 'b => 'b -> bytes -blake2b_256 value = Builtin.blake2b_256 (lower value) - -blake2b_224 : Lift bytes 'b => 'b -> bytes -blake2b_224 value = Builtin.blake2b_224 (lower value) - -keccak_256 : Lift bytes 'b => 'b -> bytes -keccak_256 value = Builtin.keccak_256 (lower value) - -ripemd_160 : Lift bytes 'b => 'b -> bytes -ripemd_160 value = Builtin.ripemd_160 (lower value) complement : Lift bytes 'b => 'b -> bytes complement value = Builtin.complementByteString (lower value) diff --git a/crates/nash-driver/base/src/Crypto.nash b/crates/nash-driver/base/src/Crypto.nash new file mode 100644 index 00000000..9163a2a2 --- /dev/null +++ b/crates/nash-driver/base/src/Crypto.nash @@ -0,0 +1,31 @@ +module Crypto exposing (sha2_256, sha3_256, blake2b_224, blake2b_256, keccak_256, ripemd_160, verifyEd25519Signature, verifyEcdsaSecp256k1Signature, verifySchnorrSecp256k1Signature) + +import Lift exposing (Lift) +import Builtin + +sha2_256 : Lift bytes 'a => 'a -> bytes +sha2_256 value = Builtin.sha2_256 (lower value) + +sha3_256 : Lift bytes 'a => 'a -> bytes +sha3_256 value = Builtin.sha3_256 (lower value) + +blake2b_224 : Lift bytes 'a => 'a -> bytes +blake2b_224 value = Builtin.blake2b_224 (lower value) + +blake2b_256 : Lift bytes 'a => 'a -> bytes +blake2b_256 value = Builtin.blake2b_256 (lower value) + +keccak_256 : Lift bytes 'a => 'a -> bytes +keccak_256 value = Builtin.keccak_256 (lower value) + +ripemd_160 : Lift bytes 'a => 'a -> bytes +ripemd_160 value = Builtin.ripemd_160 (lower value) + +verifyEd25519Signature : (Lift bytes 'k, Lift bytes 'm, Lift bytes 's) => 'k -> 'm -> 's -> bool +verifyEd25519Signature key message signature = Builtin.verifyEd25519Signature (lower key) (lower message) (lower signature) + +verifyEcdsaSecp256k1Signature : (Lift bytes 'k, Lift bytes 'm, Lift bytes 's) => 'k -> 'm -> 's -> bool +verifyEcdsaSecp256k1Signature key message signature = Builtin.verifyEcdsaSecp256k1Signature (lower key) (lower message) (lower signature) + +verifySchnorrSecp256k1Signature : (Lift bytes 'k, Lift bytes 'm, Lift bytes 's) => 'k -> 'm -> 's -> bool +verifySchnorrSecp256k1Signature key message signature = Builtin.verifySchnorrSecp256k1Signature (lower key) (lower message) (lower signature) diff --git a/crates/nash-driver/base/src/Int.nash b/crates/nash-driver/base/src/Int.nash index cf50c99a..75699dcc 100644 --- a/crates/nash-driver/base/src/Int.nash +++ b/crates/nash-driver/base/src/Int.nash @@ -1,5 +1,6 @@ -module Int exposing (add, sub, mul, negate, div, mod, quot, rem, compare, min, max, abs, pow, pow2, powMod, toBytes, fromBytes, toString) +module Int exposing (add, sub, mul, negate, div, mod, quot, rem, compare, min, max, abs, pow, pow2, powMod, gcd, lcm, isqrt, isGcd, isLcm, isSqrt, toBytes, fromBytes, toString) +import Primitive exposing (type bool(..)) import Prelude exposing (..) import Lift exposing (Lift) import Builtin @@ -106,3 +107,76 @@ toString value = showInt (lower value) showInt : int -> string showInt = Show.show + +gcd : (Lift int 'a, Lift int 'b) => 'a -> 'b -> int +gcd left right = gcdInt (abs left) (abs right) + +gcdInt : int -> int -> int +gcdInt a b = if b == 0 then a else gcdInt b (Builtin.modInteger a b) + +lcm : (Lift int 'a, Lift int 'b) => 'a -> 'b -> int +lcm left right = + let a : int + a = lower left + b : int + b = lower right + in + if a == 0 || b == 0 then 0 + else abs (Builtin.quotientInteger a (gcd a b) * b) + +isqrt : Lift int 'n => 'n -> int +isqrt value = + let n : int + n = lower value + in + if n < 0 then fail + else if n < 2 then n + else sqrtNewton n (sqrtUpper n 1) + +sqrtUpper : int -> int -> int +sqrtUpper remaining bound = + if remaining == 0 then bound + else sqrtUpper (Builtin.quotientInteger remaining 4) (bound * 2) + +sqrtNewton : int -> int -> int +sqrtNewton n current = + let next = Builtin.quotientInteger (current + Builtin.quotientInteger n current) 2 in + if next >= current then current else sqrtNewton n next + +isGcd : (Lift int 'a, Lift int 'b, Lift int 'g) => 'a -> 'b -> 'g -> bool +isGcd left right expected = + let a = abs left + b = abs right + g : int + g = lower expected + in + if g < 0 then False + else if g == 0 then a == 0 && b == 0 + else if g == 1 then gcdInt a b == 1 + else if Builtin.modInteger a g /= 0 then False + else if Builtin.modInteger b g /= 0 then False + else gcdInt (Builtin.quotientInteger a g) (Builtin.quotientInteger b g) == 1 + +isLcm : (Lift int 'a, Lift int 'b, Lift int 'm) => 'a -> 'b -> 'm -> bool +isLcm left right expected = + let a = abs left + b = abs right + m : int + m = lower expected + in + if m < 0 then False + else if a == 0 || b == 0 then m == 0 + else if m == 0 then False + else if Builtin.modInteger m a /= 0 then False + else if Builtin.modInteger m b /= 0 then False + else gcdInt (Builtin.quotientInteger m a) (Builtin.quotientInteger m b) == 1 + +isSqrt : (Lift int 'n, Lift int 'r) => 'n -> 'r -> bool +isSqrt value expected = + let n : int + n = lower value + root : int + root = lower expected + in + if n < 0 || root < 0 then False + else root * root <= n && n < (root + 1) * (root + 1) diff --git a/crates/nash-driver/base/src/Rational.nash b/crates/nash-driver/base/src/Rational.nash new file mode 100644 index 00000000..9c3f28cd --- /dev/null +++ b/crates/nash-driver/base/src/Rational.nash @@ -0,0 +1,97 @@ +module Rational exposing (type rational, new, numerator, denominator, add, sub, mul, div, negate, abs, compare, floor, ceiling, truncate) + +import Prelude exposing (..) +import Lift exposing (Lift) +import Eq exposing (Eq) +import Ord exposing (Ord) +import Num exposing (Num) +import Literal exposing (FromInt) +import Show exposing (Show) +import Ordering exposing (type ordering) +import Int +import Builtin + +type rational = Rational int int + +new : (Lift int 'n, Lift int 'd) => 'n -> 'd -> rational +new top bottom = + let n : int + n = lower top + d : int + d = lower bottom + in + if d == 0 then fail + else + let divisor = Int.gcd n d + sign = if d < 0 then -1 else 1 + in + Rational (Int.quot n divisor * sign) (Int.quot d divisor * sign) + +numerator : rational -> int +numerator (Rational n _) = n + +denominator : rational -> int +denominator (Rational _ d) = d + +add : rational -> rational -> rational +add = addRational + +addRational : rational -> rational -> rational +addRational (Rational a b) (Rational c d) = new (a * d + c * b) (b * d) + +sub : rational -> rational -> rational +sub = subRational + +subRational : rational -> rational -> rational +subRational (Rational a b) (Rational c d) = new (a * d - c * b) (b * d) + +mul : rational -> rational -> rational +mul = mulRational + +mulRational : rational -> rational -> rational +mulRational (Rational a b) (Rational c d) = new (a * c) (b * d) + +div : rational -> rational -> rational +div (Rational a b) (Rational c d) = new (a * d) (b * c) + +negate : rational -> rational +negate = negateRational + +negateRational : rational -> rational +negateRational (Rational n d) = Rational (Int.negate n) d + +abs : rational -> rational +abs (Rational n d) = Rational (Int.abs n) d + +compare : rational -> rational -> ordering +compare = compareRational + +compareRational : rational -> rational -> ordering +compareRational (Rational a b) (Rational c d) = Int.compare (a * d) (c * b) + +floor : rational -> int +floor (Rational n d) = Int.div n d + +ceiling : rational -> int +ceiling (Rational n d) = Int.negate (Int.div (Int.negate n) d) + +truncate : rational -> int +truncate (Rational n d) = Int.quot n d + +impl Eq rational where + eq (Rational a b) (Rational c d) = a == c && b == d + +impl Ord rational where + compare = compareRational + +impl Num rational where + add = addRational + sub = subRational + mul = mulRational + negate = negateRational + +impl FromInt rational where + fromInt n = Rational n 1 + +impl Show rational where + show (Rational n d) = Builtin.appendString (Int.toString n) (Builtin.appendString "/" (Int.toString d)) diff --git a/crates/nash-driver/tests/base_traits.rs b/crates/nash-driver/tests/base_traits.rs index 03d942df..af318671 100644 --- a/crates/nash-driver/tests/base_traits.rs +++ b/crates/nash-driver/tests/base_traits.rs @@ -129,3 +129,7 @@ base_snapshot!(representation_classes, "fixtures/base-traits/Classed.nash"); base_snapshot!(prop_helpers, "fixtures/base-traits/PropHelpers.nash"); base_snapshot!(cardano_value, "fixtures/base-traits/CardanoValue.nash"); base_snapshot!(cardano_helpers, "fixtures/base-traits/Cardano.nash"); + +base_snapshot!(integer_math, "fixtures/base-traits/IntegerMath.nash"); +base_snapshot!(rational_math, "fixtures/base-traits/Rational.nash"); +base_snapshot!(crypto_helpers, "fixtures/base-traits/Crypto.nash"); diff --git a/crates/nash-driver/tests/fixtures/base-traits/Crypto.nash b/crates/nash-driver/tests/fixtures/base-traits/Crypto.nash new file mode 100644 index 00000000..cfc9c6df --- /dev/null +++ b/crates/nash-driver/tests/fixtures/base-traits/Crypto.nash @@ -0,0 +1,155 @@ +module Main exposing (..) + +bigAbc : Bytes +bigAbc = #"616263" +bigEmpty : Bytes +bigEmpty = #"" + +edKey : bytes +edKey = #"d75a980182b10ab7d54bfed3c964073a0ee172f3daa62325af021a68f707511a" +edBigKey : Bytes +edBigKey = #"d75a980182b10ab7d54bfed3c964073a0ee172f3daa62325af021a68f707511a" + +edMessage : bytes +edMessage = #"" +edBigMessage : Bytes +edBigMessage = #"" + +edSignature : bytes +edSignature = #"e5564300c360ac729086e2cc806e828a84877f1eb8e5d974d873e065224901555fb8821590a33bacc61e39701cf9b46bd25bf5f0595bbe24655141438e7a100b" +edBigSignature : Bytes +edBigSignature = #"e5564300c360ac729086e2cc806e828a84877f1eb8e5d974d873e065224901555fb8821590a33bacc61e39701cf9b46bd25bf5f0595bbe24655141438e7a100b" + +ecdsaKey : bytes +ecdsaKey = #"032e433589dce61863199171f4d1e3fa946a5832621fcd29559940a0950f96fb6f" +ecdsaBigKey : Bytes +ecdsaBigKey = #"032e433589dce61863199171f4d1e3fa946a5832621fcd29559940a0950f96fb6f" + +ecdsaMessage : bytes +ecdsaMessage = #"e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" +ecdsaBigMessage : Bytes +ecdsaBigMessage = #"e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + +ecdsaSignature : bytes +ecdsaSignature = #"4941155e2303988a1be97a021fbaf9fe6064d05ea694bc5e89328f297154e5c63a2f3e7b5f509294a4c2e22feb697a16b792fabfebe9d0f38403b1c929836b5a" +ecdsaBigSignature : Bytes +ecdsaBigSignature = #"4941155e2303988a1be97a021fbaf9fe6064d05ea694bc5e89328f297154e5c63a2f3e7b5f509294a4c2e22feb697a16b792fabfebe9d0f38403b1c929836b5a" + +schnorrKey : bytes +schnorrKey = #"778caa53b4393ac467774d09497a87224bf9fab6f6e68b23086497324d6fd117" +schnorrBigKey : Bytes +schnorrBigKey = #"778caa53b4393ac467774d09497a87224bf9fab6f6e68b23086497324d6fd117" + +schnorrMessage : bytes +schnorrMessage = #"11" +schnorrBigMessage : Bytes +schnorrBigMessage = #"11" + +schnorrSignature : bytes +schnorrSignature = #"08a20a0afef64124649232e0693c583ab1b9934ae63b4c3511f3ae1134c6a303ea3173bfea6683bd101fa5aa5dbc1996fe7cacfc5a577d33ec14564cec2bacbf" +schnorrBigSignature : Bytes +schnorrBigSignature = #"08a20a0afef64124649232e0693c583ab1b9934ae63b4c3511f3ae1134c6a303ea3173bfea6683bd101fa5aa5dbc1996fe7cacfc5a577d33ec14564cec2bacbf" + +tests + test "sha2_256 empty" = do + assert (Crypto.sha2_256 #"" == #"e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855") + test "sha2_256 Big empty" = do + assert (Crypto.sha2_256 bigEmpty == #"e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855") + test "sha2_256 abc" = do + assert (Crypto.sha2_256 #"616263" == #"ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad") + test "sha2_256 Big abc" = do + assert (Crypto.sha2_256 bigAbc == #"ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad") + test "sha3_256 empty" = do + assert (Crypto.sha3_256 #"" == #"a7ffc6f8bf1ed76651c14756a061d662f580ff4de43b49fa82d80a4b80f8434a") + test "sha3_256 Big empty" = do + assert (Crypto.sha3_256 bigEmpty == #"a7ffc6f8bf1ed76651c14756a061d662f580ff4de43b49fa82d80a4b80f8434a") + test "sha3_256 abc" = do + assert (Crypto.sha3_256 #"616263" == #"3a985da74fe225b2045c172d6bd390bd855f086e3e9d525b46bfe24511431532") + test "sha3_256 Big abc" = do + assert (Crypto.sha3_256 bigAbc == #"3a985da74fe225b2045c172d6bd390bd855f086e3e9d525b46bfe24511431532") + test "blake2b_224 empty" = do + assert (Crypto.blake2b_224 #"" == #"836cc68931c2e4e3e838602eca1902591d216837bafddfe6f0c8cb07") + test "blake2b_224 Big empty" = do + assert (Crypto.blake2b_224 bigEmpty == #"836cc68931c2e4e3e838602eca1902591d216837bafddfe6f0c8cb07") + test "blake2b_224 abc" = do + assert (Crypto.blake2b_224 #"616263" == #"9bd237b02a29e43bdd6738afa5b53ff0eee178d6210b618e4511aec8") + test "blake2b_224 Big abc" = do + assert (Crypto.blake2b_224 bigAbc == #"9bd237b02a29e43bdd6738afa5b53ff0eee178d6210b618e4511aec8") + test "blake2b_256 empty" = do + assert (Crypto.blake2b_256 #"" == #"0e5751c026e543b2e8ab2eb06099daa1d1e5df47778f7787faab45cdf12fe3a8") + test "blake2b_256 Big empty" = do + assert (Crypto.blake2b_256 bigEmpty == #"0e5751c026e543b2e8ab2eb06099daa1d1e5df47778f7787faab45cdf12fe3a8") + test "blake2b_256 abc" = do + assert (Crypto.blake2b_256 #"616263" == #"bddd813c634239723171ef3fee98579b94964e3bb1cb3e427262c8c068d52319") + test "blake2b_256 Big abc" = do + assert (Crypto.blake2b_256 bigAbc == #"bddd813c634239723171ef3fee98579b94964e3bb1cb3e427262c8c068d52319") + test "keccak_256 empty" = do + assert (Crypto.keccak_256 #"" == #"c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470") + test "keccak_256 Big empty" = do + assert (Crypto.keccak_256 bigEmpty == #"c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470") + test "ripemd_160 empty" = do + assert (Crypto.ripemd_160 #"" == #"9c1185a5c5e9fc54612808977ee8f548b2258d31") + test "ripemd_160 Big empty" = do + assert (Crypto.ripemd_160 bigEmpty == #"9c1185a5c5e9fc54612808977ee8f548b2258d31") + test "ripemd_160 abc" = do + assert (Crypto.ripemd_160 #"616263" == #"8eb208f7e05d987a9b044a8e98c6b087f15a0bfc") + test "ripemd_160 Big abc" = do + assert (Crypto.ripemd_160 bigAbc == #"8eb208f7e05d987a9b044a8e98c6b087f15a0bfc") + test "keccak nonempty conformance" = do + assert (Crypto.keccak_256 #"AAFDC9243D3D4A096558A360CC27C8D862F0BE73DB5E88AA55" == #"6FFFA070B865BE3EE766DC2DB49B6AA55C369F7DE3703ADA2612D754145C01E6") + test "verifyEd25519Signature valid" = do + assert (Crypto.verifyEd25519Signature edKey edMessage edSignature) + test "verifyEd25519Signature Big Key" = do + assert (Crypto.verifyEd25519Signature edBigKey edMessage edSignature) + test "verifyEd25519Signature Big Message" = do + assert (Crypto.verifyEd25519Signature edKey edBigMessage edSignature) + test "verifyEd25519Signature Big Signature" = do + assert (Crypto.verifyEd25519Signature edKey edMessage edBigSignature) + test "verifyEd25519Signature all Big" = do + assert (Crypto.verifyEd25519Signature edBigKey edBigMessage edBigSignature) + test "verifyEd25519Signature invalid conformance" = do + assert (not (Crypto.verifyEd25519Signature #"6df9340c138cc188b5fe4464ebaa3f7fc206a2d55c3434707e74c9fc04e20ebb" #"5f4c8989" #"124f6fc6b0d100842769e71bd530664d888df8507df6c56dedfdb509aeb93416e26b918d38aa06305df3095697c18b2aa832eaa52edc0ae49fbae5a85e150c07")) + test "verifyEd25519Signature wrong key length" fail = do + let _ = Crypto.verifyEd25519Signature #"" edMessage edSignature in () + test "verifyEd25519Signature wrong signature length" fail = do + let _ = Crypto.verifyEd25519Signature edKey edMessage #"" in () + test "verifyEd25519Signature changed message" = do + assert (not (Crypto.verifyEd25519Signature edKey #"12" edSignature)) + test "verifyEcdsaSecp256k1Signature valid" = do + assert (Crypto.verifyEcdsaSecp256k1Signature ecdsaKey ecdsaMessage ecdsaSignature) + test "verifyEcdsaSecp256k1Signature Big Key" = do + assert (Crypto.verifyEcdsaSecp256k1Signature ecdsaBigKey ecdsaMessage ecdsaSignature) + test "verifyEcdsaSecp256k1Signature Big Message" = do + assert (Crypto.verifyEcdsaSecp256k1Signature ecdsaKey ecdsaBigMessage ecdsaSignature) + test "verifyEcdsaSecp256k1Signature Big Signature" = do + assert (Crypto.verifyEcdsaSecp256k1Signature ecdsaKey ecdsaMessage ecdsaBigSignature) + test "verifyEcdsaSecp256k1Signature all Big" = do + assert (Crypto.verifyEcdsaSecp256k1Signature ecdsaBigKey ecdsaBigMessage ecdsaBigSignature) + test "verifyEcdsaSecp256k1Signature invalid conformance" = do + assert (not (Crypto.verifyEcdsaSecp256k1Signature #"032e433589dce61863199171f4d1e3fa946a5832621fcd29559940a0950f96fb6f" #"e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" #"603e6e7bf67152188204f76f6274d38c477bdbc3954cdaa44e4ef49691a517ded5eaad30c2e69e3e9b124bcc48fa0e4d0aa0dfdb4ca9d537ea1dcd46c94b8f56")) + test "verifyEcdsaSecp256k1Signature wrong key length" fail = do + let _ = Crypto.verifyEcdsaSecp256k1Signature #"" ecdsaMessage ecdsaSignature in () + test "verifyEcdsaSecp256k1Signature wrong signature length" fail = do + let _ = Crypto.verifyEcdsaSecp256k1Signature ecdsaKey ecdsaMessage #"" in () + test "verifyEcdsaSecp256k1Signature changed message" = do + assert (not (Crypto.verifyEcdsaSecp256k1Signature ecdsaKey #"0000000000000000000000000000000000000000000000000000000000000000" ecdsaSignature)) + test "verifySchnorrSecp256k1Signature valid" = do + assert (Crypto.verifySchnorrSecp256k1Signature schnorrKey schnorrMessage schnorrSignature) + test "verifySchnorrSecp256k1Signature Big Key" = do + assert (Crypto.verifySchnorrSecp256k1Signature schnorrBigKey schnorrMessage schnorrSignature) + test "verifySchnorrSecp256k1Signature Big Message" = do + assert (Crypto.verifySchnorrSecp256k1Signature schnorrKey schnorrBigMessage schnorrSignature) + test "verifySchnorrSecp256k1Signature Big Signature" = do + assert (Crypto.verifySchnorrSecp256k1Signature schnorrKey schnorrMessage schnorrBigSignature) + test "verifySchnorrSecp256k1Signature all Big" = do + assert (Crypto.verifySchnorrSecp256k1Signature schnorrBigKey schnorrBigMessage schnorrBigSignature) + test "verifySchnorrSecp256k1Signature invalid conformance" = do + assert (not (Crypto.verifySchnorrSecp256k1Signature #"dff1d77f2a671c5f36183726db2341be58feae1da2deced843240f7b502ba659" #"243f6a8885a308d313198a2e03707344a4093822299f31d0082efa98ec4e6c89" #"fff97bd5755eeea420453a14355235d382f6472f8568a18b2f057a14602975563cc27944640ac607cd107ae10923d9ef7a73c643e166be5ebeafa34b1ac553e2")) + test "verifySchnorrSecp256k1Signature wrong key length" fail = do + let _ = Crypto.verifySchnorrSecp256k1Signature #"" schnorrMessage schnorrSignature in () + test "verifySchnorrSecp256k1Signature wrong signature length" fail = do + let _ = Crypto.verifySchnorrSecp256k1Signature schnorrKey schnorrMessage #"" in () + test "verifySchnorrSecp256k1Signature changed message" = do + assert (not (Crypto.verifySchnorrSecp256k1Signature schnorrKey #"12" schnorrSignature)) + test "ECDSA requires digest" fail = do + let _ = Crypto.verifyEcdsaSecp256k1Signature ecdsaKey #"" ecdsaSignature in () diff --git a/crates/nash-driver/tests/fixtures/base-traits/IntegerMath.nash b/crates/nash-driver/tests/fixtures/base-traits/IntegerMath.nash new file mode 100644 index 00000000..5d641cc8 --- /dev/null +++ b/crates/nash-driver/tests/fixtures/base-traits/IntegerMath.nash @@ -0,0 +1,232 @@ +module Main exposing (..) + +big : Int +big = 48 +bigGcd : Int +bigGcd = 6 +bigLcm : Int +bigLcm = 144 + +tests + test "gcd mixed inputs" = do + assert (Int.gcd big 18 == 6) + test "gcd zero" = do + assert (Int.gcd 0 0 == 0) + test "gcd signs" = do + assert (Int.gcd -48 -18 == 6) + test "lcm mixed inputs" = do + assert (Int.lcm 18 big == 144) + test "lcm zero" = do + assert (Int.lcm 0 0 == 0) + test "lcm signs" = do + assert (Int.lcm -48 18 == 144) + test "sqrt zero" = do + assert (Int.isqrt 0 == 0) + test "sqrt one" = do + assert (Int.isqrt 1 == 1) + test "sqrt rounds down" = do + assert (Int.isqrt big == 6) + test "sqrt exact" = do + assert (Int.isqrt 49 == 7) + test "sqrt rejects negative" fail = do + let _ = Int.isqrt -1 in () + test "oracle gcd 0" = do + assert (Int.gcd (0) (0) == 0) + test "oracle lcm 0" = do + assert (Int.lcm (0) (0) == 0) + test "oracle isGcd 0" = do + assert (Int.isGcd (0) (0) 0) + test "oracle isGcd rejects 0" = do + assert (not (Int.isGcd (0) (0) 1)) + test "oracle isLcm 0" = do + assert (Int.isLcm (0) (0) 0) + test "oracle isLcm rejects 0" = do + assert (not (Int.isLcm (0) (0) 1)) + test "oracle gcd 1" = do + assert (Int.gcd (0) (-18) == 18) + test "oracle lcm 1" = do + assert (Int.lcm (0) (-18) == 0) + test "oracle isGcd 1" = do + assert (Int.isGcd (0) (-18) 18) + test "oracle isGcd rejects 1" = do + assert (not (Int.isGcd (0) (-18) 19)) + test "oracle isLcm 1" = do + assert (Int.isLcm (0) (-18) 0) + test "oracle isLcm rejects 1" = do + assert (not (Int.isLcm (0) (-18) 1)) + test "oracle gcd 2" = do + assert (Int.gcd (-48) (18) == 6) + test "oracle lcm 2" = do + assert (Int.lcm (-48) (18) == 144) + test "oracle isGcd 2" = do + assert (Int.isGcd (-48) (18) 6) + test "oracle isGcd rejects 2" = do + assert (not (Int.isGcd (-48) (18) 7)) + test "oracle isLcm 2" = do + assert (Int.isLcm (-48) (18) 144) + test "oracle isLcm rejects 2" = do + assert (not (Int.isLcm (-48) (18) 145)) + test "oracle gcd 3" = do + assert (Int.gcd (48) (-18) == 6) + test "oracle lcm 3" = do + assert (Int.lcm (48) (-18) == 144) + test "oracle isGcd 3" = do + assert (Int.isGcd (48) (-18) 6) + test "oracle isGcd rejects 3" = do + assert (not (Int.isGcd (48) (-18) 7)) + test "oracle isLcm 3" = do + assert (Int.isLcm (48) (-18) 144) + test "oracle isLcm rejects 3" = do + assert (not (Int.isLcm (48) (-18) 145)) + test "oracle gcd 4" = do + assert (Int.gcd (-48) (-18) == 6) + test "oracle lcm 4" = do + assert (Int.lcm (-48) (-18) == 144) + test "oracle isGcd 4" = do + assert (Int.isGcd (-48) (-18) 6) + test "oracle isGcd rejects 4" = do + assert (not (Int.isGcd (-48) (-18) 7)) + test "oracle isLcm 4" = do + assert (Int.isLcm (-48) (-18) 144) + test "oracle isLcm rejects 4" = do + assert (not (Int.isLcm (-48) (-18) 145)) + test "oracle gcd 5" = do + assert (Int.gcd (35) (64) == 1) + test "oracle lcm 5" = do + assert (Int.lcm (35) (64) == 2240) + test "oracle isGcd 5" = do + assert (Int.isGcd (35) (64) 1) + test "oracle isGcd rejects 5" = do + assert (not (Int.isGcd (35) (64) 2)) + test "oracle isLcm 5" = do + assert (Int.isLcm (35) (64) 2240) + test "oracle isLcm rejects 5" = do + assert (not (Int.isLcm (35) (64) 2241)) + test "oracle gcd 6" = do + assert (Int.gcd ((Int.fromBytes True #"1500000000000000000000000000000015")) ((Int.fromBytes True #"2300000000000000000000000000000023")) == (Int.fromBytes True #"0700000000000000000000000000000007")) + test "oracle lcm 6" = do + assert (Int.lcm ((Int.fromBytes True #"1500000000000000000000000000000015")) ((Int.fromBytes True #"2300000000000000000000000000000023")) == (Int.fromBytes True #"6900000000000000000000000000000069")) + test "oracle isGcd 6" = do + assert (Int.isGcd ((Int.fromBytes True #"1500000000000000000000000000000015")) ((Int.fromBytes True #"2300000000000000000000000000000023")) (Int.fromBytes True #"0700000000000000000000000000000007")) + test "oracle isGcd rejects 6" = do + assert (not (Int.isGcd ((Int.fromBytes True #"1500000000000000000000000000000015")) ((Int.fromBytes True #"2300000000000000000000000000000023")) (Int.fromBytes True #"0700000000000000000000000000000008"))) + test "oracle isLcm 6" = do + assert (Int.isLcm ((Int.fromBytes True #"1500000000000000000000000000000015")) ((Int.fromBytes True #"2300000000000000000000000000000023")) (Int.fromBytes True #"6900000000000000000000000000000069")) + test "oracle isLcm rejects 6" = do + assert (not (Int.isLcm ((Int.fromBytes True #"1500000000000000000000000000000015")) ((Int.fromBytes True #"2300000000000000000000000000000023")) (Int.fromBytes True #"690000000000000000000000000000006a"))) + test "oracle sqrt 0" = do + assert (Int.isqrt 0 == 0) + test "oracle isSqrt 0" = do + assert (Int.isSqrt 0 0) + test "oracle isSqrt rejects 0" = do + assert (not (Int.isSqrt 0 1)) + test "oracle sqrt 1" = do + assert (Int.isqrt 1 == 1) + test "oracle isSqrt 1" = do + assert (Int.isSqrt 1 1) + test "oracle isSqrt rejects 1" = do + assert (not (Int.isSqrt 1 2)) + test "oracle sqrt 2" = do + assert (Int.isqrt 2 == 1) + test "oracle isSqrt 2" = do + assert (Int.isSqrt 2 1) + test "oracle isSqrt rejects 2" = do + assert (not (Int.isSqrt 2 2)) + test "oracle sqrt 3" = do + assert (Int.isqrt 3 == 1) + test "oracle isSqrt 3" = do + assert (Int.isSqrt 3 1) + test "oracle isSqrt rejects 3" = do + assert (not (Int.isSqrt 3 2)) + test "oracle sqrt 4" = do + assert (Int.isqrt 4 == 2) + test "oracle isSqrt 4" = do + assert (Int.isSqrt 4 2) + test "oracle isSqrt rejects 4" = do + assert (not (Int.isSqrt 4 3)) + test "oracle sqrt 5" = do + assert (Int.isqrt 15 == 3) + test "oracle isSqrt 5" = do + assert (Int.isSqrt 15 3) + test "oracle isSqrt rejects 5" = do + assert (not (Int.isSqrt 15 4)) + test "oracle sqrt 6" = do + assert (Int.isqrt 16 == 4) + test "oracle isSqrt 6" = do + assert (Int.isSqrt 16 4) + test "oracle isSqrt rejects 6" = do + assert (not (Int.isSqrt 16 5)) + test "oracle sqrt 7" = do + assert (Int.isqrt 17 == 4) + test "oracle isSqrt 7" = do + assert (Int.isSqrt 17 4) + test "oracle isSqrt rejects 7" = do + assert (not (Int.isSqrt 17 5)) + test "oracle sqrt 8" = do + assert (Int.isqrt (Int.fromBytes True #"0100000000000000020000000000000000") == 18446744073709551616) + test "oracle isSqrt 8" = do + assert (Int.isSqrt (Int.fromBytes True #"0100000000000000020000000000000000") 18446744073709551616) + test "oracle isSqrt rejects 8" = do + assert (not (Int.isSqrt (Int.fromBytes True #"0100000000000000020000000000000000") 18446744073709551617)) + test "oracle sqrt 9" = do + assert (Int.isqrt (Int.fromBytes True #"0100000000000000020000000000000001") == 18446744073709551617) + test "oracle isSqrt 9" = do + assert (Int.isSqrt (Int.fromBytes True #"0100000000000000020000000000000001") 18446744073709551617) + test "oracle isSqrt rejects 9" = do + assert (not (Int.isSqrt (Int.fromBytes True #"0100000000000000020000000000000001") 18446744073709551618)) + test "oracle sqrt 10" = do + assert (Int.isqrt (Int.fromBytes True #"0100000000000000020000000000000002") == 18446744073709551617) + test "oracle isSqrt 10" = do + assert (Int.isSqrt (Int.fromBytes True #"0100000000000000020000000000000002") 18446744073709551617) + test "oracle isSqrt rejects 10" = do + assert (not (Int.isSqrt (Int.fromBytes True #"0100000000000000020000000000000002") 18446744073709551618)) + test "oracle sqrt 11" = do + assert (Int.isqrt (Int.fromBytes True #"0100000000000000040000000000000003") == 18446744073709551617) + test "oracle isSqrt 11" = do + assert (Int.isSqrt (Int.fromBytes True #"0100000000000000040000000000000003") 18446744073709551617) + test "oracle isSqrt rejects 11" = do + assert (not (Int.isSqrt (Int.fromBytes True #"0100000000000000040000000000000003") 18446744073709551618)) + test "oracle sqrt 12" = do + assert (Int.isqrt (Int.fromBytes True #"01000000000000000000000000000000f600000000000000000000000000003b18") == (Int.fromBytes True #"010000000000000000000000000000007a")) + test "oracle isSqrt 12" = do + assert (Int.isSqrt (Int.fromBytes True #"01000000000000000000000000000000f600000000000000000000000000003b18") (Int.fromBytes True #"010000000000000000000000000000007a")) + test "oracle isSqrt rejects 12" = do + assert (not (Int.isSqrt (Int.fromBytes True #"01000000000000000000000000000000f600000000000000000000000000003b18") (Int.fromBytes True #"010000000000000000000000000000007b"))) + test "oracle sqrt 13" = do + assert (Int.isqrt (Int.fromBytes True #"01000000000000000000000000000000f600000000000000000000000000003b19") == (Int.fromBytes True #"010000000000000000000000000000007b")) + test "oracle isSqrt 13" = do + assert (Int.isSqrt (Int.fromBytes True #"01000000000000000000000000000000f600000000000000000000000000003b19") (Int.fromBytes True #"010000000000000000000000000000007b")) + test "oracle isSqrt rejects 13" = do + assert (not (Int.isSqrt (Int.fromBytes True #"01000000000000000000000000000000f600000000000000000000000000003b19") (Int.fromBytes True #"010000000000000000000000000000007c"))) + test "oracle sqrt 14" = do + assert (Int.isqrt (Int.fromBytes True #"01000000000000000000000000000000f600000000000000000000000000003b1a") == (Int.fromBytes True #"010000000000000000000000000000007b")) + test "oracle isSqrt 14" = do + assert (Int.isSqrt (Int.fromBytes True #"01000000000000000000000000000000f600000000000000000000000000003b1a") (Int.fromBytes True #"010000000000000000000000000000007b")) + test "oracle isSqrt rejects 14" = do + assert (not (Int.isSqrt (Int.fromBytes True #"01000000000000000000000000000000f600000000000000000000000000003b1a") (Int.fromBytes True #"010000000000000000000000000000007c"))) + test "oracle sqrt 15" = do + assert (Int.isqrt (Int.fromBytes True #"01000000000000000000000000000000f800000000000000000000000000003c0f") == (Int.fromBytes True #"010000000000000000000000000000007b")) + test "oracle isSqrt 15" = do + assert (Int.isSqrt (Int.fromBytes True #"01000000000000000000000000000000f800000000000000000000000000003c0f") (Int.fromBytes True #"010000000000000000000000000000007b")) + test "oracle isSqrt rejects 15" = do + assert (not (Int.isSqrt (Int.fromBytes True #"01000000000000000000000000000000f800000000000000000000000000003c0f") (Int.fromBytes True #"010000000000000000000000000000007c"))) + test "gcd negative expected" = do + assert (not (Int.isGcd 12 18 -6)) + test "lcm negative expected" = do + assert (not (Int.isLcm 12 18 -36)) + test "sqrt negative expected" = do + assert (not (Int.isSqrt 4 -2)) + test "sqrt negative input" = do + assert (not (Int.isSqrt -4 2)) + test "reject non-greatest common divisor" = do + assert (not (Int.isGcd 12 18 3)) + test "reject non-least common multiple" = do + assert (not (Int.isLcm 12 18 72)) + test "predicate mixed Big inputs" = do + assert (Int.isGcd big 18 bigGcd && Int.isLcm big 18 bigLcm && Int.isSqrt big bigGcd) + test "gcd predicate exhaustive small inputs" = do + assert (List.all (\a -> List.all (\b -> List.all (\g -> Int.isGcd a b g == (Int.gcd a b == g)) (List.range -1 6)) (List.range -3 4)) (List.range -3 4)) + test "lcm predicate exhaustive small inputs" = do + assert (List.all (\a -> List.all (\b -> List.all (\m -> Int.isLcm a b m == (Int.lcm a b == m)) (List.range -1 14)) (List.range -3 4)) (List.range -3 4)) + test "sqrt predicate exhaustive small inputs" = do + assert (List.all (\n -> List.all (\r -> Int.isSqrt n r == (Int.isqrt n == r)) (List.range -1 12)) (List.range 0 101)) diff --git a/crates/nash-driver/tests/fixtures/base-traits/Rational.nash b/crates/nash-driver/tests/fixtures/base-traits/Rational.nash new file mode 100644 index 00000000..a2c18ebb --- /dev/null +++ b/crates/nash-driver/tests/fixtures/base-traits/Rational.nash @@ -0,0 +1,170 @@ +module Main exposing (..) + +import Rational exposing (type rational) + +big : Int +big = lift -4 +half = Rational.new 1 2 +third = Rational.new 1 3 +literal : rational +literal = 2 + +tests + test "normalize mixed inputs and signs" = do + assert (Rational.new 2 big == Rational.new -1 2) + test "normalize zero" = do + assert (Rational.denominator (Rational.new 0 -5) == 1) + test "numerator" = do + assert (Rational.numerator (Rational.new 4 6) == 2) + test "denominator" = do + assert (Rational.denominator (Rational.new 4 6) == 3) + test "addition" = do + assert (half + third == Rational.new 5 6) + test "subtraction" = do + assert (half - third == Rational.new 1 6) + test "multiplication" = do + assert (half * third == Rational.new 1 6) + test "division" = do + assert (Rational.div half third == Rational.new 3 2) + test "negate" = do + assert (-half == Rational.new -1 2) + test "abs" = do + assert (Rational.abs (-half) == half) + test "compare" = do + assert (third < half) + test "integer literal" = do + assert (literal == Rational.new 2 1) + test "show" = do + assert (show (Rational.new 2 -4) == "-1/2") + test "floor negative" = do + assert (Rational.floor (Rational.new -7 3) == -3) + test "ceiling negative" = do + assert (Rational.ceiling (Rational.new -7 3) == -2) + test "truncate negative" = do + assert (Rational.truncate (Rational.new -7 3) == -2) + test "floor positive" = do + assert (Rational.floor (Rational.new 7 3) == 2) + test "ceiling positive" = do + assert (Rational.ceiling (Rational.new 7 3) == 3) + test "zero denominator fails" fail = do + let _ = Rational.new 1 0 in () + test "division by zero fails" fail = do + let _ = Rational.div half (Rational.new 0 1) in () + test "oracle normalize 0" = do + assert ((Rational.new (0) (-7)) == Rational.new (0) 1) + test "oracle floor 0" = do + assert (Rational.floor (Rational.new (0) (-7)) == (0)) + test "oracle ceiling 0" = do + assert (Rational.ceiling (Rational.new (0) (-7)) == (0)) + test "oracle truncate 0" = do + assert (Rational.truncate (Rational.new (0) (-7)) == (0)) + test "oracle normalize 1" = do + assert ((Rational.new (6) (-8)) == Rational.new (-3) 4) + test "oracle floor 1" = do + assert (Rational.floor (Rational.new (6) (-8)) == (-1)) + test "oracle ceiling 1" = do + assert (Rational.ceiling (Rational.new (6) (-8)) == (0)) + test "oracle truncate 1" = do + assert (Rational.truncate (Rational.new (6) (-8)) == (0)) + test "oracle normalize 2" = do + assert ((Rational.new (-6) (-8)) == Rational.new (3) 4) + test "oracle floor 2" = do + assert (Rational.floor (Rational.new (-6) (-8)) == (0)) + test "oracle ceiling 2" = do + assert (Rational.ceiling (Rational.new (-6) (-8)) == (1)) + test "oracle truncate 2" = do + assert (Rational.truncate (Rational.new (-6) (-8)) == (0)) + test "oracle normalize 3" = do + assert ((Rational.new (7) (3)) == Rational.new (7) 3) + test "oracle floor 3" = do + assert (Rational.floor (Rational.new (7) (3)) == (2)) + test "oracle ceiling 3" = do + assert (Rational.ceiling (Rational.new (7) (3)) == (3)) + test "oracle truncate 3" = do + assert (Rational.truncate (Rational.new (7) (3)) == (2)) + test "oracle normalize 4" = do + assert ((Rational.new (-7) (3)) == Rational.new (-7) 3) + test "oracle floor 4" = do + assert (Rational.floor (Rational.new (-7) (3)) == (-3)) + test "oracle ceiling 4" = do + assert (Rational.ceiling (Rational.new (-7) (3)) == (-2)) + test "oracle truncate 4" = do + assert (Rational.truncate (Rational.new (-7) (3)) == (-2)) + test "oracle normalize 5" = do + assert ((Rational.new (6) (3)) == Rational.new (2) 1) + test "oracle floor 5" = do + assert (Rational.floor (Rational.new (6) (3)) == (2)) + test "oracle ceiling 5" = do + assert (Rational.ceiling (Rational.new (6) (3)) == (2)) + test "oracle truncate 5" = do + assert (Rational.truncate (Rational.new (6) (3)) == (2)) + test "oracle normalize 6" = do + assert ((Rational.new (-6) (3)) == Rational.new (-2) 1) + test "oracle floor 6" = do + assert (Rational.floor (Rational.new (-6) (3)) == (-2)) + test "oracle ceiling 6" = do + assert (Rational.ceiling (Rational.new (-6) (3)) == (-2)) + test "oracle truncate 6" = do + assert (Rational.truncate (Rational.new (-6) (3)) == (-2)) + test "oracle normalize 7" = do + assert ((Rational.new ((Int.fromBytes True #"0100000000000000000000000000000001")) ((Int.fromBytes True #"0300000000000000000000000000000003"))) == Rational.new (1) 3) + test "oracle floor 7" = do + assert (Rational.floor (Rational.new ((Int.fromBytes True #"0100000000000000000000000000000001")) ((Int.fromBytes True #"0300000000000000000000000000000003"))) == (0)) + test "oracle ceiling 7" = do + assert (Rational.ceiling (Rational.new ((Int.fromBytes True #"0100000000000000000000000000000001")) ((Int.fromBytes True #"0300000000000000000000000000000003"))) == (1)) + test "oracle truncate 7" = do + assert (Rational.truncate (Rational.new ((Int.fromBytes True #"0100000000000000000000000000000001")) ((Int.fromBytes True #"0300000000000000000000000000000003"))) == (0)) + test "oracle add 0" = do + assert (Rational.add (Rational.new (-2) 3) (Rational.new (5) 6) == (Rational.new (1) 6)) + test "oracle sub 0" = do + assert (Rational.sub (Rational.new (-2) 3) (Rational.new (5) 6) == (Rational.new (-3) 2)) + test "oracle mul 0" = do + assert (Rational.mul (Rational.new (-2) 3) (Rational.new (5) 6) == (Rational.new (-5) 9)) + test "oracle div 0" = do + assert (Rational.div (Rational.new (-2) 3) (Rational.new (5) 6) == (Rational.new (-4) 5)) + test "oracle compare 0" = do + assert (Rational.compare (Rational.new (-2) 3) (Rational.new (5) 6) == LT) + test "oracle add 1" = do + assert (Rational.add (Rational.new (7) 12) (Rational.new (-14) 9) == (Rational.new (-35) 36)) + test "oracle sub 1" = do + assert (Rational.sub (Rational.new (7) 12) (Rational.new (-14) 9) == (Rational.new (77) 36)) + test "oracle mul 1" = do + assert (Rational.mul (Rational.new (7) 12) (Rational.new (-14) 9) == (Rational.new (-49) 54)) + test "oracle div 1" = do + assert (Rational.div (Rational.new (7) 12) (Rational.new (-14) 9) == (Rational.new (-3) 8)) + test "oracle compare 1" = do + assert (Rational.compare (Rational.new (7) 12) (Rational.new (-14) 9) == GT) + test "oracle add 2" = do + assert (Rational.add (Rational.new (0) 1) (Rational.new (-5) 7) == (Rational.new (-5) 7)) + test "oracle sub 2" = do + assert (Rational.sub (Rational.new (0) 1) (Rational.new (-5) 7) == (Rational.new (5) 7)) + test "oracle mul 2" = do + assert (Rational.mul (Rational.new (0) 1) (Rational.new (-5) 7) == (Rational.new (0) 1)) + test "oracle div 2" = do + assert (Rational.div (Rational.new (0) 1) (Rational.new (-5) 7) == (Rational.new (0) 1)) + test "oracle compare 2" = do + assert (Rational.compare (Rational.new (0) 1) (Rational.new (-5) 7) == GT) + test "oracle add 3" = do + assert (Rational.add (Rational.new ((Int.fromBytes True #"0100000000000000000000000000000001")) (Int.fromBytes True #"ffffffffffffffffffffffffffffffff")) (Rational.new ((Int.fromBytes True #"ffffffffffffffffffffffffffffffff")) (Int.fromBytes True #"0100000000000000000000000000000001")) == (Rational.new ((Int.fromBytes True #"020000000000000000000000000000000000000000000000000000000000000002")) (Int.fromBytes True #"ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"))) + test "oracle sub 3" = do + assert (Rational.sub (Rational.new ((Int.fromBytes True #"0100000000000000000000000000000001")) (Int.fromBytes True #"ffffffffffffffffffffffffffffffff")) (Rational.new ((Int.fromBytes True #"ffffffffffffffffffffffffffffffff")) (Int.fromBytes True #"0100000000000000000000000000000001")) == (Rational.new ((Int.fromBytes True #"0400000000000000000000000000000000")) (Int.fromBytes True #"ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"))) + test "oracle mul 3" = do + assert (Rational.mul (Rational.new ((Int.fromBytes True #"0100000000000000000000000000000001")) (Int.fromBytes True #"ffffffffffffffffffffffffffffffff")) (Rational.new ((Int.fromBytes True #"ffffffffffffffffffffffffffffffff")) (Int.fromBytes True #"0100000000000000000000000000000001")) == (Rational.new (1) 1)) + test "oracle div 3" = do + assert (Rational.div (Rational.new ((Int.fromBytes True #"0100000000000000000000000000000001")) (Int.fromBytes True #"ffffffffffffffffffffffffffffffff")) (Rational.new ((Int.fromBytes True #"ffffffffffffffffffffffffffffffff")) (Int.fromBytes True #"0100000000000000000000000000000001")) == (Rational.new ((Int.fromBytes True #"010000000000000000000000000000000200000000000000000000000000000001")) (Int.fromBytes True #"fffffffffffffffffffffffffffffffe00000000000000000000000000000001"))) + test "oracle compare 3" = do + assert (Rational.compare (Rational.new ((Int.fromBytes True #"0100000000000000000000000000000001")) (Int.fromBytes True #"ffffffffffffffffffffffffffffffff")) (Rational.new ((Int.fromBytes True #"ffffffffffffffffffffffffffffffff")) (Int.fromBytes True #"0100000000000000000000000000000001")) == GT) + test "oracle add 4" = do + assert (Rational.add (Rational.new (1) 2) (Rational.new (1) 2) == (Rational.new (1) 1)) + test "oracle sub 4" = do + assert (Rational.sub (Rational.new (1) 2) (Rational.new (1) 2) == (Rational.new (0) 1)) + test "oracle mul 4" = do + assert (Rational.mul (Rational.new (1) 2) (Rational.new (1) 2) == (Rational.new (1) 4)) + test "oracle div 4" = do + assert (Rational.div (Rational.new (1) 2) (Rational.new (1) 2) == (Rational.new (1) 1)) + test "oracle compare 4" = do + assert (Rational.compare (Rational.new (1) 2) (Rational.new (1) 2) == EQ) + test "zero over zero fails" fail = do + let _ = Rational.new 0 0 in () + test "zero divided by zero fails" fail = do + let _ = Rational.div (Rational.new 0 1) (Rational.new 0 1) in () diff --git a/crates/nash-driver/tests/fixtures/base-traits/TypeHelpers.nash b/crates/nash-driver/tests/fixtures/base-traits/TypeHelpers.nash index 1a35b5b2..1643f8c8 100644 --- a/crates/nash-driver/tests/fixtures/base-traits/TypeHelpers.nash +++ b/crates/nash-driver/tests/fixtures/base-traits/TypeHelpers.nash @@ -86,14 +86,6 @@ tests assert (Bytes.countSetBits bigBytes == 10) assert (Bytes.findFirstSetBit #"08" == 3) assert (Bytes.findFirstSetBit #"00" == -1) - test "hashes" = do - assert (Bytes.sha2_256 #"" == #"e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855") - assert (Bytes.sha3_256 #"" == #"a7ffc6f8bf1ed76651c14756a061d662f580ff4de43b49fa82d80a4b80f8434a") - assert (Bytes.blake2b_256 #"" == #"0e5751c026e543b2e8ab2eb06099daa1d1e5df47778f7787faab45cdf12fe3a8") - assert (Bytes.blake2b_224 #"" == #"836cc68931c2e4e3e838602eca1902591d216837bafddfe6f0c8cb07") - test "hashes 2" = do - assert (Bytes.keccak_256 #"" == #"c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470") - assert (Bytes.ripemd_160 #"" == #"9c1185a5c5e9fc54612808977ee8f548b2258d31") test "byte index out of bounds" fail = do assert (Bytes.at #"" 0 == 0) test "text helpers" = do diff --git a/crates/nash-driver/tests/fixtures/crypto/README.md b/crates/nash-driver/tests/fixtures/crypto/README.md new file mode 100644 index 00000000..a033ff4d --- /dev/null +++ b/crates/nash-driver/tests/fixtures/crypto/README.md @@ -0,0 +1,26 @@ +# Crypto reference vectors + +`../base-traits/Crypto.nash` runs the public Nash helpers against these vectors: + +- Hashes: empty input and ASCII `abc`. Python's standard `hashlib` independently + computes SHA-256, SHA3-256, Blake2b-224/256 and RIPEMD-160 (`hashlib.new`). +- Keccak: the existing Plutus conformance empty and 200-bit input vectors from + `crates/nash-plutus/tests/conformance/builtin/semantics/keccak_256`. The nonempty + vector originates in the Keccak round-3 ShortMsgKAT_256 dataset, as recorded in + that fixture. It is not the standardized SHA3 variant. +- Ed25519: Plutus conformance verifyEd25519Signature vectors 01 and 05. +- ECDSA: verifyEcdsaSecp256k1Signature vectors 01 and 03. Those fixtures explicitly + SHA-256 the empty message before verification; the Nash fixture uses the same + precomputed digest. Vector 03 checks high-S rejection. +- Schnorr: verifySchnorrSecp256k1Signature vectors 16 and 06 (BIP340), including + a valid one-byte message and a signature with odd-Y R that must not verify. + +The existing conformance suite executes all 83 signature vectors through the +Plutus evaluator. Run it with: + +```sh +cargo test -p nash-plutus --test conformance builtin_semantics_verify +``` + +Public-helper snapshots use the shared driver test runner and call compiler +functions directly. They do not invoke the Nash CLI. diff --git a/crates/nash-driver/tests/fixtures/math/README.md b/crates/nash-driver/tests/fixtures/math/README.md new file mode 100644 index 00000000..50835baf --- /dev/null +++ b/crates/nash-driver/tests/fixtures/math/README.md @@ -0,0 +1,7 @@ +# Exact math references + +Run `python3 reference.py` with Python 3.9 or later. The standard-library +`math` and `fractions.Fraction` results are asserted in the Nash source fixtures +`../base-traits/IntegerMath.nash` and `../base-traits/Rational.nash`. +They include signs, zero, 129-bit and 257-bit square boundaries, exact fractional +arithmetic, rounding and invalid inputs. No compiler CLI is invoked by tests. diff --git a/crates/nash-driver/tests/fixtures/math/reference.py b/crates/nash-driver/tests/fixtures/math/reference.py new file mode 100644 index 00000000..54e1c95d --- /dev/null +++ b/crates/nash-driver/tests/fixtures/math/reference.py @@ -0,0 +1,15 @@ +from math import gcd, lcm, isqrt, floor, ceil, trunc +from fractions import Fraction as F +import json +B = 2**128 + 1 +out = {} +out['gcd_lcm'] = [[a,b,gcd(a,b),lcm(a,b)] for a,b in [(0,0),(0,-18),(-48,18),(48,-18),(-48,-18),(35,64),(B*21,B*35)]] +out['sqrt'] = [[n,isqrt(n)] for n in [0,1,2,3,4,15,16,17] + [v for k in [2**64+1,2**128+123] for v in [k*k-1,k*k,k*k+1,(k+1)**2-1]]] +out['normalize_round'] = [[a,b,f.numerator,f.denominator,floor(f),ceil(f),trunc(f)] for a,b in [(0,-7),(6,-8),(-6,-8),(7,3),(-7,3),(6,3),(-6,3),(B,3*B)] for f in [F(a,b)]] +def pair(f): return [f.numerator,f.denominator] +out['arithmetic'] = [[pair(a),pair(b),pair(a+b),pair(a-b),pair(a*b),pair(a/b),(a>b)-(a List.all (\\b -> List.all (\\g -> Int.isGcd a b g == (Int.gcd a b == g)) (List.range -1 6)) (List.range -3 4)) (List.range -3 4))\n test \"lcm predicate exhaustive small inputs\" = do\n assert (List.all (\\a -> List.all (\\b -> List.all (\\m -> Int.isLcm a b m == (Int.lcm a b == m)) (List.range -1 14)) (List.range -3 4)) (List.range -3 4))\n test \"sqrt predicate exhaustive small inputs\" = do\n assert (List.all (\\n -> List.all (\\r -> Int.isSqrt n r == (Int.isqrt n == r)) (List.range -1 12)) (List.range 0 101))\n" +--- +gcd mixed inputs: Pass; traces: [] +gcd zero: Pass; traces: [] +gcd signs: Pass; traces: [] +lcm mixed inputs: Pass; traces: [] +lcm zero: Pass; traces: [] +lcm signs: Pass; traces: [] +sqrt zero: Pass; traces: [] +sqrt one: Pass; traces: [] +sqrt rounds down: Pass; traces: [] +sqrt exact: Pass; traces: [] +sqrt rejects negative: Pass; traces: [] +oracle gcd 0: Pass; traces: [] +oracle lcm 0: Pass; traces: [] +oracle isGcd 0: Pass; traces: [] +oracle isGcd rejects 0: Pass; traces: [] +oracle isLcm 0: Pass; traces: [] +oracle isLcm rejects 0: Pass; traces: [] +oracle gcd 1: Pass; traces: [] +oracle lcm 1: Pass; traces: [] +oracle isGcd 1: Pass; traces: [] +oracle isGcd rejects 1: Pass; traces: [] +oracle isLcm 1: Pass; traces: [] +oracle isLcm rejects 1: Pass; traces: [] +oracle gcd 2: Pass; traces: [] +oracle lcm 2: Pass; traces: [] +oracle isGcd 2: Pass; traces: [] +oracle isGcd rejects 2: Pass; traces: [] +oracle isLcm 2: Pass; traces: [] +oracle isLcm rejects 2: Pass; traces: [] +oracle gcd 3: Pass; traces: [] +oracle lcm 3: Pass; traces: [] +oracle isGcd 3: Pass; traces: [] +oracle isGcd rejects 3: Pass; traces: [] +oracle isLcm 3: Pass; traces: [] +oracle isLcm rejects 3: Pass; traces: [] +oracle gcd 4: Pass; traces: [] +oracle lcm 4: Pass; traces: [] +oracle isGcd 4: Pass; traces: [] +oracle isGcd rejects 4: Pass; traces: [] +oracle isLcm 4: Pass; traces: [] +oracle isLcm rejects 4: Pass; traces: [] +oracle gcd 5: Pass; traces: [] +oracle lcm 5: Pass; traces: [] +oracle isGcd 5: Pass; traces: [] +oracle isGcd rejects 5: Pass; traces: [] +oracle isLcm 5: Pass; traces: [] +oracle isLcm rejects 5: Pass; traces: [] +oracle gcd 6: Pass; traces: [] +oracle lcm 6: Pass; traces: [] +oracle isGcd 6: Pass; traces: [] +oracle isGcd rejects 6: Pass; traces: [] +oracle isLcm 6: Pass; traces: [] +oracle isLcm rejects 6: Pass; traces: [] +oracle sqrt 0: Pass; traces: [] +oracle isSqrt 0: Pass; traces: [] +oracle isSqrt rejects 0: Pass; traces: [] +oracle sqrt 1: Pass; traces: [] +oracle isSqrt 1: Pass; traces: [] +oracle isSqrt rejects 1: Pass; traces: [] +oracle sqrt 2: Pass; traces: [] +oracle isSqrt 2: Pass; traces: [] +oracle isSqrt rejects 2: Pass; traces: [] +oracle sqrt 3: Pass; traces: [] +oracle isSqrt 3: Pass; traces: [] +oracle isSqrt rejects 3: Pass; traces: [] +oracle sqrt 4: Pass; traces: [] +oracle isSqrt 4: Pass; traces: [] +oracle isSqrt rejects 4: Pass; traces: [] +oracle sqrt 5: Pass; traces: [] +oracle isSqrt 5: Pass; traces: [] +oracle isSqrt rejects 5: Pass; traces: [] +oracle sqrt 6: Pass; traces: [] +oracle isSqrt 6: Pass; traces: [] +oracle isSqrt rejects 6: Pass; traces: [] +oracle sqrt 7: Pass; traces: [] +oracle isSqrt 7: Pass; traces: [] +oracle isSqrt rejects 7: Pass; traces: [] +oracle sqrt 8: Pass; traces: [] +oracle isSqrt 8: Pass; traces: [] +oracle isSqrt rejects 8: Pass; traces: [] +oracle sqrt 9: Pass; traces: [] +oracle isSqrt 9: Pass; traces: [] +oracle isSqrt rejects 9: Pass; traces: [] +oracle sqrt 10: Pass; traces: [] +oracle isSqrt 10: Pass; traces: [] +oracle isSqrt rejects 10: Pass; traces: [] +oracle sqrt 11: Pass; traces: [] +oracle isSqrt 11: Pass; traces: [] +oracle isSqrt rejects 11: Pass; traces: [] +oracle sqrt 12: Pass; traces: [] +oracle isSqrt 12: Pass; traces: [] +oracle isSqrt rejects 12: Pass; traces: [] +oracle sqrt 13: Pass; traces: [] +oracle isSqrt 13: Pass; traces: [] +oracle isSqrt rejects 13: Pass; traces: [] +oracle sqrt 14: Pass; traces: [] +oracle isSqrt 14: Pass; traces: [] +oracle isSqrt rejects 14: Pass; traces: [] +oracle sqrt 15: Pass; traces: [] +oracle isSqrt 15: Pass; traces: [] +oracle isSqrt rejects 15: Pass; traces: [] +gcd negative expected: Pass; traces: [] +lcm negative expected: Pass; traces: [] +sqrt negative expected: Pass; traces: [] +sqrt negative input: Pass; traces: [] +reject non-greatest common divisor: Pass; traces: [] +reject non-least common multiple: Pass; traces: [] +predicate mixed Big inputs: Pass; traces: [] +gcd predicate exhaustive small inputs: Pass; traces: [] +lcm predicate exhaustive small inputs: Pass; traces: [] +sqrt predicate exhaustive small inputs: Pass; traces: [] diff --git a/crates/nash-driver/tests/snapshots/base_traits__rational_math.snap b/crates/nash-driver/tests/snapshots/base_traits__rational_math.snap new file mode 100644 index 00000000..a9139c33 --- /dev/null +++ b/crates/nash-driver/tests/snapshots/base_traits__rational_math.snap @@ -0,0 +1,83 @@ +--- +source: crates/nash-driver/tests/base_traits.rs +description: "module Main exposing (..)\n\nimport Rational exposing (type rational)\n\nbig : Int\nbig = lift -4\nhalf = Rational.new 1 2\nthird = Rational.new 1 3\nliteral : rational\nliteral = 2\n\ntests\n test \"normalize mixed inputs and signs\" = do\n assert (Rational.new 2 big == Rational.new -1 2)\n test \"normalize zero\" = do\n assert (Rational.denominator (Rational.new 0 -5) == 1)\n test \"numerator\" = do\n assert (Rational.numerator (Rational.new 4 6) == 2)\n test \"denominator\" = do\n assert (Rational.denominator (Rational.new 4 6) == 3)\n test \"addition\" = do\n assert (half + third == Rational.new 5 6)\n test \"subtraction\" = do\n assert (half - third == Rational.new 1 6)\n test \"multiplication\" = do\n assert (half * third == Rational.new 1 6)\n test \"division\" = do\n assert (Rational.div half third == Rational.new 3 2)\n test \"negate\" = do\n assert (-half == Rational.new -1 2)\n test \"abs\" = do\n assert (Rational.abs (-half) == half)\n test \"compare\" = do\n assert (third < half)\n test \"integer literal\" = do\n assert (literal == Rational.new 2 1)\n test \"show\" = do\n assert (show (Rational.new 2 -4) == \"-1/2\")\n test \"floor negative\" = do\n assert (Rational.floor (Rational.new -7 3) == -3)\n test \"ceiling negative\" = do\n assert (Rational.ceiling (Rational.new -7 3) == -2)\n test \"truncate negative\" = do\n assert (Rational.truncate (Rational.new -7 3) == -2)\n test \"floor positive\" = do\n assert (Rational.floor (Rational.new 7 3) == 2)\n test \"ceiling positive\" = do\n assert (Rational.ceiling (Rational.new 7 3) == 3)\n test \"zero denominator fails\" fail = do\n let _ = Rational.new 1 0 in ()\n test \"division by zero fails\" fail = do\n let _ = Rational.div half (Rational.new 0 1) in ()\n test \"oracle normalize 0\" = do\n assert ((Rational.new (0) (-7)) == Rational.new (0) 1)\n test \"oracle floor 0\" = do\n assert (Rational.floor (Rational.new (0) (-7)) == (0))\n test \"oracle ceiling 0\" = do\n assert (Rational.ceiling (Rational.new (0) (-7)) == (0))\n test \"oracle truncate 0\" = do\n assert (Rational.truncate (Rational.new (0) (-7)) == (0))\n test \"oracle normalize 1\" = do\n assert ((Rational.new (6) (-8)) == Rational.new (-3) 4)\n test \"oracle floor 1\" = do\n assert (Rational.floor (Rational.new (6) (-8)) == (-1))\n test \"oracle ceiling 1\" = do\n assert (Rational.ceiling (Rational.new (6) (-8)) == (0))\n test \"oracle truncate 1\" = do\n assert (Rational.truncate (Rational.new (6) (-8)) == (0))\n test \"oracle normalize 2\" = do\n assert ((Rational.new (-6) (-8)) == Rational.new (3) 4)\n test \"oracle floor 2\" = do\n assert (Rational.floor (Rational.new (-6) (-8)) == (0))\n test \"oracle ceiling 2\" = do\n assert (Rational.ceiling (Rational.new (-6) (-8)) == (1))\n test \"oracle truncate 2\" = do\n assert (Rational.truncate (Rational.new (-6) (-8)) == (0))\n test \"oracle normalize 3\" = do\n assert ((Rational.new (7) (3)) == Rational.new (7) 3)\n test \"oracle floor 3\" = do\n assert (Rational.floor (Rational.new (7) (3)) == (2))\n test \"oracle ceiling 3\" = do\n assert (Rational.ceiling (Rational.new (7) (3)) == (3))\n test \"oracle truncate 3\" = do\n assert (Rational.truncate (Rational.new (7) (3)) == (2))\n test \"oracle normalize 4\" = do\n assert ((Rational.new (-7) (3)) == Rational.new (-7) 3)\n test \"oracle floor 4\" = do\n assert (Rational.floor (Rational.new (-7) (3)) == (-3))\n test \"oracle ceiling 4\" = do\n assert (Rational.ceiling (Rational.new (-7) (3)) == (-2))\n test \"oracle truncate 4\" = do\n assert (Rational.truncate (Rational.new (-7) (3)) == (-2))\n test \"oracle normalize 5\" = do\n assert ((Rational.new (6) (3)) == Rational.new (2) 1)\n test \"oracle floor 5\" = do\n assert (Rational.floor (Rational.new (6) (3)) == (2))\n test \"oracle ceiling 5\" = do\n assert (Rational.ceiling (Rational.new (6) (3)) == (2))\n test \"oracle truncate 5\" = do\n assert (Rational.truncate (Rational.new (6) (3)) == (2))\n test \"oracle normalize 6\" = do\n assert ((Rational.new (-6) (3)) == Rational.new (-2) 1)\n test \"oracle floor 6\" = do\n assert (Rational.floor (Rational.new (-6) (3)) == (-2))\n test \"oracle ceiling 6\" = do\n assert (Rational.ceiling (Rational.new (-6) (3)) == (-2))\n test \"oracle truncate 6\" = do\n assert (Rational.truncate (Rational.new (-6) (3)) == (-2))\n test \"oracle normalize 7\" = do\n assert ((Rational.new ((Int.fromBytes True #\"0100000000000000000000000000000001\")) ((Int.fromBytes True #\"0300000000000000000000000000000003\"))) == Rational.new (1) 3)\n test \"oracle floor 7\" = do\n assert (Rational.floor (Rational.new ((Int.fromBytes True #\"0100000000000000000000000000000001\")) ((Int.fromBytes True #\"0300000000000000000000000000000003\"))) == (0))\n test \"oracle ceiling 7\" = do\n assert (Rational.ceiling (Rational.new ((Int.fromBytes True #\"0100000000000000000000000000000001\")) ((Int.fromBytes True #\"0300000000000000000000000000000003\"))) == (1))\n test \"oracle truncate 7\" = do\n assert (Rational.truncate (Rational.new ((Int.fromBytes True #\"0100000000000000000000000000000001\")) ((Int.fromBytes True #\"0300000000000000000000000000000003\"))) == (0))\n test \"oracle add 0\" = do\n assert (Rational.add (Rational.new (-2) 3) (Rational.new (5) 6) == (Rational.new (1) 6))\n test \"oracle sub 0\" = do\n assert (Rational.sub (Rational.new (-2) 3) (Rational.new (5) 6) == (Rational.new (-3) 2))\n test \"oracle mul 0\" = do\n assert (Rational.mul (Rational.new (-2) 3) (Rational.new (5) 6) == (Rational.new (-5) 9))\n test \"oracle div 0\" = do\n assert (Rational.div (Rational.new (-2) 3) (Rational.new (5) 6) == (Rational.new (-4) 5))\n test \"oracle compare 0\" = do\n assert (Rational.compare (Rational.new (-2) 3) (Rational.new (5) 6) == LT)\n test \"oracle add 1\" = do\n assert (Rational.add (Rational.new (7) 12) (Rational.new (-14) 9) == (Rational.new (-35) 36))\n test \"oracle sub 1\" = do\n assert (Rational.sub (Rational.new (7) 12) (Rational.new (-14) 9) == (Rational.new (77) 36))\n test \"oracle mul 1\" = do\n assert (Rational.mul (Rational.new (7) 12) (Rational.new (-14) 9) == (Rational.new (-49) 54))\n test \"oracle div 1\" = do\n assert (Rational.div (Rational.new (7) 12) (Rational.new (-14) 9) == (Rational.new (-3) 8))\n test \"oracle compare 1\" = do\n assert (Rational.compare (Rational.new (7) 12) (Rational.new (-14) 9) == GT)\n test \"oracle add 2\" = do\n assert (Rational.add (Rational.new (0) 1) (Rational.new (-5) 7) == (Rational.new (-5) 7))\n test \"oracle sub 2\" = do\n assert (Rational.sub (Rational.new (0) 1) (Rational.new (-5) 7) == (Rational.new (5) 7))\n test \"oracle mul 2\" = do\n assert (Rational.mul (Rational.new (0) 1) (Rational.new (-5) 7) == (Rational.new (0) 1))\n test \"oracle div 2\" = do\n assert (Rational.div (Rational.new (0) 1) (Rational.new (-5) 7) == (Rational.new (0) 1))\n test \"oracle compare 2\" = do\n assert (Rational.compare (Rational.new (0) 1) (Rational.new (-5) 7) == GT)\n test \"oracle add 3\" = do\n assert (Rational.add (Rational.new ((Int.fromBytes True #\"0100000000000000000000000000000001\")) (Int.fromBytes True #\"ffffffffffffffffffffffffffffffff\")) (Rational.new ((Int.fromBytes True #\"ffffffffffffffffffffffffffffffff\")) (Int.fromBytes True #\"0100000000000000000000000000000001\")) == (Rational.new ((Int.fromBytes True #\"020000000000000000000000000000000000000000000000000000000000000002\")) (Int.fromBytes True #\"ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff\")))\n test \"oracle sub 3\" = do\n assert (Rational.sub (Rational.new ((Int.fromBytes True #\"0100000000000000000000000000000001\")) (Int.fromBytes True #\"ffffffffffffffffffffffffffffffff\")) (Rational.new ((Int.fromBytes True #\"ffffffffffffffffffffffffffffffff\")) (Int.fromBytes True #\"0100000000000000000000000000000001\")) == (Rational.new ((Int.fromBytes True #\"0400000000000000000000000000000000\")) (Int.fromBytes True #\"ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff\")))\n test \"oracle mul 3\" = do\n assert (Rational.mul (Rational.new ((Int.fromBytes True #\"0100000000000000000000000000000001\")) (Int.fromBytes True #\"ffffffffffffffffffffffffffffffff\")) (Rational.new ((Int.fromBytes True #\"ffffffffffffffffffffffffffffffff\")) (Int.fromBytes True #\"0100000000000000000000000000000001\")) == (Rational.new (1) 1))\n test \"oracle div 3\" = do\n assert (Rational.div (Rational.new ((Int.fromBytes True #\"0100000000000000000000000000000001\")) (Int.fromBytes True #\"ffffffffffffffffffffffffffffffff\")) (Rational.new ((Int.fromBytes True #\"ffffffffffffffffffffffffffffffff\")) (Int.fromBytes True #\"0100000000000000000000000000000001\")) == (Rational.new ((Int.fromBytes True #\"010000000000000000000000000000000200000000000000000000000000000001\")) (Int.fromBytes True #\"fffffffffffffffffffffffffffffffe00000000000000000000000000000001\")))\n test \"oracle compare 3\" = do\n assert (Rational.compare (Rational.new ((Int.fromBytes True #\"0100000000000000000000000000000001\")) (Int.fromBytes True #\"ffffffffffffffffffffffffffffffff\")) (Rational.new ((Int.fromBytes True #\"ffffffffffffffffffffffffffffffff\")) (Int.fromBytes True #\"0100000000000000000000000000000001\")) == GT)\n test \"oracle add 4\" = do\n assert (Rational.add (Rational.new (1) 2) (Rational.new (1) 2) == (Rational.new (1) 1))\n test \"oracle sub 4\" = do\n assert (Rational.sub (Rational.new (1) 2) (Rational.new (1) 2) == (Rational.new (0) 1))\n test \"oracle mul 4\" = do\n assert (Rational.mul (Rational.new (1) 2) (Rational.new (1) 2) == (Rational.new (1) 4))\n test \"oracle div 4\" = do\n assert (Rational.div (Rational.new (1) 2) (Rational.new (1) 2) == (Rational.new (1) 1))\n test \"oracle compare 4\" = do\n assert (Rational.compare (Rational.new (1) 2) (Rational.new (1) 2) == EQ)\n test \"zero over zero fails\" fail = do\n let _ = Rational.new 0 0 in ()\n test \"zero divided by zero fails\" fail = do\n let _ = Rational.div (Rational.new 0 1) (Rational.new 0 1) in ()\n" +--- +normalize mixed inputs and signs: Pass; traces: [] +normalize zero: Pass; traces: [] +numerator: Pass; traces: [] +denominator: Pass; traces: [] +addition: Pass; traces: [] +subtraction: Pass; traces: [] +multiplication: Pass; traces: [] +division: Pass; traces: [] +negate: Pass; traces: [] +abs: Pass; traces: [] +compare: Pass; traces: [] +integer literal: Pass; traces: [] +show: Pass; traces: [] +floor negative: Pass; traces: [] +ceiling negative: Pass; traces: [] +truncate negative: Pass; traces: [] +floor positive: Pass; traces: [] +ceiling positive: Pass; traces: [] +zero denominator fails: Pass; traces: [] +division by zero fails: Pass; traces: [] +oracle normalize 0: Pass; traces: [] +oracle floor 0: Pass; traces: [] +oracle ceiling 0: Pass; traces: [] +oracle truncate 0: Pass; traces: [] +oracle normalize 1: Pass; traces: [] +oracle floor 1: Pass; traces: [] +oracle ceiling 1: Pass; traces: [] +oracle truncate 1: Pass; traces: [] +oracle normalize 2: Pass; traces: [] +oracle floor 2: Pass; traces: [] +oracle ceiling 2: Pass; traces: [] +oracle truncate 2: Pass; traces: [] +oracle normalize 3: Pass; traces: [] +oracle floor 3: Pass; traces: [] +oracle ceiling 3: Pass; traces: [] +oracle truncate 3: Pass; traces: [] +oracle normalize 4: Pass; traces: [] +oracle floor 4: Pass; traces: [] +oracle ceiling 4: Pass; traces: [] +oracle truncate 4: Pass; traces: [] +oracle normalize 5: Pass; traces: [] +oracle floor 5: Pass; traces: [] +oracle ceiling 5: Pass; traces: [] +oracle truncate 5: Pass; traces: [] +oracle normalize 6: Pass; traces: [] +oracle floor 6: Pass; traces: [] +oracle ceiling 6: Pass; traces: [] +oracle truncate 6: Pass; traces: [] +oracle normalize 7: Pass; traces: [] +oracle floor 7: Pass; traces: [] +oracle ceiling 7: Pass; traces: [] +oracle truncate 7: Pass; traces: [] +oracle add 0: Pass; traces: [] +oracle sub 0: Pass; traces: [] +oracle mul 0: Pass; traces: [] +oracle div 0: Pass; traces: [] +oracle compare 0: Pass; traces: [] +oracle add 1: Pass; traces: [] +oracle sub 1: Pass; traces: [] +oracle mul 1: Pass; traces: [] +oracle div 1: Pass; traces: [] +oracle compare 1: Pass; traces: [] +oracle add 2: Pass; traces: [] +oracle sub 2: Pass; traces: [] +oracle mul 2: Pass; traces: [] +oracle div 2: Pass; traces: [] +oracle compare 2: Pass; traces: [] +oracle add 3: Pass; traces: [] +oracle sub 3: Pass; traces: [] +oracle mul 3: Pass; traces: [] +oracle div 3: Pass; traces: [] +oracle compare 3: Pass; traces: [] +oracle add 4: Pass; traces: [] +oracle sub 4: Pass; traces: [] +oracle mul 4: Pass; traces: [] +oracle div 4: Pass; traces: [] +oracle compare 4: Pass; traces: [] +zero over zero fails: Pass; traces: [] +zero divided by zero fails: Pass; traces: [] diff --git a/crates/nash-driver/tests/snapshots/base_traits__type_helpers.snap b/crates/nash-driver/tests/snapshots/base_traits__type_helpers.snap index f3f1ea8e..c6ee4ce7 100644 --- a/crates/nash-driver/tests/snapshots/base_traits__type_helpers.snap +++ b/crates/nash-driver/tests/snapshots/base_traits__type_helpers.snap @@ -1,6 +1,6 @@ --- source: crates/nash-driver/tests/base_traits.rs -description: "module Main exposing (..)\n\nbigInt : Int\nbigInt = 7\nbigBytes : Bytes\nbigBytes = #\"abcd\"\nbigIndices : List Int\nbigIndices = lift [Builtin.iData 0, Builtin.iData 8]\nbigOption : Option Int\nbigOption = Option.Some 7\nlittleOption : option Int\nlittleOption = Some 7\nnone : option int\nnone = None\nboth : (Lift bool 'a, Lift bool 'b) => 'a -> 'b -> bool\nboth a b = Bool.and a b\nbadBig : unit -> Bool\nbadBig _ = fail\n\ntests\n test \"mixed booleans and ordering\" = do\n assert (Bool.and Bool.True True)\n assert (Bool.or False Bool.True)\n assert (not (Bool.xor Bool.True True))\n assert (not (Bool.not Bool.True))\n test \"mixed booleans and ordering 2\" = do\n assert (Bool.or Bool.True (badBig ()))\n assert (not (Bool.and False (badBig ())))\n assert (Bool.True || badBig ())\n assert (both Bool.True True)\n test \"mixed booleans and ordering 3\" = do\n assert (Ordering.invert Ordering.LT == GT)\n assert (Ordering.then_ EQ Ordering.GT == GT)\n test \"integer helpers\" = do\n assert (Int.abs (-7) == 7)\n assert (Int.abs bigInt == 7)\n assert (Int.pow 2 10 == 1024)\n assert (Int.pow bigInt 0 == 1)\n test \"every small power lookup\" = do\n assert (List.map Int.pow2 (List.range 0 9) == [1, 2, 4, 8, 16, 32, 64, 128, 256])\n test \"exact powers of two across byte boundaries\" = do\n assert (Int.pow2 0 == 1)\n assert (Int.pow2 bigInt == 128)\n assert (Int.pow2 8 == 256)\n assert (Int.pow2 9 == 512)\n assert (Int.pow2 64 == 18446744073709551616)\n assert (Int.pow2 128 == 18446744073709551616 * 18446744073709551616)\n assert (Int.pow 2 128 == Int.pow2 128)\n assert (Int.pow (-2) 9 == -512)\n assert (Int.pow 3 5 == 243)\n assert (Int.pow 0 0 == 1)\n test \"negative power of two exponent\" fail = do\n assert (Int.pow2 (-1) == 0)\n test \"integer helpers 2\" = do\n assert (Int.powMod 2 10 7 == 2)\n assert (Int.toBytes True 2 bigInt == #\"0007\")\n assert (Int.fromBytes False bigBytes == 52651)\n assert (Int.toString bigInt == \"7\")\n test \"negative exponent\" fail = do\n assert (Int.pow 2 (-1) == 0)\n test \"negative integer encoding\" fail = do\n assert (Int.toBytes True 0 (-1) == #\"\")\n test \"byte access and slicing\" = do\n assert (Bytes.length bigBytes == 2)\n assert (Bytes.at bigBytes 1 == 205)\n assert (Bytes.slice 1 1 bigBytes == #\"cd\")\n assert (Bytes.take 1 bigBytes == #\"ab\")\n test \"byte access and slicing 2\" = do\n assert (Bytes.drop 1 bigBytes == #\"cd\")\n assert (Bytes.drop (-1) bigBytes == #\"abcd\")\n assert (Bytes.take (-1) bigBytes == #\"\")\n assert (Bytes.toHex bigBytes == \"abcd\")\n test \"byte access and slicing 3\" = do\n assert (Bytes.concat [bigBytes, bigBytes] == #\"abcdabcd\")\n test \"byte logic\" = do\n assert (Bytes.and False bigBytes #\"ff00\" == #\"ab00\")\n assert (Bytes.or False bigBytes #\"00ff\" == #\"abff\")\n assert (Bytes.xor False bigBytes #\"ffff\" == #\"5432\")\n assert (Bytes.complement bigBytes == #\"5432\")\n test \"byte logic 2\" = do\n assert (Bytes.readBit #\"01\" 0)\n assert (Bytes.writeBits #\"0000\" bigIndices True == #\"0101\")\n assert (Bytes.shift #\"01\" 1 == #\"02\")\n assert (Bytes.rotate #\"80\" 1 == #\"01\")\n test \"byte logic 3\" = do\n assert (Bytes.countSetBits bigBytes == 10)\n assert (Bytes.findFirstSetBit #\"08\" == 3)\n assert (Bytes.findFirstSetBit #\"00\" == -1)\n test \"hashes\" = do\n assert (Bytes.sha2_256 #\"\" == #\"e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855\")\n assert (Bytes.sha3_256 #\"\" == #\"a7ffc6f8bf1ed76651c14756a061d662f580ff4de43b49fa82d80a4b80f8434a\")\n assert (Bytes.blake2b_256 #\"\" == #\"0e5751c026e543b2e8ab2eb06099daa1d1e5df47778f7787faab45cdf12fe3a8\")\n assert (Bytes.blake2b_224 #\"\" == #\"836cc68931c2e4e3e838602eca1902591d216837bafddfe6f0c8cb07\")\n test \"hashes 2\" = do\n assert (Bytes.keccak_256 #\"\" == #\"c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470\")\n assert (Bytes.ripemd_160 #\"\" == #\"9c1185a5c5e9fc54612808977ee8f548b2258d31\")\n test \"byte index out of bounds\" fail = do\n assert (Bytes.at #\"\" 0 == 0)\n test \"text helpers\" = do\n assert (String.toBytes \"é\" == #\"c3a9\")\n assert (String.fromBytes #\"c3a9\" == \"é\")\n assert (String.fromInt bigInt == \"7\")\n assert (String.concat [\"a\", \"b\"] == \"ab\")\n test \"text helpers 2\" = do\n assert (String.join \",\" [\"a\", \"b\", \"c\"] == \"a,b,c\")\n assert (String.join \",\" [] == \"\")\n test \"invalid UTF-8\" fail = do\n assert (String.fromBytes #\"ff\" == \"\")\n test \"option helpers preserve payloads\" = do\n assert (Option.map Builtin.unIData bigOption == Some 7)\n assert (Option.map2 Int.add littleOption bigOption == Some 14)\n assert (Option.andThen (\\x -> Option.Some x) littleOption == littleOption)\n assert (Option.isSome bigOption)\n assert (Option.apply (Some Builtin.unIData) bigOption == Some 7)\n test \"option helpers preserve payloads 2\" = do\n assert (not (Option.isSome none))\n assert (Option.unwrap bigOption == bigInt)\n assert (Option.withDefault bigInt bigOption == bigInt)\n test \"unwrap None\" fail = do\n assert (Option.unwrap none == 0)\n test \"Big scalar control inputs\" = do\n assert (List.take bigInt [1, 2] == [1, 2])\n assert (Int.pow 2 bigInt == 128)\n assert (Int.toBytes Bool.True bigInt 1 == #\"00000000000001\")\n assert (Bytes.or Bool.True #\"00\" #\"ff\" == #\"ff\")\n test \"Big array indices\" = do\n assert (Array.at (Array.fromList [5, 6]) (Builtin.iData 1) == 6)\n assert (Array.get (Array.fromList [5, 6]) bigInt == None)\n" +description: "module Main exposing (..)\n\nbigInt : Int\nbigInt = 7\nbigBytes : Bytes\nbigBytes = #\"abcd\"\nbigIndices : List Int\nbigIndices = lift [Builtin.iData 0, Builtin.iData 8]\nbigOption : Option Int\nbigOption = Option.Some 7\nlittleOption : option Int\nlittleOption = Some 7\nnone : option int\nnone = None\nboth : (Lift bool 'a, Lift bool 'b) => 'a -> 'b -> bool\nboth a b = Bool.and a b\nbadBig : unit -> Bool\nbadBig _ = fail\n\ntests\n test \"mixed booleans and ordering\" = do\n assert (Bool.and Bool.True True)\n assert (Bool.or False Bool.True)\n assert (not (Bool.xor Bool.True True))\n assert (not (Bool.not Bool.True))\n test \"mixed booleans and ordering 2\" = do\n assert (Bool.or Bool.True (badBig ()))\n assert (not (Bool.and False (badBig ())))\n assert (Bool.True || badBig ())\n assert (both Bool.True True)\n test \"mixed booleans and ordering 3\" = do\n assert (Ordering.invert Ordering.LT == GT)\n assert (Ordering.then_ EQ Ordering.GT == GT)\n test \"integer helpers\" = do\n assert (Int.abs (-7) == 7)\n assert (Int.abs bigInt == 7)\n assert (Int.pow 2 10 == 1024)\n assert (Int.pow bigInt 0 == 1)\n test \"every small power lookup\" = do\n assert (List.map Int.pow2 (List.range 0 9) == [1, 2, 4, 8, 16, 32, 64, 128, 256])\n test \"exact powers of two across byte boundaries\" = do\n assert (Int.pow2 0 == 1)\n assert (Int.pow2 bigInt == 128)\n assert (Int.pow2 8 == 256)\n assert (Int.pow2 9 == 512)\n assert (Int.pow2 64 == 18446744073709551616)\n assert (Int.pow2 128 == 18446744073709551616 * 18446744073709551616)\n assert (Int.pow 2 128 == Int.pow2 128)\n assert (Int.pow (-2) 9 == -512)\n assert (Int.pow 3 5 == 243)\n assert (Int.pow 0 0 == 1)\n test \"negative power of two exponent\" fail = do\n assert (Int.pow2 (-1) == 0)\n test \"integer helpers 2\" = do\n assert (Int.powMod 2 10 7 == 2)\n assert (Int.toBytes True 2 bigInt == #\"0007\")\n assert (Int.fromBytes False bigBytes == 52651)\n assert (Int.toString bigInt == \"7\")\n test \"negative exponent\" fail = do\n assert (Int.pow 2 (-1) == 0)\n test \"negative integer encoding\" fail = do\n assert (Int.toBytes True 0 (-1) == #\"\")\n test \"byte access and slicing\" = do\n assert (Bytes.length bigBytes == 2)\n assert (Bytes.at bigBytes 1 == 205)\n assert (Bytes.slice 1 1 bigBytes == #\"cd\")\n assert (Bytes.take 1 bigBytes == #\"ab\")\n test \"byte access and slicing 2\" = do\n assert (Bytes.drop 1 bigBytes == #\"cd\")\n assert (Bytes.drop (-1) bigBytes == #\"abcd\")\n assert (Bytes.take (-1) bigBytes == #\"\")\n assert (Bytes.toHex bigBytes == \"abcd\")\n test \"byte access and slicing 3\" = do\n assert (Bytes.concat [bigBytes, bigBytes] == #\"abcdabcd\")\n test \"byte logic\" = do\n assert (Bytes.and False bigBytes #\"ff00\" == #\"ab00\")\n assert (Bytes.or False bigBytes #\"00ff\" == #\"abff\")\n assert (Bytes.xor False bigBytes #\"ffff\" == #\"5432\")\n assert (Bytes.complement bigBytes == #\"5432\")\n test \"byte logic 2\" = do\n assert (Bytes.readBit #\"01\" 0)\n assert (Bytes.writeBits #\"0000\" bigIndices True == #\"0101\")\n assert (Bytes.shift #\"01\" 1 == #\"02\")\n assert (Bytes.rotate #\"80\" 1 == #\"01\")\n test \"byte logic 3\" = do\n assert (Bytes.countSetBits bigBytes == 10)\n assert (Bytes.findFirstSetBit #\"08\" == 3)\n assert (Bytes.findFirstSetBit #\"00\" == -1)\n test \"byte index out of bounds\" fail = do\n assert (Bytes.at #\"\" 0 == 0)\n test \"text helpers\" = do\n assert (String.toBytes \"é\" == #\"c3a9\")\n assert (String.fromBytes #\"c3a9\" == \"é\")\n assert (String.fromInt bigInt == \"7\")\n assert (String.concat [\"a\", \"b\"] == \"ab\")\n test \"text helpers 2\" = do\n assert (String.join \",\" [\"a\", \"b\", \"c\"] == \"a,b,c\")\n assert (String.join \",\" [] == \"\")\n test \"invalid UTF-8\" fail = do\n assert (String.fromBytes #\"ff\" == \"\")\n test \"option helpers preserve payloads\" = do\n assert (Option.map Builtin.unIData bigOption == Some 7)\n assert (Option.map2 Int.add littleOption bigOption == Some 14)\n assert (Option.andThen (\\x -> Option.Some x) littleOption == littleOption)\n assert (Option.isSome bigOption)\n assert (Option.apply (Some Builtin.unIData) bigOption == Some 7)\n test \"option helpers preserve payloads 2\" = do\n assert (not (Option.isSome none))\n assert (Option.unwrap bigOption == bigInt)\n assert (Option.withDefault bigInt bigOption == bigInt)\n test \"unwrap None\" fail = do\n assert (Option.unwrap none == 0)\n test \"Big scalar control inputs\" = do\n assert (List.take bigInt [1, 2] == [1, 2])\n assert (Int.pow 2 bigInt == 128)\n assert (Int.toBytes Bool.True bigInt 1 == #\"00000000000001\")\n assert (Bytes.or Bool.True #\"00\" #\"ff\" == #\"ff\")\n test \"Big array indices\" = do\n assert (Array.at (Array.fromList [5, 6]) (Builtin.iData 1) == 6)\n assert (Array.get (Array.fromList [5, 6]) bigInt == None)\n" --- mixed booleans and ordering: Pass; traces: [] mixed booleans and ordering 2: Pass; traces: [] @@ -18,8 +18,6 @@ byte access and slicing 3: Pass; traces: [] byte logic: Pass; traces: [] byte logic 2: Pass; traces: [] byte logic 3: Pass; traces: [] -hashes: Pass; traces: [] -hashes 2: Pass; traces: [] byte index out of bounds: Pass; traces: [] text helpers: Pass; traces: [] text helpers 2: Pass; traces: [] diff --git a/crates/nash-driver/tests/snapshots/testing_base__integer_predicate_costs.snap b/crates/nash-driver/tests/snapshots/testing_base__integer_predicate_costs.snap new file mode 100644 index 00000000..f318cb13 --- /dev/null +++ b/crates/nash-driver/tests/snapshots/testing_base__integer_predicate_costs.snap @@ -0,0 +1,20 @@ +--- +source: crates/nash-driver/tests/testing_base.rs +description: " assert ((Int.gcd 48 18 == 6) == True)\n assert ((Int.isGcd 48 18 6) == True)\n assert ((Int.gcd 6765 4181 == 1) == True)\n assert ((Int.isGcd 6765 4181 1) == True)\n assert ((Int.gcd 6765 4181 == 5000) == False)\n assert ((Int.isGcd 6765 4181 5000) == False)\n assert ((Int.lcm 48 18 == 144) == True)\n assert ((Int.isLcm 48 18 144) == True)\n assert ((Int.lcm 48 18 == 143) == False)\n assert ((Int.isLcm 48 18 143) == False)\n assert ((Int.lcm 48 18 == 288) == False)\n assert ((Int.isLcm 48 18 288) == False)\n assert ((Int.isqrt 48 == 6) == True)\n assert ((Int.isSqrt 48 6) == True)\n assert ((Int.isqrt (Int.fromBytes True #\"0100000000000000020000000000000001\") == 18446744073709551617) == True)\n assert ((Int.isSqrt (Int.fromBytes True #\"0100000000000000020000000000000001\") 18446744073709551617) == True)" +--- +gcd_valid compute: cpu=4980801 memory=25912 +gcd_valid predicate: cpu=8007476 memory=39622 +gcd_coprime compute: cpu=13024746 memory=58942 +gcd_coprime predicate: cpu=14523035 memory=67046 +gcd_reject compute: cpu=13104746 memory=59442 +gcd_reject predicate: cpu=5109898 memory=28410 +lcm_valid compute: cpu=7021973 memory=36019 +lcm_valid predicate: cpu=8740072 memory=42725 +lcm_reject compute: cpu=7101973 memory=36519 +lcm_reject predicate: cpu=5306231 memory=29311 +lcm_multiple compute: cpu=7101973 memory=36519 +lcm_multiple predicate: cpu=8820072 memory=43225 +sqrt_small compute: cpu=10317400 memory=49235 +sqrt_small predicate: cpu=4542248 memory=24314 +sqrt_large compute: cpu=67141424 memory=277820 +sqrt_large predicate: cpu=6236984 memory=27424 diff --git a/crates/nash-driver/tests/testing_base.rs b/crates/nash-driver/tests/testing_base.rs index 551f8cbe..95d22032 100644 --- a/crates/nash-driver/tests/testing_base.rs +++ b/crates/nash-driver/tests/testing_base.rs @@ -193,28 +193,85 @@ async fn integer_width_bound_costs() { ), ] { let source = format!(" assert (({expression}) == {expected})"); - let output = compile(&source).await; - let arena = Arena::new(); - let program = syn::parse_program(&arena, &output.uplc).unwrap(); - let result = program - .apply( - &arena, - Term::data( - &arena, - nash_plutus::data::PlutusData::integer_from(&arena, 0), - ), - ) - .eval(&arena); - assert!( - result.term.is_ok(), - "{name} width {width}: {:?}", - result.term - ); - let budget = result.info.consumed_budget; - report.push_str(&format!( - "width={width} {name}: cpu={} memory={}\n", - budget.cpu, budget.mem - )); + report.push_str(&measure_budget(&format!("width={width} {name}"), &source).await); + sources.push(source); + } + } + insta::with_settings!({description => sources.join("\n"), omit_expression => true}, { + insta::assert_snapshot!(report); + }); +} + +async fn measure_budget(name: &str, source: &str) -> String { + let output = compile(source).await; + let arena = Arena::new(); + let program = syn::parse_program(&arena, &output.uplc).unwrap(); + let result = program + .apply( + &arena, + Term::data( + &arena, + nash_plutus::data::PlutusData::integer_from(&arena, 0), + ), + ) + .eval(&arena); + assert!(result.term.is_ok(), "{name}: {:?}", result.term); + let budget = result.info.consumed_budget; + format!("{name}: cpu={} memory={}\n", budget.cpu, budget.mem) +} + +#[tokio::test] +async fn integer_predicate_costs() { + let mut report = String::new(); + let mut sources = Vec::new(); + for (name, computed, predicate, expected) in [ + ( + "gcd_valid", + "Int.gcd 48 18 == 6", + "Int.isGcd 48 18 6", + "True", + ), + ( + "gcd_coprime", + "Int.gcd 6765 4181 == 1", + "Int.isGcd 6765 4181 1", + "True", + ), + ( + "gcd_reject", + "Int.gcd 6765 4181 == 5000", + "Int.isGcd 6765 4181 5000", + "False", + ), + ( + "lcm_valid", + "Int.lcm 48 18 == 144", + "Int.isLcm 48 18 144", + "True", + ), + ( + "lcm_reject", + "Int.lcm 48 18 == 143", + "Int.isLcm 48 18 143", + "False", + ), + ( + "lcm_multiple", + "Int.lcm 48 18 == 288", + "Int.isLcm 48 18 288", + "False", + ), + ("sqrt_small", "Int.isqrt 48 == 6", "Int.isSqrt 48 6", "True"), + ( + "sqrt_large", + "Int.isqrt (Int.fromBytes True #\"0100000000000000020000000000000001\") == 18446744073709551617", + "Int.isSqrt (Int.fromBytes True #\"0100000000000000020000000000000001\") 18446744073709551617", + "True", + ), + ] { + for (kind, expression) in [("compute", computed), ("predicate", predicate)] { + let source = format!(" assert (({expression}) == {expected})"); + report.push_str(&measure_budget(&format!("{name} {kind}"), &source).await); sources.push(source); } } diff --git a/docs/stdlib.md b/docs/stdlib.md index 66ea51b1..4e5e7898 100644 --- a/docs/stdlib.md +++ b/docs/stdlib.md @@ -44,6 +44,8 @@ crates/nash-driver/base/ Option.nash option / Option Ordering.nash ordering / Ordering Int.nash int functions; Big Int + Rational.nash normalized little rational arithmetic + Crypto.nash hashing and signature builtin helpers Bytes.nash bytes functions; Big Bytes String.nash string (little only; no Big twin) List.nash list functions; Big List @@ -97,7 +99,7 @@ import Functor exposing (Functor) import Applicative exposing (Applicative) import Monad exposing (Monad) import Lift exposing (Lift) -import Data exposing (ToData, FromData, Validate) +import Data exposing (ToData, FromData, Validate, Decode) import Literal exposing (FromInt, FromString, FromBytes, FromBool, FromUnit) import Bool exposing (Bool, not, and, or, xor) import Unit exposing (Unit) @@ -106,6 +108,8 @@ import Ordering exposing (Ordering, type ordering(..)) import Cons exposing (type cons(..)) import Builtin import Int +import Rational +import Crypto import Bytes import String import List @@ -680,11 +684,91 @@ abs : Lift int 'n => 'n -> int pow : (Lift int 'n, Lift int 'e) => 'n -> 'e -> int pow2 : Lift int 'e => 'e -> int powMod : (Lift int 'n, Lift int 'e, Lift int 'm) => 'n -> 'e -> 'm -> int +gcd : (Lift int 'a, Lift int 'b) => 'a -> 'b -> int +lcm : (Lift int 'a, Lift int 'b) => 'a -> 'b -> int +isqrt : Lift int 'n => 'n -> int +isGcd : (Lift int 'a, Lift int 'b, Lift int 'g) => 'a -> 'b -> 'g -> bool +isLcm : (Lift int 'a, Lift int 'b, Lift int 'm) => 'a -> 'b -> 'm -> bool +isSqrt : (Lift int 'n, Lift int 'r) => 'n -> 'r -> bool toBytes : (Lift bool 'b, Lift int 's, Lift int 'n) => 'b -> 's -> 'n -> bytes fromBytes : (Lift bool 'b, Lift bytes 'v) => 'b -> 'v -> int toString : Lift int 'n => 'n -> string ``` +gcd and lcm return nonnegative integers; gcd(0,0) and lcm(0,n) are zero. +isqrt is the floor square root and fails for negative input. The implementation +uses a power-of-two upper bound followed by integer Newton steps. + +The predicate helpers put the expected result last, matching List.isLength. +isGcd and isLcm verify the greatest/least result, not merely a common divisor +or multiple. They reject negative expected values. isSqrt checks +`r*r <= n && n < (r+1)*(r+1)` and returns False for negative n or r. It verifies +the floor root, so isSqrt 48 6 is True. GCD/LCM early rejection can save work; +valid candidates still require proving coprimality. Budget snapshots compare +each predicate with computing the result and comparing it. They measure complete +compiled validators, including argument binding, comparisons and assertions. + +### `Rational` + +`Rational.rational` is a little type with a hidden constructor. `Rational.new` +accepts independently Big/little integer numerator and denominator, rejects a +zero denominator, reduces by gcd and makes the denominator positive. Zero is +always 0/1. This arithmetic type is separate from Cardano.Tx.Rational, which +preserves the ledger's constructor encoding without arithmetic normalization. + +```nash +new : (Lift int 'n, Lift int 'd) => 'n -> 'd -> rational +numerator : rational -> int +denominator : rational -> int +add : rational -> rational -> rational +sub : rational -> rational -> rational +mul : rational -> rational -> rational +div : rational -> rational -> rational +negate : rational -> rational +abs : rational -> rational +compare : rational -> rational -> ordering +floor : rational -> int +ceiling : rational -> int +truncate : rational -> int +``` + +Eq, Ord, Num, FromInt and Show instances support equality, ordering, arithmetic +operators, integer literals and `show` as normalized `numerator/denominator`. +Division uses Rational.div and fails when the divisor is zero. Floor rounds +toward negative infinity, ceiling toward positive infinity, and truncate toward +zero. All arithmetic is exact integer arithmetic without floating-point conversion. + +### `Crypto` + +Hash helpers live in Crypto rather than Bytes. Crypto is implicitly available as a qualified module. Every argument accepts +bytes or Bytes independently; hashes return little bytes and verification returns +little bool. Helpers only call lower and the corresponding real Plutus builtin. +They do not encode Data, hash messages implicitly, repair malformed lengths or +catch builtin failures. Builtin availability follows the compiler's target policy. + +| Helper | Inputs and result | +|---|---| +| sha2_256, sha3_256, blake2b_224, blake2b_256, keccak_256, ripemd_160 | bytes/Bytes -> bytes | +| verifyEd25519Signature | key, message, signature -> bool | +| verifyEcdsaSecp256k1Signature | key, digest, signature -> bool | +| verifySchnorrSecp256k1Signature | key, message, signature -> bool | + +Ed25519 uses a 32-byte public key and 64-byte signature, with an arbitrary-length +message. ECDSA uses a SEC1 public key, a precomputed 32-byte digest and compact +64-byte r||s signature; high-S signatures return False. Use compressed 33-byte +keys for ledger portability; the current Nash runtime also parses uncompressed +65-byte SEC1 keys. Schnorr uses a 32-byte x-only public key, 64-byte signature and +an arbitrary-length message (the vectors include a one-byte message). + +Wrong required lengths and invalid SEC1/x-only key encodings fail execution; +a well-formed signature that does not verify returns False. Keccak-256 and +SHA3-256 are different algorithms. Existing BLS operations remain in Builtin; +Crypto adds no aliases for operations without Big byte inputs. + +Known-answer fixtures cover all nine helpers, nonempty hashes, valid and invalid +signatures, independently mixed Big/little arguments and malformed inputs. Their +provenance is in `crates/nash-driver/tests/fixtures/crypto/README.md`. + ### `Bytes` ```elm @@ -698,12 +782,6 @@ slice : (Lift int 's, Lift int 'n, Lift bytes 'b) => 's -> 'n -> 'b -> bytes take : (Lift int 'n, Lift bytes 'b) => 'n -> 'b -> bytes drop : (Lift int 'n, Lift bytes 'b) => 'n -> 'b -> bytes concat : (Lift (list 'b) ('f 'b), Lift bytes 'b) => ('f 'b) -> bytes -sha2_256 : Lift bytes 'b => 'b -> bytes -sha3_256 : Lift bytes 'b => 'b -> bytes -blake2b_256 : Lift bytes 'b => 'b -> bytes -blake2b_224 : Lift bytes 'b => 'b -> bytes -keccak_256 : Lift bytes 'b => 'b -> bytes -ripemd_160 : Lift bytes 'b => 'b -> bytes complement : Lift bytes 'b => 'b -> bytes countSetBits : Lift bytes 'b => 'b -> int findFirstSetBit : Lift bytes 'b => 'b -> int diff --git a/plans/12-stdlib.md b/plans/12-stdlib.md index bac6a458..0ef6e9f1 100644 --- a/plans/12-stdlib.md +++ b/plans/12-stdlib.md @@ -3,7 +3,8 @@ Goal: write `crates/nash-driver/base/` in Nash per [docs/stdlib.md](../docs/stdlib.md) and wire it into the compiler: embedded package, default imports, the synthetic `Builtin` module, the trait modules and twin types, the type -modules, decoders, `Prop`, `Test`, `Ast`, `Cardano.*`. +modules, Data traits, `Prop`, `Test`, `Ast`, `Cardano.*`, integer math, +`Rational` and `Crypto`. Prerequisites, by chunk (each chunk's Nash must type-check with the compiler features available when it lands): @@ -17,6 +18,8 @@ compiler features available when it lands): | 8 `Prop`, 9 `Test` | plans/03, plans/10 (tests block, sequencing `do`, `prng` protocol, runner) | | 10 `Ast`, `Derive` | plans/11 chunk 4 (tags) and chunk 10 | | 11 `Cardano.*` | chunk 7 | +| 12 integer math and Rational | chunks 5–6 | +| 13 Crypto | chunks 3 and 6; existing Plutus crypto builtins | Module layout is docs/stdlib.md "Layout": one file per trait, one module per type pair named by the uppercase name, helpers accepting either outer representation @@ -55,8 +58,8 @@ replace the tested PRNG, replay, label, and assertion protocols. ## Current status -Reconciled with chunk 11 implementation (2026-09-25). Base currently -ships 32 embedded Nash modules. Plan 12 remains incomplete in `SPEC.md`. +Reconciled with chunks 12 and 13 implementation (2026-09-25). Base currently +ships 34 embedded Nash modules. Plan 12 remains incomplete in `SPEC.md`. | Chunk | Status | Remaining work | |---|---|---| @@ -70,7 +73,9 @@ ships 32 embedded Nash modules. Plan 12 remains incomplete in `SPEC.md`. | 8 Prop | complete | none | | 9 Test | complete through Plan 10 | preserve existing runner protocol | | 10 Ast / Derive | not implemented | requires Plan 11 | -| 11 Cardano | implemented | Haskell codec fixtures, native value helpers, interval comparisons | +| 11 Cardano | complete | none | +| 12 integer math / Rational | complete | none | +| 13 Crypto | complete | none | Chunk 8 records its historical validation; chunk 11 adds executed Haskell comparisons and reruns the full workspace checks. @@ -239,7 +244,8 @@ documented builtin failure behavior. Tests in `base_traits.rs` compile embedded Base, then execute Nash fixtures for Lists and TypeHelpers as well as the existing trait fixtures. They cover mixed representations, preserved elements, callback results, empty and invalid -inputs, stable ordering, hashes, UTF-8, and lazy booleans. No CLI subprocesses. +inputs, stable ordering, UTF-8, and lazy booleans. Hash coverage now lives in +chunk 13's Crypto fixture. No CLI subprocesses. **Validation:** formatting and strict Clippy passed; 3,386 workspace tests passed, 3 ignored. Snapshot checks passed with no unreferenced snapshots. @@ -488,6 +494,54 @@ reproduces all ten committed CBOR fixtures byte for byte. --- +## Chunk 12: integer math and `Rational` — complete + +- [x] Add Int.gcd, Int.lcm and Int.isqrt, accepting Big/little integers and returning int. +- [x] Use nonnegative gcd/lcm; gcd(0,0) = lcm(0,n) = 0. isqrt returns the floor + of the nonnegative square root and fails for a negative input. +- [x] Add isGcd/isLcm/isSqrt with input arguments first and expected result last. + Reject negative expected values; isSqrt returns False for negative input. + Verify equivalence and benchmark CPU/memory against computing then comparing. +- [x] Add a little Rational.rational type with hidden constructor. Rational.new + accepts Big/little numerator and denominator, reduces by gcd and keeps the + denominator positive. Zero normalizes to 0/1; zero denominator fails. +- [x] Supply numerator/denominator, add/sub/mul/div, negate/abs, comparison, + floor/ceiling/truncate and little Eq/Ord/Num/FromInt/Show instances. +- [x] Keep this normalized arithmetic type separate from Cardano.Tx.Rational's + exact ledger wire representation. +- [x] Verify signs, zeros, large integers, square boundaries and exact fractions + against Python's math/fractions reference using source snapshot tests. +- [x] Update implicit imports, docs and changesets; run the workspace checks. + +## Chunk 13: `Crypto` — complete + +- [x] Move the existing Bytes hash helpers into Crypto and provide thin Nash helpers for SHA-256, SHA3-256, Blake2b-224/256, + Keccak-256, RIPEMD-160, Ed25519, ECDSA-secp256k1 and Schnorr-secp256k1. +- [x] Accept Big/little bytes independently for each input; return little bytes + or bool. Call the real Plutus builtin directly, without implicit hashing or + Data encoding, retries, length repair or failure interception. +- [x] Document key/signature formats, message/prehash expectations and builtin + malformed-input behavior. Existing BLS operations remain available through + Builtin; this chunk does not duplicate APIs without Big byte inputs. +- [x] Execute known-answer snapshots for every helper, valid/invalid signatures, + mixed Big/little arguments and malformed input. Keep vector provenance. +- [x] Update implicit imports, docs and changesets; run the workspace checks. + +Implementation: Int.nash, Rational.nash and Crypto.nash. The six hash wrappers +moved from Bytes into Crypto; their duplicate general-helper snapshots were +removed. Rational and Crypto are implicit qualified modules. + +Validation: Python math/Fraction exact-reference fixtures, existing Plutus +signature conformance vectors, 241 Nash tests including small-range predicate +equivalence checks, and full-validator budget snapshots. Formatting, strict +all-target/all-feature Clippy and the full workspace suite passed: 3,456 tests, +3 ignored. The separate signature conformance run passed all 83 cases. + +Budget results in `testing_base__integer_predicate_costs.snap`: large isSqrt +used 6,236,984 CPU / 27,424 memory versus 67,141,424 / 277,820 for computing and +comparing. isGcd/isLcm save work on early rejection but can cost more for valid +candidates; no general claim of cheaper verification is made. + ## Test harness - `crates/nash-driver/tests/bundled_base.rs` compiles every embedded module and import-free applications through the driver library, without invoking the CLI. From 09ca8317595c7afd41a9f55394da9c5ba95e5280 Mon Sep 17 00:00:00 2001 From: microproofs Date: Fri, 25 Sep 2026 18:26:30 -0400 Subject: [PATCH 07/10] refactor(base): share pipes for Data conversions Signed-off-by: microproofs --- .sampo/changesets/data-pipes.md | 5 +++++ crates/nash-codegen/src/build/tests/source.rs | 1 + crates/nash-codegen/src/tests/integration.rs | 4 ++++ crates/nash-driver/base/src/Data.nash | 13 +++++++++++-- crates/nash-driver/base/src/Function.nash | 10 ++++++++++ crates/nash-driver/base/src/Prelude.nash | 5 +++-- docs/stdlib.md | 9 +++++++-- plans/12-stdlib.md | 2 +- 8 files changed, 42 insertions(+), 7 deletions(-) create mode 100644 .sampo/changesets/data-pipes.md create mode 100644 crates/nash-driver/base/src/Function.nash diff --git a/.sampo/changesets/data-pipes.md b/.sampo/changesets/data-pipes.md new file mode 100644 index 00000000..8c235a55 --- /dev/null +++ b/.sampo/changesets/data-pipes.md @@ -0,0 +1,5 @@ +--- +cargo/nash-driver: patch +--- + +Use pipe operators for list Data conversions and share their implementations through Function. diff --git a/crates/nash-codegen/src/build/tests/source.rs b/crates/nash-codegen/src/build/tests/source.rs index 7b517102..7a8da9ec 100644 --- a/crates/nash-codegen/src/build/tests/source.rs +++ b/crates/nash-codegen/src/build/tests/source.rs @@ -1,4 +1,5 @@ const BASE_MODULES: &[&str] = &[ + include_str!("../../../../nash-driver/base/src/Function.nash"), include_str!("../../../../nash-driver/base/src/Lift.nash"), include_str!("../../../../nash-driver/base/src/Bool.nash"), include_str!("../../../../nash-driver/base/src/Unit.nash"), diff --git a/crates/nash-codegen/src/tests/integration.rs b/crates/nash-codegen/src/tests/integration.rs index 36f70017..b22f8d7f 100644 --- a/crates/nash-codegen/src/tests/integration.rs +++ b/crates/nash-codegen/src/tests/integration.rs @@ -28,6 +28,10 @@ fn compile_selected( let mut interfaces = BTreeMap::from([("Builtin", nash_can::kinds::builtin_interface(bump))]); let mut modules = Vec::new(); for (text, package) in crate::harness::dependency_order([ + ( + include_str!("../../../nash-driver/base/src/Function.nash"), + Some(primitives::BASE), + ), ( include_str!("../../tests/fixtures/VestingLiteral.nash"), Some(primitives::BASE), diff --git a/crates/nash-driver/base/src/Data.nash b/crates/nash-driver/base/src/Data.nash index bc45d300..bfa52ce6 100644 --- a/crates/nash-driver/base/src/Data.nash +++ b/crates/nash-driver/base/src/Data.nash @@ -1,6 +1,7 @@ module Data exposing (ToData, FromData, Validate, Decode, serialise, tag, fields) import Builtin +import Function exposing ((|>)) import Literal import Eq exposing (Eq) import Option exposing (Option, type option(..)) @@ -158,10 +159,18 @@ impl Decode unit where _ -> None impl ToData 'a => ToData (list 'a) where - toData values = Primitive.coerce (Builtin.listData (mapList toData values)) + toData values = + values + |> mapList toData + |> Builtin.listData + |> Primitive.coerce impl FromData 'a => FromData (list 'a) where - fromData value = mapList fromData (Builtin.unListData (Primitive.coerce value)) + fromData value = + value + |> Primitive.coerce + |> Builtin.unListData + |> mapList fromData impl Decode 'a => Decode (list 'a) where decode value = case value of diff --git a/crates/nash-driver/base/src/Function.nash b/crates/nash-driver/base/src/Function.nash new file mode 100644 index 00000000..e4dcfb8a --- /dev/null +++ b/crates/nash-driver/base/src/Function.nash @@ -0,0 +1,10 @@ +module Function exposing (applyForward, applyBackward, (|>), (<|)) + +infix left 0 (|>) = applyForward +infix right 0 (<|) = applyBackward + +applyForward : 'a -> ('a -> 'b) -> 'b +applyForward x f = f x + +applyBackward : ('a -> 'b) -> 'a -> 'b +applyBackward f x = f x diff --git a/crates/nash-driver/base/src/Prelude.nash b/crates/nash-driver/base/src/Prelude.nash index a523686b..7af0b180 100644 --- a/crates/nash-driver/base/src/Prelude.nash +++ b/crates/nash-driver/base/src/Prelude.nash @@ -1,6 +1,7 @@ module Prelude exposing (..) import Builtin +import Function import Primitive exposing (type bool(..)) import Bool exposing (and, or) @@ -45,10 +46,10 @@ always : 'a -> 'b -> 'a always x _ = x applyForward : 'a -> ('a -> 'b) -> 'b -applyForward x f = f x +applyForward = Function.applyForward applyBackward : ('a -> 'b) -> 'a -> 'b -applyBackward f x = f x +applyBackward = Function.applyBackward composeLeft : ('b -> 'c) -> ('a -> 'b) -> 'a -> 'c composeLeft g f x = g (f x) diff --git a/docs/stdlib.md b/docs/stdlib.md index 4e5e7898..e24bbf91 100644 --- a/docs/stdlib.md +++ b/docs/stdlib.md @@ -25,6 +25,7 @@ The layout includes planned library modules as well as the current foundation. ``` crates/nash-driver/base/ src/ + Function.nash dependency-free forward and backward application Prelude.nash infix declarations, basics Eq.nash trait Eq + impls for compiler-known types Ord.nash trait Ord @@ -175,6 +176,9 @@ table binds (`|>`, `<|`, `<<`, `>>`, `::` targets, plus `identity`/`always`), and the prelude impls: impls for tuples, which count as defined in `nash/base` under the orphan rule. Everything else lives in the trait modules or the type modules. `Prelude` imports the traits it needs and `Bool` (for `&&`/`||`). +The pipe implementations live in dependency-free `Function`; `Prelude` keeps +its public pipe operators and application functions. Base modules below +`Prelude` can import the pipes from `Function` without an import cycle. The application default-import catalog exposes all shipped traits independently; Base modules use explicit imports to keep the bootstrap graph acyclic. The `::` target is named `prepend` (not `cons`, which is the `Cons` @@ -195,6 +199,7 @@ import Applicative exposing (Applicative) import Monad exposing (Monad) import Bool exposing (and, or) import Builtin +import Function infix left 0 (|>) = applyForward infix right 0 (<|) = applyBackward @@ -226,10 +231,10 @@ always : 'a -> 'b -> 'a always x _ = x applyForward : 'a -> ('a -> 'b) -> 'b -applyForward x f = f x +applyForward = Function.applyForward applyBackward : ('a -> 'b) -> 'a -> 'b -applyBackward f x = f x +applyBackward = Function.applyBackward composeLeft : ('b -> 'c) -> ('a -> 'b) -> 'a -> 'c composeLeft g f x = g (f x) diff --git a/plans/12-stdlib.md b/plans/12-stdlib.md index 0ef6e9f1..e7fe4536 100644 --- a/plans/12-stdlib.md +++ b/plans/12-stdlib.md @@ -59,7 +59,7 @@ replace the tested PRNG, replay, label, and assertion protocols. ## Current status Reconciled with chunks 12 and 13 implementation (2026-09-25). Base currently -ships 34 embedded Nash modules. Plan 12 remains incomplete in `SPEC.md`. +ships 35 embedded Nash modules. Plan 12 remains incomplete in `SPEC.md`. | Chunk | Status | Remaining work | |---|---|---| From 100e54a54c302182d405a473cd794a40240a9d68 Mon Sep 17 00:00:00 2001 From: microproofs Date: Fri, 25 Sep 2026 19:06:47 -0400 Subject: [PATCH 08/10] refactor(base): pipe one-argument call chains Signed-off-by: microproofs --- .sampo/changesets/base-pipes.md | 5 + ...ins__data_map_rejects_malformed_value.snap | 235 ++--- ...nested_list_rejects_malformed_element.snap | 147 +-- ...ins__validate_data_accepts_nested_map.snap | 219 ++--- ...terns__expect_head3_cast_data_no_tail.snap | 229 ++--- ...tterns__expect_head_cast_data_no_tail.snap | 187 ++-- ...erns__expect_head_cast_data_with_tail.snap | 207 ++-- ...__validate_list_rejects_wrong_element.snap | 110 ++- ...terns__validate_map_rejects_wrong_key.snap | 163 ++-- ...rs_and_string_parentheses_1_V3_Silent.snap | 906 +++++++++--------- ..._order_and_lazy_branches_1_V3_Verbose.snap | 57 +- ...ative_layouts_specialize_1_V3_Verbose.snap | 178 ++-- ...raw_without_running_body_1_V3_Verbose.snap | 57 +- ...nitialize_later_generators_V3_Verbose.snap | 240 ++--- ...lected_target_is_enforced_2_V1_Silent.snap | 179 ++-- ...lected_target_is_enforced_2_V2_Silent.snap | 179 ++-- ...lected_target_is_enforced_3_V3_Silent.snap | 179 ++-- ...and_power_assert_payloads_1_V3_Silent.snap | 57 +- crates/nash-driver/base/src/Array.nash | 6 +- crates/nash-driver/base/src/Bytes.nash | 26 +- .../nash-driver/base/src/Cardano/Value.nash | 15 +- crates/nash-driver/base/src/Cons.nash | 6 +- crates/nash-driver/base/src/Crypto.nash | 31 +- crates/nash-driver/base/src/Data.nash | 40 +- crates/nash-driver/base/src/Int.nash | 10 +- crates/nash-driver/base/src/List.nash | 35 +- crates/nash-driver/base/src/Map.nash | 11 +- crates/nash-driver/base/src/Prop.nash | 5 +- crates/nash-driver/base/src/Show.nash | 33 +- crates/nash-driver/base/src/String.nash | 6 +- 30 files changed, 2014 insertions(+), 1744 deletions(-) create mode 100644 .sampo/changesets/base-pipes.md diff --git a/.sampo/changesets/base-pipes.md b/.sampo/changesets/base-pipes.md new file mode 100644 index 00000000..a70b4854 --- /dev/null +++ b/.sampo/changesets/base-pipes.md @@ -0,0 +1,5 @@ +--- +cargo/nash-driver: patch +--- + +Use pipe operators for one-argument call chains across the base library. diff --git a/crates/nash-codegen/src/build/tests/source/snapshots/nash_codegen__build__tests__source__builtins__data_map_rejects_malformed_value.snap b/crates/nash-codegen/src/build/tests/source/snapshots/nash_codegen__build__tests__source__builtins__data_map_rejects_malformed_value.snap index 0b2a5931..7365031d 100644 --- a/crates/nash-codegen/src/build/tests/source/snapshots/nash_codegen__build__tests__source__builtins__data_map_rejects_malformed_value.snap +++ b/crates/nash-codegen/src/build/tests/source/snapshots/nash_codegen__build__tests__source__builtins__data_map_rejects_malformed_value.snap @@ -3,129 +3,134 @@ source: crates/nash-codegen/src/build/tests/source/builtins.rs description: "module Main exposing (..)\nimport Primitive exposing (..)\nimport Builtin exposing (..)\nimport Data exposing (validate)\ndecoded : Map Bytes (List Int)\ndecoded = validate (Map [mkPairData (B #\"aa\") (List [B #\"bb\"])])\nmain = nullList (unMapData decoded)\n" --- --- core -let mapList#11 : ('erased -> pair Data Data) -> list 'erased -> list (pair Data Data) = \(f#15 : 'erased -> pair Data Data) values#16 : list 'erased -> (\self#52 : 'erased -> self#52 self#52) (\self#52 : 'erased values#16 : list 'erased -> let match#19 : list 'erased = values#16 in -case@List match#19 of +let applyForward#11 : 'erased -> ('erased -> 'erased) -> 'erased = \x#16 : 'erased (f#17 : 'erased -> 'erased) -> f#17 x#16 in +let mapList#12 : ('erased -> pair Data Data) -> list 'erased -> list (pair Data Data) = \(f#18 : 'erased -> pair Data Data) values#19 : list 'erased -> (\self#54 : 'erased -> self#54 self#54) (\self#54 : 'erased values#19 : list 'erased -> let match#22 : list 'erased = values#19 in +case@List match#22 of Nil -> (con (list (pair data data)) []) - Cons field#20 field#21 -> let first#17 : 'erased = field#20 in - let rest#18 : list 'erased = field#21 in - mkCons (f#15 first#17) (self#52 self#52 rest#18)) values#16 in -let validate#25 : Data -> Bytes = \value#30 : Data -> let candidate#31 : Bytes = (\coerce#33 : 'erased -> coerce#33) value#30 in -let destruct#32 : bytes = unBData candidate#31 in -candidate#31 in -let mapList#36 : ('erased -> Data) -> list 'erased -> list Data = \(f#39 : 'erased -> Data) values#40 : list 'erased -> (\self#51 : 'erased -> self#51 self#51) (\self#51 : 'erased values#40 : list 'erased -> let match#43 : list 'erased = values#40 in -case@List match#43 of + Cons field#23 field#24 -> let first#20 : 'erased = field#23 in + let rest#21 : list 'erased = field#24 in + mkCons (f#18 first#20) (self#54 self#54 rest#21)) values#19 in +let validate#28 : Data -> Bytes = \value#33 : Data -> let candidate#34 : Bytes = (\coerce#36 : 'erased -> coerce#36) value#33 in +let destruct#35 : bytes = unBData candidate#34 in +candidate#34 in +let mapList#39 : ('erased -> Data) -> list 'erased -> list Data = \(f#42 : 'erased -> Data) values#43 : list 'erased -> (\self#53 : 'erased -> self#53 self#53) (\self#53 : 'erased values#43 : list 'erased -> let match#46 : list 'erased = values#43 in +case@List match#46 of Nil -> (con (list data) []) - Cons field#44 field#45 -> let first#41 : 'erased = field#44 in - let rest#42 : list 'erased = field#45 in - mkCons (f#39 first#41) (self#51 self#51 rest#42)) values#40 in -let validate#37 : Data -> Int = \value#46 : Data -> let candidate#47 : Int = (\coerce#49 : 'erased -> coerce#49) value#46 in -let destruct#48 : int = unIData candidate#47 in -candidate#47 in -let validate#26 : Data -> List Data = \value#34 : Data -> let candidate#35 : List Data = (\coerce#38 : 'erased -> coerce#38) value#34 in -let projection#50 : list Data = unListData candidate#35 in -listData (mapList#36 validate#37 projection#50) in -let pairValidate#12 : pair Data Data -> pair Data Data = \arg#22 : pair Data Data -> let match#27 : pair Data Data = arg#22 in -case@Pair match#27 of - Pair field#28 field#29 -> let key#23 : Data = field#28 in - let value#24 : Data = field#29 in - mkPairData (validate#25 key#23) (validate#26 value#24) in -let validate#3 : Data -> Map Data Data = \value#9 : Data -> let candidate#10 : Map Data Data = (\coerce#13 : 'erased -> coerce#13) value#9 in -mapData (mapList#11 pairValidate#12 (unMapData candidate#10)) in -let fromBytes#6 : bytes -> bytes = \value#14 : bytes -> value#14 in + Cons field#47 field#48 -> let first#44 : 'erased = field#47 in + let rest#45 : list 'erased = field#48 in + mkCons (f#42 first#44) (self#53 self#53 rest#45)) values#43 in +let validate#40 : Data -> Int = \value#49 : Data -> let candidate#50 : Int = (\coerce#52 : 'erased -> coerce#52) value#49 in +let destruct#51 : int = unIData candidate#50 in +candidate#50 in +let validate#29 : Data -> List Data = \value#37 : Data -> let candidate#38 : List Data = (\coerce#41 : 'erased -> coerce#41) value#37 in +applyForward#11 (applyForward#11 (applyForward#11 candidate#38 unListData) (mapList#39 validate#40)) listData in +let pairValidate#13 : pair Data Data -> pair Data Data = \arg#25 : pair Data Data -> let match#30 : pair Data Data = arg#25 in +case@Pair match#30 of + Pair field#31 field#32 -> let key#26 : Data = field#31 in + let value#27 : Data = field#32 in + mkPairData (validate#28 key#26) (validate#29 value#27) in +let validate#3 : Data -> Map Data Data = \value#9 : Data -> let candidate#10 : Map Data Data = (\coerce#14 : 'erased -> coerce#14) value#9 in +applyForward#11 (applyForward#11 (applyForward#11 candidate#10 unMapData) (mapList#12 pairValidate#13)) mapData in +let fromBytes#6 : bytes -> bytes = \value#15 : bytes -> value#15 in let decoded#2 : Map Bytes (List Int) = validate#3 ((\field#4 : list (pair Data Data) -> mapData field#4) (mkCons (mkPairData ((\field#5 : bytes -> bData field#5) (fromBytes#6 (con bytestring #aa))) ((\field#7 : list Data -> listData field#7) (mkCons ((\field#8 : bytes -> bData field#8) (fromBytes#6 (con bytestring #bb))) (con (list data) [])))) (con (list (pair data data)) []))) in let main#1 : bool = nullList (unMapData decoded#2) in main#1 --- uplc (program 1.1.0 - [(lam mapList_11 - [(lam validate_25 - [(lam mapList_36 - [(lam validate_37 - [(lam validate_26 - [(lam pairValidate_12 - [(lam validate_3 - [(lam fromBytes_6 - [(lam decoded_2 - [(lam main_1 main_1) - [(force (builtin nullList)) [(builtin unMapData) decoded_2]]]) - [validate_3 - [(lam field_4 [(builtin mapData) field_4]) - [[(force (builtin mkCons)) - [[(builtin mkPairData) - [(lam field_5 [(builtin bData) field_5]) [fromBytes_6 (con bytestring #aa)]]] - [(lam field_7 [(builtin listData) field_7]) - [[(force (builtin mkCons)) - [(lam field_8 [(builtin bData) field_8]) [fromBytes_6 (con bytestring #bb)]]] - (con (list data) [])]]]] - (con (list (pair data data)) [])]]]]) - (lam value_14 value_14)]) - (lam value_9 - [(lam candidate_10 - [(builtin mapData) - [[mapList_11 pairValidate_12] [(builtin unMapData) candidate_10]]]) - [(lam coerce_13 coerce_13) value_9]])]) - (lam arg_22 - [(lam match_27 - (case - match_27 - (lam field_28 - (lam field_29 - [(lam key_23 - [(lam value_24 - [[(builtin mkPairData) [validate_25 key_23]] [validate_26 value_24]]) - field_29]) - field_28])))) - arg_22])]) - (lam value_34 - [(lam candidate_35 - [(lam projection_50 - [(builtin listData) [[mapList_36 validate_37] projection_50]]) - [(builtin unListData) candidate_35]]) - [(lam coerce_38 coerce_38) value_34]])]) - (lam value_46 - [(lam candidate_47 - [(lam destruct_48 candidate_47) [(builtin unIData) candidate_47]]) - [(lam coerce_49 coerce_49) value_46]])]) - (lam f_39 - (lam values_40 - [[(lam self_51 [self_51 self_51]) - (lam self_51 - (lam values_40 - [(lam match_43 + [(lam applyForward_11 + [(lam mapList_12 + [(lam validate_28 + [(lam mapList_39 + [(lam validate_40 + [(lam validate_29 + [(lam pairValidate_13 + [(lam validate_3 + [(lam fromBytes_6 + [(lam decoded_2 + [(lam main_1 main_1) + [(force (builtin nullList)) [(builtin unMapData) decoded_2]]]) + [validate_3 + [(lam field_4 [(builtin mapData) field_4]) + [[(force (builtin mkCons)) + [[(builtin mkPairData) + [(lam field_5 [(builtin bData) field_5]) [fromBytes_6 (con bytestring #aa)]]] + [(lam field_7 [(builtin listData) field_7]) + [[(force (builtin mkCons)) + [(lam field_8 [(builtin bData) field_8]) [fromBytes_6 (con bytestring #bb)]]] + (con (list data) [])]]]] + (con (list (pair data data)) [])]]]]) + (lam value_15 value_15)]) + (lam value_9 + [(lam candidate_10 + [[applyForward_11 + [[applyForward_11 [[applyForward_11 candidate_10] (builtin unMapData)]] + [mapList_12 pairValidate_13]]] + (builtin mapData)]) + [(lam coerce_14 coerce_14) value_9]])]) + (lam arg_25 + [(lam match_30 (case - match_43 - (lam field_44 - (lam field_45 - [(lam first_41 - [(lam rest_42 - [[(force (builtin mkCons)) [f_39 first_41]] [[self_51 self_51] rest_42]]) - field_45]) - field_44])) - (con (list data) []))) - values_40]))] - values_40]))]) - (lam value_30 - [(lam candidate_31 - [(lam destruct_32 candidate_31) [(builtin unBData) candidate_31]]) - [(lam coerce_33 coerce_33) value_30]])]) - (lam f_15 - (lam values_16 - [[(lam self_52 [self_52 self_52]) - (lam self_52 - (lam values_16 - [(lam match_19 - (case - match_19 - (lam field_20 - (lam field_21 - [(lam first_17 - [(lam rest_18 - [[(force (builtin mkCons)) [f_15 first_17]] [[self_52 self_52] rest_18]]) - field_21]) - field_20])) - (con (list (pair data data)) []))) - values_16]))] - values_16]))]) + match_30 + (lam field_31 + (lam field_32 + [(lam key_26 + [(lam value_27 + [[(builtin mkPairData) [validate_28 key_26]] [validate_29 value_27]]) + field_32]) + field_31])))) + arg_25])]) + (lam value_37 + [(lam candidate_38 + [[applyForward_11 + [[applyForward_11 [[applyForward_11 candidate_38] (builtin unListData)]] + [mapList_39 validate_40]]] + (builtin listData)]) + [(lam coerce_41 coerce_41) value_37]])]) + (lam value_49 + [(lam candidate_50 + [(lam destruct_51 candidate_50) [(builtin unIData) candidate_50]]) + [(lam coerce_52 coerce_52) value_49]])]) + (lam f_42 + (lam values_43 + [[(lam self_53 [self_53 self_53]) + (lam self_53 + (lam values_43 + [(lam match_46 + (case + match_46 + (lam field_47 + (lam field_48 + [(lam first_44 + [(lam rest_45 + [[(force (builtin mkCons)) [f_42 first_44]] [[self_53 self_53] rest_45]]) + field_48]) + field_47])) + (con (list data) []))) + values_43]))] + values_43]))]) + (lam value_33 + [(lam candidate_34 + [(lam destruct_35 candidate_34) [(builtin unBData) candidate_34]]) + [(lam coerce_36 coerce_36) value_33]])]) + (lam f_18 + (lam values_19 + [[(lam self_54 [self_54 self_54]) + (lam self_54 + (lam values_19 + [(lam match_22 + (case + match_22 + (lam field_23 + (lam field_24 + [(lam first_20 + [(lam rest_21 + [[(force (builtin mkCons)) [f_18 first_20]] [[self_54 self_54] rest_21]]) + field_24]) + field_23])) + (con (list (pair data data)) []))) + values_19]))] + values_19]))]) + (lam x_16 (lam f_17 [f_17 x_16]))]) --- result error: Runtime(MalFormedData(ByteString([187]))) --- logs diff --git a/crates/nash-codegen/src/build/tests/source/snapshots/nash_codegen__build__tests__source__builtins__data_nested_list_rejects_malformed_element.snap b/crates/nash-codegen/src/build/tests/source/snapshots/nash_codegen__build__tests__source__builtins__data_nested_list_rejects_malformed_element.snap index 8ca221b2..215ca9ae 100644 --- a/crates/nash-codegen/src/build/tests/source/snapshots/nash_codegen__build__tests__source__builtins__data_nested_list_rejects_malformed_element.snap +++ b/crates/nash-codegen/src/build/tests/source/snapshots/nash_codegen__build__tests__source__builtins__data_nested_list_rejects_malformed_element.snap @@ -3,85 +3,88 @@ source: crates/nash-codegen/src/build/tests/source/builtins.rs description: "module Main exposing (..)\nimport Primitive exposing (..)\nimport Builtin exposing (..)\nimport Data exposing (validate)\ndecoded : List (List Int)\ndecoded = validate (List [List [I 1, B #\"aa\"]])\nmain = nullList (unListData decoded)\n" --- --- core -let mapList#12 : ('erased -> Data) -> list 'erased -> list Data = \(f#17 : 'erased -> Data) values#18 : list 'erased -> (\self#35 : 'erased -> self#35 self#35) (\self#35 : 'erased values#18 : list 'erased -> let match#21 : list 'erased = values#18 in -case@List match#21 of +let applyForward#12 : 'erased -> ('erased -> 'erased) -> 'erased = \x#18 : 'erased (f#19 : 'erased -> 'erased) -> f#19 x#18 in +let mapList#13 : ('erased -> Data) -> list 'erased -> list Data = \(f#20 : 'erased -> Data) values#21 : list 'erased -> (\self#36 : 'erased -> self#36 self#36) (\self#36 : 'erased values#21 : list 'erased -> let match#24 : list 'erased = values#21 in +case@List match#24 of Nil -> (con (list data) []) - Cons field#22 field#23 -> let first#19 : 'erased = field#22 in - let rest#20 : list 'erased = field#23 in - mkCons (f#17 first#19) (self#35 self#35 rest#20)) values#18 in -let validate#26 : Data -> Int = \value#28 : Data -> let candidate#29 : Int = (\coerce#31 : 'erased -> coerce#31) value#28 in -let destruct#30 : int = unIData candidate#29 in -candidate#29 in -let validate#13 : Data -> List Data = \value#24 : Data -> let candidate#25 : List Data = (\coerce#27 : 'erased -> coerce#27) value#24 in -let projection#32 : list Data = unListData candidate#25 in -listData (mapList#12 validate#26 projection#32) in -let validate#3 : Data -> List Data = \value#10 : Data -> let candidate#11 : List Data = (\coerce#14 : 'erased -> coerce#14) value#10 in -let projection#33 : list Data = unListData candidate#11 in -listData (mapList#12 validate#13 projection#33) in -let fromInt#7 : int -> int = \value#15 : int -> value#15 in -let fromBytes#9 : bytes -> bytes = \value#16 : bytes -> value#16 in + Cons field#25 field#26 -> let first#22 : 'erased = field#25 in + let rest#23 : list 'erased = field#26 in + mkCons (f#20 first#22) (self#36 self#36 rest#23)) values#21 in +let validate#29 : Data -> Int = \value#31 : Data -> let candidate#32 : Int = (\coerce#34 : 'erased -> coerce#34) value#31 in +let destruct#33 : int = unIData candidate#32 in +candidate#32 in +let validate#14 : Data -> List Data = \value#27 : Data -> let candidate#28 : List Data = (\coerce#30 : 'erased -> coerce#30) value#27 in +applyForward#12 (applyForward#12 (applyForward#12 candidate#28 unListData) (mapList#13 validate#29)) listData in +let validate#3 : Data -> List Data = \value#10 : Data -> let candidate#11 : List Data = (\coerce#15 : 'erased -> coerce#15) value#10 in +applyForward#12 (applyForward#12 (applyForward#12 candidate#11 unListData) (mapList#13 validate#14)) listData in +let fromInt#7 : int -> int = \value#16 : int -> value#16 in +let fromBytes#9 : bytes -> bytes = \value#17 : bytes -> value#17 in let decoded#2 : List (List Int) = validate#3 ((\field#4 : list Data -> listData field#4) (mkCons ((\field#5 : list Data -> listData field#5) (mkCons ((\field#6 : int -> iData field#6) (fromInt#7 1)) (mkCons ((\field#8 : bytes -> bData field#8) (fromBytes#9 (con bytestring #aa))) (con (list data) [])))) (con (list data) []))) in -let projection#34 : list Data = unListData decoded#2 in -let main#1 : bool = nullList projection#34 in +let projection#35 : list Data = unListData decoded#2 in +let main#1 : bool = nullList projection#35 in main#1 --- uplc (program 1.1.0 - [(lam mapList_12 - [(lam validate_26 - [(lam validate_13 - [(lam validate_3 - [(lam fromInt_7 - [(lam fromBytes_9 - [(lam decoded_2 - [(lam projection_34 - [(lam main_1 main_1) [(force (builtin nullList)) projection_34]]) - [(builtin unListData) decoded_2]]) - [validate_3 - [(lam field_4 [(builtin listData) field_4]) - [[(force (builtin mkCons)) - [(lam field_5 [(builtin listData) field_5]) - [[(force (builtin mkCons)) - [(lam field_6 [(builtin iData) field_6]) [fromInt_7 (con integer 1)]]] + [(lam applyForward_12 + [(lam mapList_13 + [(lam validate_29 + [(lam validate_14 + [(lam validate_3 + [(lam fromInt_7 + [(lam fromBytes_9 + [(lam decoded_2 + [(lam projection_35 + [(lam main_1 main_1) [(force (builtin nullList)) projection_35]]) + [(builtin unListData) decoded_2]]) + [validate_3 + [(lam field_4 [(builtin listData) field_4]) + [[(force (builtin mkCons)) + [(lam field_5 [(builtin listData) field_5]) [[(force (builtin mkCons)) - [(lam field_8 [(builtin bData) field_8]) [fromBytes_9 (con bytestring #aa)]]] - (con (list data) [])]]]] - (con (list data) [])]]]]) + [(lam field_6 [(builtin iData) field_6]) [fromInt_7 (con integer 1)]]] + [[(force (builtin mkCons)) + [(lam field_8 [(builtin bData) field_8]) [fromBytes_9 (con bytestring #aa)]]] + (con (list data) [])]]]] + (con (list data) [])]]]]) + (lam value_17 value_17)]) (lam value_16 value_16)]) - (lam value_15 value_15)]) - (lam value_10 - [(lam candidate_11 - [(lam projection_33 - [(builtin listData) [[mapList_12 validate_13] projection_33]]) - [(builtin unListData) candidate_11]]) - [(lam coerce_14 coerce_14) value_10]])]) - (lam value_24 - [(lam candidate_25 - [(lam projection_32 - [(builtin listData) [[mapList_12 validate_26] projection_32]]) - [(builtin unListData) candidate_25]]) - [(lam coerce_27 coerce_27) value_24]])]) - (lam value_28 - [(lam candidate_29 - [(lam destruct_30 candidate_29) [(builtin unIData) candidate_29]]) - [(lam coerce_31 coerce_31) value_28]])]) - (lam f_17 - (lam values_18 - [[(lam self_35 [self_35 self_35]) - (lam self_35 - (lam values_18 - [(lam match_21 - (case - match_21 - (lam field_22 - (lam field_23 - [(lam first_19 - [(lam rest_20 - [[(force (builtin mkCons)) [f_17 first_19]] [[self_35 self_35] rest_20]]) - field_23]) - field_22])) - (con (list data) []))) - values_18]))] - values_18]))]) + (lam value_10 + [(lam candidate_11 + [[applyForward_12 + [[applyForward_12 [[applyForward_12 candidate_11] (builtin unListData)]] + [mapList_13 validate_14]]] + (builtin listData)]) + [(lam coerce_15 coerce_15) value_10]])]) + (lam value_27 + [(lam candidate_28 + [[applyForward_12 + [[applyForward_12 [[applyForward_12 candidate_28] (builtin unListData)]] + [mapList_13 validate_29]]] + (builtin listData)]) + [(lam coerce_30 coerce_30) value_27]])]) + (lam value_31 + [(lam candidate_32 + [(lam destruct_33 candidate_32) [(builtin unIData) candidate_32]]) + [(lam coerce_34 coerce_34) value_31]])]) + (lam f_20 + (lam values_21 + [[(lam self_36 [self_36 self_36]) + (lam self_36 + (lam values_21 + [(lam match_24 + (case + match_24 + (lam field_25 + (lam field_26 + [(lam first_22 + [(lam rest_23 + [[(force (builtin mkCons)) [f_20 first_22]] [[self_36 self_36] rest_23]]) + field_26]) + field_25])) + (con (list data) []))) + values_21]))] + values_21]))]) + (lam x_18 (lam f_19 [f_19 x_18]))]) --- result error: Runtime(MalFormedData(ByteString([170]))) --- logs diff --git a/crates/nash-codegen/src/build/tests/source/snapshots/nash_codegen__build__tests__source__builtins__validate_data_accepts_nested_map.snap b/crates/nash-codegen/src/build/tests/source/snapshots/nash_codegen__build__tests__source__builtins__validate_data_accepts_nested_map.snap index d560c2da..5e5b6c48 100644 --- a/crates/nash-codegen/src/build/tests/source/snapshots/nash_codegen__build__tests__source__builtins__validate_data_accepts_nested_map.snap +++ b/crates/nash-codegen/src/build/tests/source/snapshots/nash_codegen__build__tests__source__builtins__validate_data_accepts_nested_map.snap @@ -4,37 +4,37 @@ description: "module Main exposing (..)\nimport Primitive exposing (..)\nimport --- --- core let toData#2 : 'erased -> Data = \coerce#5 : 'erased -> coerce#5 in -let fromBytes#9 : bytes -> bytes = \value#18 : bytes -> value#18 in -let fromInt#12 : int -> int = \value#19 : int -> value#19 in +let fromBytes#9 : bytes -> bytes = \value#19 : bytes -> value#19 in +let fromInt#12 : int -> int = \value#20 : int -> value#20 in let input#4 : Data = (\field#7 : list (pair Data Data) -> mapData field#7) (mkCons (mkPairData ((\field#8 : bytes -> bData field#8) (fromBytes#9 (con bytestring #aa))) ((\field#10 : list Data -> listData field#10) (mkCons ((\field#11 : int -> iData field#11) (fromInt#12 42)) (con (list data) [])))) (con (list (pair data data)) [])) in -let mapList#15 : ('erased -> pair Data Data) -> list 'erased -> list (pair Data Data) = \(f#20 : 'erased -> pair Data Data) values#21 : list 'erased -> (\self#57 : 'erased -> self#57 self#57) (\self#57 : 'erased values#21 : list 'erased -> let match#24 : list 'erased = values#21 in -case@List match#24 of +let applyForward#15 : 'erased -> ('erased -> 'erased) -> 'erased = \x#21 : 'erased (f#22 : 'erased -> 'erased) -> f#22 x#21 in +let mapList#16 : ('erased -> pair Data Data) -> list 'erased -> list (pair Data Data) = \(f#23 : 'erased -> pair Data Data) values#24 : list 'erased -> (\self#59 : 'erased -> self#59 self#59) (\self#59 : 'erased values#24 : list 'erased -> let match#27 : list 'erased = values#24 in +case@List match#27 of Nil -> (con (list (pair data data)) []) - Cons field#25 field#26 -> let first#22 : 'erased = field#25 in - let rest#23 : list 'erased = field#26 in - mkCons (f#20 first#22) (self#57 self#57 rest#23)) values#21 in -let validate#30 : Data -> Bytes = \value#35 : Data -> let candidate#36 : Bytes = (\coerce#38 : 'erased -> coerce#38) value#35 in -let destruct#37 : bytes = unBData candidate#36 in -candidate#36 in -let mapList#41 : ('erased -> Data) -> list 'erased -> list Data = \(f#44 : 'erased -> Data) values#45 : list 'erased -> (\self#56 : 'erased -> self#56 self#56) (\self#56 : 'erased values#45 : list 'erased -> let match#48 : list 'erased = values#45 in -case@List match#48 of + Cons field#28 field#29 -> let first#25 : 'erased = field#28 in + let rest#26 : list 'erased = field#29 in + mkCons (f#23 first#25) (self#59 self#59 rest#26)) values#24 in +let validate#33 : Data -> Bytes = \value#38 : Data -> let candidate#39 : Bytes = (\coerce#41 : 'erased -> coerce#41) value#38 in +let destruct#40 : bytes = unBData candidate#39 in +candidate#39 in +let mapList#44 : ('erased -> Data) -> list 'erased -> list Data = \(f#47 : 'erased -> Data) values#48 : list 'erased -> (\self#58 : 'erased -> self#58 self#58) (\self#58 : 'erased values#48 : list 'erased -> let match#51 : list 'erased = values#48 in +case@List match#51 of Nil -> (con (list data) []) - Cons field#49 field#50 -> let first#46 : 'erased = field#49 in - let rest#47 : list 'erased = field#50 in - mkCons (f#44 first#46) (self#56 self#56 rest#47)) values#45 in -let validate#42 : Data -> Int = \value#51 : Data -> let candidate#52 : Int = (\coerce#54 : 'erased -> coerce#54) value#51 in -let destruct#53 : int = unIData candidate#52 in -candidate#52 in -let validate#31 : Data -> List Data = \value#39 : Data -> let candidate#40 : List Data = (\coerce#43 : 'erased -> coerce#43) value#39 in -let projection#55 : list Data = unListData candidate#40 in -listData (mapList#41 validate#42 projection#55) in -let pairValidate#16 : pair Data Data -> pair Data Data = \arg#27 : pair Data Data -> let match#32 : pair Data Data = arg#27 in -case@Pair match#32 of - Pair field#33 field#34 -> let key#28 : Data = field#33 in - let value#29 : Data = field#34 in - mkPairData (validate#30 key#28) (validate#31 value#29) in -let validate#6 : Data -> Map Data Data = \value#13 : Data -> let candidate#14 : Map Data Data = (\coerce#17 : 'erased -> coerce#17) value#13 in -mapData (mapList#15 pairValidate#16 (unMapData candidate#14)) in + Cons field#52 field#53 -> let first#49 : 'erased = field#52 in + let rest#50 : list 'erased = field#53 in + mkCons (f#47 first#49) (self#58 self#58 rest#50)) values#48 in +let validate#45 : Data -> Int = \value#54 : Data -> let candidate#55 : Int = (\coerce#57 : 'erased -> coerce#57) value#54 in +let destruct#56 : int = unIData candidate#55 in +candidate#55 in +let validate#34 : Data -> List Data = \value#42 : Data -> let candidate#43 : List Data = (\coerce#46 : 'erased -> coerce#46) value#42 in +applyForward#15 (applyForward#15 (applyForward#15 candidate#43 unListData) (mapList#44 validate#45)) listData in +let pairValidate#17 : pair Data Data -> pair Data Data = \arg#30 : pair Data Data -> let match#35 : pair Data Data = arg#30 in +case@Pair match#35 of + Pair field#36 field#37 -> let key#31 : Data = field#36 in + let value#32 : Data = field#37 in + mkPairData (validate#33 key#31) (validate#34 value#32) in +let validate#6 : Data -> Map Data Data = \value#13 : Data -> let candidate#14 : Map Data Data = (\coerce#18 : 'erased -> coerce#18) value#13 in +applyForward#15 (applyForward#15 (applyForward#15 candidate#14 unMapData) (mapList#16 pairValidate#17)) mapData in let decoded#3 : Map Bytes (List Int) = validate#6 input#4 in let main#1 : bool = equalsData (toData#2 decoded#3) input#4 in main#1 @@ -44,83 +44,88 @@ main#1 [(lam fromBytes_9 [(lam fromInt_12 [(lam input_4 - [(lam mapList_15 - [(lam validate_30 - [(lam mapList_41 - [(lam validate_42 - [(lam validate_31 - [(lam pairValidate_16 - [(lam validate_6 - [(lam decoded_3 - [(lam main_1 main_1) [[(builtin equalsData) [toData_2 decoded_3]] input_4]]) - [validate_6 input_4]]) - (lam value_13 - [(lam candidate_14 - [(builtin mapData) - [[mapList_15 pairValidate_16] [(builtin unMapData) candidate_14]]]) - [(lam coerce_17 coerce_17) value_13]])]) - (lam arg_27 - [(lam match_32 - (case - match_32 - (lam field_33 - (lam field_34 - [(lam key_28 - [(lam value_29 - [[(builtin mkPairData) [validate_30 key_28]] [validate_31 value_29]]) - field_34]) - field_33])))) - arg_27])]) - (lam value_39 - [(lam candidate_40 - [(lam projection_55 - [(builtin listData) [[mapList_41 validate_42] projection_55]]) - [(builtin unListData) candidate_40]]) - [(lam coerce_43 coerce_43) value_39]])]) - (lam value_51 - [(lam candidate_52 - [(lam destruct_53 candidate_52) [(builtin unIData) candidate_52]]) - [(lam coerce_54 coerce_54) value_51]])]) - (lam f_44 - (lam values_45 - [[(lam self_56 [self_56 self_56]) - (lam self_56 - (lam values_45 - [(lam match_48 + [(lam applyForward_15 + [(lam mapList_16 + [(lam validate_33 + [(lam mapList_44 + [(lam validate_45 + [(lam validate_34 + [(lam pairValidate_17 + [(lam validate_6 + [(lam decoded_3 + [(lam main_1 main_1) [[(builtin equalsData) [toData_2 decoded_3]] input_4]]) + [validate_6 input_4]]) + (lam value_13 + [(lam candidate_14 + [[applyForward_15 + [[applyForward_15 [[applyForward_15 candidate_14] (builtin unMapData)]] + [mapList_16 pairValidate_17]]] + (builtin mapData)]) + [(lam coerce_18 coerce_18) value_13]])]) + (lam arg_30 + [(lam match_35 (case - match_48 - (lam field_49 - (lam field_50 - [(lam first_46 - [(lam rest_47 - [[(force (builtin mkCons)) [f_44 first_46]] [[self_56 self_56] rest_47]]) - field_50]) - field_49])) - (con (list data) []))) - values_45]))] - values_45]))]) - (lam value_35 - [(lam candidate_36 - [(lam destruct_37 candidate_36) [(builtin unBData) candidate_36]]) - [(lam coerce_38 coerce_38) value_35]])]) - (lam f_20 - (lam values_21 - [[(lam self_57 [self_57 self_57]) - (lam self_57 - (lam values_21 - [(lam match_24 - (case - match_24 - (lam field_25 - (lam field_26 - [(lam first_22 - [(lam rest_23 - [[(force (builtin mkCons)) [f_20 first_22]] [[self_57 self_57] rest_23]]) - field_26]) - field_25])) - (con (list (pair data data)) []))) - values_21]))] - values_21]))]) + match_35 + (lam field_36 + (lam field_37 + [(lam key_31 + [(lam value_32 + [[(builtin mkPairData) [validate_33 key_31]] [validate_34 value_32]]) + field_37]) + field_36])))) + arg_30])]) + (lam value_42 + [(lam candidate_43 + [[applyForward_15 + [[applyForward_15 [[applyForward_15 candidate_43] (builtin unListData)]] + [mapList_44 validate_45]]] + (builtin listData)]) + [(lam coerce_46 coerce_46) value_42]])]) + (lam value_54 + [(lam candidate_55 + [(lam destruct_56 candidate_55) [(builtin unIData) candidate_55]]) + [(lam coerce_57 coerce_57) value_54]])]) + (lam f_47 + (lam values_48 + [[(lam self_58 [self_58 self_58]) + (lam self_58 + (lam values_48 + [(lam match_51 + (case + match_51 + (lam field_52 + (lam field_53 + [(lam first_49 + [(lam rest_50 + [[(force (builtin mkCons)) [f_47 first_49]] [[self_58 self_58] rest_50]]) + field_53]) + field_52])) + (con (list data) []))) + values_48]))] + values_48]))]) + (lam value_38 + [(lam candidate_39 + [(lam destruct_40 candidate_39) [(builtin unBData) candidate_39]]) + [(lam coerce_41 coerce_41) value_38]])]) + (lam f_23 + (lam values_24 + [[(lam self_59 [self_59 self_59]) + (lam self_59 + (lam values_24 + [(lam match_27 + (case + match_27 + (lam field_28 + (lam field_29 + [(lam first_25 + [(lam rest_26 + [[(force (builtin mkCons)) [f_23 first_25]] [[self_59 self_59] rest_26]]) + field_29]) + field_28])) + (con (list (pair data data)) []))) + values_24]))] + values_24]))]) + (lam x_21 (lam f_22 [f_22 x_21]))]) [(lam field_7 [(builtin mapData) field_7]) [[(force (builtin mkCons)) [[(builtin mkPairData) @@ -130,12 +135,12 @@ main#1 [(lam field_11 [(builtin iData) field_11]) [fromInt_12 (con integer 42)]]] (con (list data) [])]]]] (con (list (pair data data)) [])]]]) - (lam value_19 value_19)]) - (lam value_18 value_18)]) + (lam value_20 value_20)]) + (lam value_19 value_19)]) (lam coerce_5 coerce_5)]) --- result (con bool True) --- logs [] --- budget -cpu: 6055930, memory: 25813 +cpu: 6631930, memory: 29413 diff --git a/crates/nash-codegen/src/build/tests/source/snapshots/nash_codegen__build__tests__source__patterns__expect_head3_cast_data_no_tail.snap b/crates/nash-codegen/src/build/tests/source/snapshots/nash_codegen__build__tests__source__patterns__expect_head3_cast_data_no_tail.snap index c4076576..e67ff8d2 100644 --- a/crates/nash-codegen/src/build/tests/source/snapshots/nash_codegen__build__tests__source__patterns__expect_head3_cast_data_no_tail.snap +++ b/crates/nash-codegen/src/build/tests/source/snapshots/nash_codegen__build__tests__source__patterns__expect_head3_cast_data_no_tail.snap @@ -4,30 +4,30 @@ description: "module Main exposing (..)\nimport Primitive exposing (..)\nimport --- --- core let fromBool#8 : bool -> bool = \value#34 : bool -> value#34 in -let mapList#35 : ('erased -> int) -> list 'erased -> list int = \(f#42 : 'erased -> int) values#43 : list 'erased -> (\self#11 : 'erased -> self#11 self#11) (\self#11 : 'erased values#43 : list 'erased -> let match#46 : list 'erased = values#43 in -case@List match#46 of +let mapList#35 : ('erased -> int) -> list 'erased -> list int = \(f#43 : 'erased -> int) values#44 : list 'erased -> (\self#11 : 'erased -> self#11 self#11) (\self#11 : 'erased values#44 : list 'erased -> let match#47 : list 'erased = values#44 in +case@List match#47 of Nil -> (con (list integer) []) - Cons field#47 field#48 -> let x#44 : 'erased = field#47 in - let xs#45 : list 'erased = field#48 in - mkCons (f#42 x#44) (self#11 self#11 xs#45)) values#43 in + Cons field#48 field#49 -> let x#45 : 'erased = field#48 in + let xs#46 : list 'erased = field#49 in + mkCons (f#43 x#45) (self#11 self#11 xs#46)) values#44 in let map#27 : ('erased -> int) -> list 'erased -> list int = mapList#35 in -let mapList#38 : ('erased -> Data) -> list 'erased -> list Data = \(f#49 : 'erased -> Data) values#50 : list 'erased -> (\self#10 : 'erased -> self#10 self#10) (\self#10 : 'erased values#50 : list 'erased -> let match#53 : list 'erased = values#50 in -case@List match#53 of +let applyForward#38 : 'erased -> ('erased -> 'erased) -> 'erased = \x#50 : 'erased (f#51 : 'erased -> 'erased) -> f#51 x#50 in +let mapList#39 : ('erased -> Data) -> list 'erased -> list Data = \(f#52 : 'erased -> Data) values#53 : list 'erased -> (\self#10 : 'erased -> self#10 self#10) (\self#10 : 'erased values#53 : list 'erased -> let match#56 : list 'erased = values#53 in +case@List match#56 of Nil -> (con (list data) []) - Cons field#54 field#55 -> let first#51 : 'erased = field#54 in - let rest#52 : list 'erased = field#55 in - mkCons (f#49 first#51) (self#10 self#10 rest#52)) values#50 in -let validate#39 : Data -> Int = \value#56 : Data -> let candidate#57 : Int = (\coerce#59 : 'erased -> coerce#59) value#56 in -let destruct#58 : int = unIData candidate#57 in -candidate#57 in -let validate#28 : Data -> List Data = \value#36 : Data -> let candidate#37 : List Data = (\coerce#40 : 'erased -> coerce#40) value#36 in -let projection#60 : list Data = unListData candidate#37 in -listData (mapList#38 validate#39 projection#60) in -let fromInt#31 : int -> int = \value#41 : int -> value#41 in + Cons field#57 field#58 -> let first#54 : 'erased = field#57 in + let rest#55 : list 'erased = field#58 in + mkCons (f#52 first#54) (self#10 self#10 rest#55)) values#53 in +let validate#40 : Data -> Int = \value#59 : Data -> let candidate#60 : Int = (\coerce#62 : 'erased -> coerce#62) value#59 in +let destruct#61 : int = unIData candidate#60 in +candidate#60 in +let validate#28 : Data -> List Data = \value#36 : Data -> let candidate#37 : List Data = (\coerce#41 : 'erased -> coerce#41) value#36 in +applyForward#38 (applyForward#38 (applyForward#38 candidate#37 unListData) (mapList#39 validate#40)) listData in +let fromInt#31 : int -> int = \value#42 : int -> value#42 in let a#2 : Data = (\field#29 : list Data -> listData field#29) (mkCons ((\field#30 : int -> iData field#30) (fromInt#31 1)) (mkCons ((\field#32 : int -> iData field#32) (fromInt#31 2)) (mkCons ((\field#33 : int -> iData field#33) (fromInt#31 3)) (con (list data) [])))) in let checked#3 : List Int = validate#28 a#2 in -let projection#61 : list Data = unListData checked#3 in -let values#4 : list int = map#27 unIData projection#61 in +let projection#63 : list Data = unListData checked#3 in +let values#4 : list int = map#27 unIData projection#63 in let match#9 : list int = values#4 in let leaf#18 : 'erased = delay error in let main#1 : bool = case@List match#9 of @@ -52,112 +52,115 @@ main#1 [(lam fromBool_8 [(lam mapList_35 [(lam map_27 - [(lam mapList_38 - [(lam validate_39 - [(lam validate_28 - [(lam fromInt_31 - [(lam a_2 - [(lam checked_3 - [(lam projection_61 - [(lam values_4 - [(lam match_9 - [(lam leaf_18 - [(lam main_1 main_1) - (case - match_9 - (lam field_19 - (lam field_20 - (case - field_20 - (lam field_21 - (lam field_22 - (case - field_22 - (lam field_23 - (lam field_24 - (case - field_24 - (lam field_25 (lam field_26 (force leaf_18))) - [(lam h_5 - [(lam i_6 - [(lam j_7 - (case - [[(builtin equalsInteger) h_5] h_5] - [fromBool_8 (con bool False)] + [(lam applyForward_38 + [(lam mapList_39 + [(lam validate_40 + [(lam validate_28 + [(lam fromInt_31 + [(lam a_2 + [(lam checked_3 + [(lam projection_63 + [(lam values_4 + [(lam match_9 + [(lam leaf_18 + [(lam main_1 main_1) + (case + match_9 + (lam field_19 + (lam field_20 + (case + field_20 + (lam field_21 + (lam field_22 + (case + field_22 + (lam field_23 + (lam field_24 + (case + field_24 + (lam field_25 (lam field_26 (force leaf_18))) + [(lam h_5 + [(lam i_6 + [(lam j_7 (case - [[(builtin equalsInteger) i_6] i_6] + [[(builtin equalsInteger) h_5] h_5] [fromBool_8 (con bool False)] - [[(builtin equalsInteger) j_7] j_7]))) - field_23]) - field_21]) - field_19]))) - (force leaf_18)))) - (force leaf_18)))) - (force leaf_18))]) - (delay (error))]) - values_4]) - [[map_27 (builtin unIData)] projection_61]]) - [(builtin unListData) checked_3]]) - [validate_28 a_2]]) - [(lam field_29 [(builtin listData) field_29]) - [[(force (builtin mkCons)) - [(lam field_30 [(builtin iData) field_30]) [fromInt_31 (con integer 1)]]] + (case + [[(builtin equalsInteger) i_6] i_6] + [fromBool_8 (con bool False)] + [[(builtin equalsInteger) j_7] j_7]))) + field_23]) + field_21]) + field_19]))) + (force leaf_18)))) + (force leaf_18)))) + (force leaf_18))]) + (delay (error))]) + values_4]) + [[map_27 (builtin unIData)] projection_63]]) + [(builtin unListData) checked_3]]) + [validate_28 a_2]]) + [(lam field_29 [(builtin listData) field_29]) [[(force (builtin mkCons)) - [(lam field_32 [(builtin iData) field_32]) [fromInt_31 (con integer 2)]]] + [(lam field_30 [(builtin iData) field_30]) [fromInt_31 (con integer 1)]]] [[(force (builtin mkCons)) - [(lam field_33 [(builtin iData) field_33]) [fromInt_31 (con integer 3)]]] - (con (list data) [])]]]]]) - (lam value_41 value_41)]) - (lam value_36 - [(lam candidate_37 - [(lam projection_60 - [(builtin listData) [[mapList_38 validate_39] projection_60]]) - [(builtin unListData) candidate_37]]) - [(lam coerce_40 coerce_40) value_36]])]) - (lam value_56 - [(lam candidate_57 - [(lam destruct_58 candidate_57) [(builtin unIData) candidate_57]]) - [(lam coerce_59 coerce_59) value_56]])]) - (lam f_49 - (lam values_50 - [[(lam self_10 [self_10 self_10]) - (lam self_10 - (lam values_50 - [(lam match_53 - (case - match_53 - (lam field_54 - (lam field_55 - [(lam first_51 - [(lam rest_52 - [[(force (builtin mkCons)) [f_49 first_51]] [[self_10 self_10] rest_52]]) - field_55]) - field_54])) - (con (list data) []))) - values_50]))] - values_50]))]) + [(lam field_32 [(builtin iData) field_32]) [fromInt_31 (con integer 2)]]] + [[(force (builtin mkCons)) + [(lam field_33 [(builtin iData) field_33]) [fromInt_31 (con integer 3)]]] + (con (list data) [])]]]]]) + (lam value_42 value_42)]) + (lam value_36 + [(lam candidate_37 + [[applyForward_38 + [[applyForward_38 [[applyForward_38 candidate_37] (builtin unListData)]] + [mapList_39 validate_40]]] + (builtin listData)]) + [(lam coerce_41 coerce_41) value_36]])]) + (lam value_59 + [(lam candidate_60 + [(lam destruct_61 candidate_60) [(builtin unIData) candidate_60]]) + [(lam coerce_62 coerce_62) value_59]])]) + (lam f_52 + (lam values_53 + [[(lam self_10 [self_10 self_10]) + (lam self_10 + (lam values_53 + [(lam match_56 + (case + match_56 + (lam field_57 + (lam field_58 + [(lam first_54 + [(lam rest_55 + [[(force (builtin mkCons)) [f_52 first_54]] [[self_10 self_10] rest_55]]) + field_58]) + field_57])) + (con (list data) []))) + values_53]))] + values_53]))]) + (lam x_50 (lam f_51 [f_51 x_50]))]) mapList_35]) - (lam f_42 - (lam values_43 + (lam f_43 + (lam values_44 [[(lam self_11 [self_11 self_11]) (lam self_11 - (lam values_43 - [(lam match_46 + (lam values_44 + [(lam match_47 (case - match_46 - (lam field_47 - (lam field_48 - [(lam x_44 - [(lam xs_45 [[(force (builtin mkCons)) [f_42 x_44]] [[self_11 self_11] xs_45]]) - field_48]) - field_47])) + match_47 + (lam field_48 + (lam field_49 + [(lam x_45 + [(lam xs_46 [[(force (builtin mkCons)) [f_43 x_45]] [[self_11 self_11] xs_46]]) + field_49]) + field_48])) (con (list integer) []))) - values_43]))] - values_43]))]) + values_44]))] + values_44]))]) (lam value_34 value_34)]) --- result (con bool True) --- logs [] --- budget -cpu: 7114288, memory: 38407 +cpu: 7402288, memory: 40207 diff --git a/crates/nash-codegen/src/build/tests/source/snapshots/nash_codegen__build__tests__source__patterns__expect_head_cast_data_no_tail.snap b/crates/nash-codegen/src/build/tests/source/snapshots/nash_codegen__build__tests__source__patterns__expect_head_cast_data_no_tail.snap index 55d01001..ce83cf28 100644 --- a/crates/nash-codegen/src/build/tests/source/snapshots/nash_codegen__build__tests__source__patterns__expect_head_cast_data_no_tail.snap +++ b/crates/nash-codegen/src/build/tests/source/snapshots/nash_codegen__build__tests__source__patterns__expect_head_cast_data_no_tail.snap @@ -3,30 +3,30 @@ source: crates/nash-codegen/src/build/tests/source/patterns.rs description: "module Main exposing (..)\nimport Primitive exposing (..)\nimport Builtin exposing (..)\nimport Data exposing (validate)\nimport Lift exposing (lower)\nimport Functor\nmain =\n let\n a : Data\n a = List [I 1, I 2, I 3]\n checked : List Int\n checked = validate a\n values : list int\n values = Functor.map Builtin.unIData (Builtin.unListData checked)\n in\n case values of\n [h] -> equalsInteger h h\n _ -> fail\n" --- --- core -let mapList#23 : ('erased -> int) -> list 'erased -> list int = \(f#30 : 'erased -> int) values#31 : list 'erased -> (\self#8 : 'erased -> self#8 self#8) (\self#8 : 'erased values#31 : list 'erased -> let match#34 : list 'erased = values#31 in -case@List match#34 of +let mapList#23 : ('erased -> int) -> list 'erased -> list int = \(f#31 : 'erased -> int) values#32 : list 'erased -> (\self#8 : 'erased -> self#8 self#8) (\self#8 : 'erased values#32 : list 'erased -> let match#35 : list 'erased = values#32 in +case@List match#35 of Nil -> (con (list integer) []) - Cons field#35 field#36 -> let x#32 : 'erased = field#35 in - let xs#33 : list 'erased = field#36 in - mkCons (f#30 x#32) (self#8 self#8 xs#33)) values#31 in + Cons field#36 field#37 -> let x#33 : 'erased = field#36 in + let xs#34 : list 'erased = field#37 in + mkCons (f#31 x#33) (self#8 self#8 xs#34)) values#32 in let map#16 : ('erased -> int) -> list 'erased -> list int = mapList#23 in -let mapList#26 : ('erased -> Data) -> list 'erased -> list Data = \(f#37 : 'erased -> Data) values#38 : list 'erased -> (\self#7 : 'erased -> self#7 self#7) (\self#7 : 'erased values#38 : list 'erased -> let match#41 : list 'erased = values#38 in -case@List match#41 of +let applyForward#26 : 'erased -> ('erased -> 'erased) -> 'erased = \x#38 : 'erased (f#39 : 'erased -> 'erased) -> f#39 x#38 in +let mapList#27 : ('erased -> Data) -> list 'erased -> list Data = \(f#40 : 'erased -> Data) values#41 : list 'erased -> (\self#7 : 'erased -> self#7 self#7) (\self#7 : 'erased values#41 : list 'erased -> let match#44 : list 'erased = values#41 in +case@List match#44 of Nil -> (con (list data) []) - Cons field#42 field#43 -> let first#39 : 'erased = field#42 in - let rest#40 : list 'erased = field#43 in - mkCons (f#37 first#39) (self#7 self#7 rest#40)) values#38 in -let validate#27 : Data -> Int = \value#44 : Data -> let candidate#45 : Int = (\coerce#47 : 'erased -> coerce#47) value#44 in -let destruct#46 : int = unIData candidate#45 in -candidate#45 in -let validate#17 : Data -> List Data = \value#24 : Data -> let candidate#25 : List Data = (\coerce#28 : 'erased -> coerce#28) value#24 in -let projection#48 : list Data = unListData candidate#25 in -listData (mapList#26 validate#27 projection#48) in -let fromInt#20 : int -> int = \value#29 : int -> value#29 in + Cons field#45 field#46 -> let first#42 : 'erased = field#45 in + let rest#43 : list 'erased = field#46 in + mkCons (f#40 first#42) (self#7 self#7 rest#43)) values#41 in +let validate#28 : Data -> Int = \value#47 : Data -> let candidate#48 : Int = (\coerce#50 : 'erased -> coerce#50) value#47 in +let destruct#49 : int = unIData candidate#48 in +candidate#48 in +let validate#17 : Data -> List Data = \value#24 : Data -> let candidate#25 : List Data = (\coerce#29 : 'erased -> coerce#29) value#24 in +applyForward#26 (applyForward#26 (applyForward#26 candidate#25 unListData) (mapList#27 validate#28)) listData in +let fromInt#20 : int -> int = \value#30 : int -> value#30 in let a#2 : Data = (\field#18 : list Data -> listData field#18) (mkCons ((\field#19 : int -> iData field#19) (fromInt#20 1)) (mkCons ((\field#21 : int -> iData field#21) (fromInt#20 2)) (mkCons ((\field#22 : int -> iData field#22) (fromInt#20 3)) (con (list data) [])))) in let checked#3 : List Int = validate#17 a#2 in -let projection#49 : list Data = unListData checked#3 in -let values#4 : list int = map#16 unIData projection#49 in +let projection#51 : list Data = unListData checked#3 in +let values#4 : list int = map#16 unIData projection#51 in let match#6 : list int = values#4 in let leaf#11 : 'erased = delay error in let main#1 : bool = case@List match#6 of @@ -40,86 +40,89 @@ main#1 (program 1.1.0 [(lam mapList_23 [(lam map_16 - [(lam mapList_26 - [(lam validate_27 - [(lam validate_17 - [(lam fromInt_20 - [(lam a_2 - [(lam checked_3 - [(lam projection_49 - [(lam values_4 - [(lam match_6 - [(lam leaf_11 - [(lam main_1 main_1) - (case - match_6 - (lam field_12 - (lam field_13 - (case - field_13 - (lam field_14 (lam field_15 (force leaf_11))) - [(lam h_5 [[(builtin equalsInteger) h_5] h_5]) field_12]))) - (force leaf_11))]) - (delay (error))]) - values_4]) - [[map_16 (builtin unIData)] projection_49]]) - [(builtin unListData) checked_3]]) - [validate_17 a_2]]) - [(lam field_18 [(builtin listData) field_18]) - [[(force (builtin mkCons)) - [(lam field_19 [(builtin iData) field_19]) [fromInt_20 (con integer 1)]]] + [(lam applyForward_26 + [(lam mapList_27 + [(lam validate_28 + [(lam validate_17 + [(lam fromInt_20 + [(lam a_2 + [(lam checked_3 + [(lam projection_51 + [(lam values_4 + [(lam match_6 + [(lam leaf_11 + [(lam main_1 main_1) + (case + match_6 + (lam field_12 + (lam field_13 + (case + field_13 + (lam field_14 (lam field_15 (force leaf_11))) + [(lam h_5 [[(builtin equalsInteger) h_5] h_5]) field_12]))) + (force leaf_11))]) + (delay (error))]) + values_4]) + [[map_16 (builtin unIData)] projection_51]]) + [(builtin unListData) checked_3]]) + [validate_17 a_2]]) + [(lam field_18 [(builtin listData) field_18]) [[(force (builtin mkCons)) - [(lam field_21 [(builtin iData) field_21]) [fromInt_20 (con integer 2)]]] + [(lam field_19 [(builtin iData) field_19]) [fromInt_20 (con integer 1)]]] [[(force (builtin mkCons)) - [(lam field_22 [(builtin iData) field_22]) [fromInt_20 (con integer 3)]]] - (con (list data) [])]]]]]) - (lam value_29 value_29)]) - (lam value_24 - [(lam candidate_25 - [(lam projection_48 - [(builtin listData) [[mapList_26 validate_27] projection_48]]) - [(builtin unListData) candidate_25]]) - [(lam coerce_28 coerce_28) value_24]])]) - (lam value_44 - [(lam candidate_45 - [(lam destruct_46 candidate_45) [(builtin unIData) candidate_45]]) - [(lam coerce_47 coerce_47) value_44]])]) - (lam f_37 - (lam values_38 - [[(lam self_7 [self_7 self_7]) - (lam self_7 - (lam values_38 - [(lam match_41 - (case - match_41 - (lam field_42 - (lam field_43 - [(lam first_39 - [(lam rest_40 - [[(force (builtin mkCons)) [f_37 first_39]] [[self_7 self_7] rest_40]]) - field_43]) - field_42])) - (con (list data) []))) - values_38]))] - values_38]))]) + [(lam field_21 [(builtin iData) field_21]) [fromInt_20 (con integer 2)]]] + [[(force (builtin mkCons)) + [(lam field_22 [(builtin iData) field_22]) [fromInt_20 (con integer 3)]]] + (con (list data) [])]]]]]) + (lam value_30 value_30)]) + (lam value_24 + [(lam candidate_25 + [[applyForward_26 + [[applyForward_26 [[applyForward_26 candidate_25] (builtin unListData)]] + [mapList_27 validate_28]]] + (builtin listData)]) + [(lam coerce_29 coerce_29) value_24]])]) + (lam value_47 + [(lam candidate_48 + [(lam destruct_49 candidate_48) [(builtin unIData) candidate_48]]) + [(lam coerce_50 coerce_50) value_47]])]) + (lam f_40 + (lam values_41 + [[(lam self_7 [self_7 self_7]) + (lam self_7 + (lam values_41 + [(lam match_44 + (case + match_44 + (lam field_45 + (lam field_46 + [(lam first_42 + [(lam rest_43 + [[(force (builtin mkCons)) [f_40 first_42]] [[self_7 self_7] rest_43]]) + field_46]) + field_45])) + (con (list data) []))) + values_41]))] + values_41]))]) + (lam x_38 (lam f_39 [f_39 x_38]))]) mapList_23]) - (lam f_30 - (lam values_31 + (lam f_31 + (lam values_32 [[(lam self_8 [self_8 self_8]) (lam self_8 - (lam values_31 - [(lam match_34 + (lam values_32 + [(lam match_35 (case - match_34 - (lam field_35 - (lam field_36 - [(lam x_32 - [(lam xs_33 [[(force (builtin mkCons)) [f_30 x_32]] [[self_8 self_8] xs_33]]) - field_36]) - field_35])) + match_35 + (lam field_36 + (lam field_37 + [(lam x_33 + [(lam xs_34 [[(force (builtin mkCons)) [f_31 x_33]] [[self_8 self_8] xs_34]]) + field_37]) + field_36])) (con (list integer) []))) - values_31]))] - values_31]))]) + values_32]))] + values_32]))]) --- result error: ExplicitErrorTerm --- logs diff --git a/crates/nash-codegen/src/build/tests/source/snapshots/nash_codegen__build__tests__source__patterns__expect_head_cast_data_with_tail.snap b/crates/nash-codegen/src/build/tests/source/snapshots/nash_codegen__build__tests__source__patterns__expect_head_cast_data_with_tail.snap index 6f0df75c..89b48537 100644 --- a/crates/nash-codegen/src/build/tests/source/snapshots/nash_codegen__build__tests__source__patterns__expect_head_cast_data_with_tail.snap +++ b/crates/nash-codegen/src/build/tests/source/snapshots/nash_codegen__build__tests__source__patterns__expect_head_cast_data_with_tail.snap @@ -4,30 +4,30 @@ description: "module Main exposing (..)\nimport Primitive exposing (..)\nimport --- --- core let fromBool#7 : bool -> bool = \value#25 : bool -> value#25 in -let mapList#26 : ('erased -> int) -> list 'erased -> list int = \(f#33 : 'erased -> int) values#34 : list 'erased -> (\self#10 : 'erased -> self#10 self#10) (\self#10 : 'erased values#34 : list 'erased -> let match#37 : list 'erased = values#34 in -case@List match#37 of +let mapList#26 : ('erased -> int) -> list 'erased -> list int = \(f#34 : 'erased -> int) values#35 : list 'erased -> (\self#10 : 'erased -> self#10 self#10) (\self#10 : 'erased values#35 : list 'erased -> let match#38 : list 'erased = values#35 in +case@List match#38 of Nil -> (con (list integer) []) - Cons field#38 field#39 -> let x#35 : 'erased = field#38 in - let xs#36 : list 'erased = field#39 in - mkCons (f#33 x#35) (self#10 self#10 xs#36)) values#34 in + Cons field#39 field#40 -> let x#36 : 'erased = field#39 in + let xs#37 : list 'erased = field#40 in + mkCons (f#34 x#36) (self#10 self#10 xs#37)) values#35 in let map#18 : ('erased -> int) -> list 'erased -> list int = mapList#26 in -let mapList#29 : ('erased -> Data) -> list 'erased -> list Data = \(f#40 : 'erased -> Data) values#41 : list 'erased -> (\self#9 : 'erased -> self#9 self#9) (\self#9 : 'erased values#41 : list 'erased -> let match#44 : list 'erased = values#41 in -case@List match#44 of +let applyForward#29 : 'erased -> ('erased -> 'erased) -> 'erased = \x#41 : 'erased (f#42 : 'erased -> 'erased) -> f#42 x#41 in +let mapList#30 : ('erased -> Data) -> list 'erased -> list Data = \(f#43 : 'erased -> Data) values#44 : list 'erased -> (\self#9 : 'erased -> self#9 self#9) (\self#9 : 'erased values#44 : list 'erased -> let match#47 : list 'erased = values#44 in +case@List match#47 of Nil -> (con (list data) []) - Cons field#45 field#46 -> let first#42 : 'erased = field#45 in - let rest#43 : list 'erased = field#46 in - mkCons (f#40 first#42) (self#9 self#9 rest#43)) values#41 in -let validate#30 : Data -> Int = \value#47 : Data -> let candidate#48 : Int = (\coerce#50 : 'erased -> coerce#50) value#47 in -let destruct#49 : int = unIData candidate#48 in -candidate#48 in -let validate#19 : Data -> List Data = \value#27 : Data -> let candidate#28 : List Data = (\coerce#31 : 'erased -> coerce#31) value#27 in -let projection#51 : list Data = unListData candidate#28 in -listData (mapList#29 validate#30 projection#51) in -let fromInt#22 : int -> int = \value#32 : int -> value#32 in + Cons field#48 field#49 -> let first#45 : 'erased = field#48 in + let rest#46 : list 'erased = field#49 in + mkCons (f#43 first#45) (self#9 self#9 rest#46)) values#44 in +let validate#31 : Data -> Int = \value#50 : Data -> let candidate#51 : Int = (\coerce#53 : 'erased -> coerce#53) value#50 in +let destruct#52 : int = unIData candidate#51 in +candidate#51 in +let validate#19 : Data -> List Data = \value#27 : Data -> let candidate#28 : List Data = (\coerce#32 : 'erased -> coerce#32) value#27 in +applyForward#29 (applyForward#29 (applyForward#29 candidate#28 unListData) (mapList#30 validate#31)) listData in +let fromInt#22 : int -> int = \value#33 : int -> value#33 in let a#2 : Data = (\field#20 : list Data -> listData field#20) (mkCons ((\field#21 : int -> iData field#21) (fromInt#22 1)) (mkCons ((\field#23 : int -> iData field#23) (fromInt#22 2)) (mkCons ((\field#24 : int -> iData field#24) (fromInt#22 3)) (con (list data) [])))) in let checked#3 : List Int = validate#19 a#2 in -let projection#52 : list Data = unListData checked#3 in -let values#4 : list int = map#18 unIData projection#52 in +let projection#54 : list Data = unListData checked#3 in +let values#4 : list int = map#18 unIData projection#54 in let match#8 : list int = values#4 in let leaf#13 : 'erased = delay error in let main#1 : bool = case@List match#8 of @@ -45,99 +45,102 @@ main#1 [(lam fromBool_7 [(lam mapList_26 [(lam map_18 - [(lam mapList_29 - [(lam validate_30 - [(lam validate_19 - [(lam fromInt_22 - [(lam a_2 - [(lam checked_3 - [(lam projection_52 - [(lam values_4 - [(lam match_8 - [(lam leaf_13 - [(lam main_1 main_1) - (case - match_8 - (lam field_14 - (lam field_15 - (case - field_15 - (lam field_16 - (lam field_17 - [(lam h_5 - [(lam j_6 - (case - [[(builtin equalsInteger) h_5] h_5] - [fromBool_7 (con bool False)] - [[(builtin equalsInteger) j_6] j_6])) - field_16]) - field_14])) - (force leaf_13)))) - (force leaf_13))]) - (delay (error))]) - values_4]) - [[map_18 (builtin unIData)] projection_52]]) - [(builtin unListData) checked_3]]) - [validate_19 a_2]]) - [(lam field_20 [(builtin listData) field_20]) - [[(force (builtin mkCons)) - [(lam field_21 [(builtin iData) field_21]) [fromInt_22 (con integer 1)]]] + [(lam applyForward_29 + [(lam mapList_30 + [(lam validate_31 + [(lam validate_19 + [(lam fromInt_22 + [(lam a_2 + [(lam checked_3 + [(lam projection_54 + [(lam values_4 + [(lam match_8 + [(lam leaf_13 + [(lam main_1 main_1) + (case + match_8 + (lam field_14 + (lam field_15 + (case + field_15 + (lam field_16 + (lam field_17 + [(lam h_5 + [(lam j_6 + (case + [[(builtin equalsInteger) h_5] h_5] + [fromBool_7 (con bool False)] + [[(builtin equalsInteger) j_6] j_6])) + field_16]) + field_14])) + (force leaf_13)))) + (force leaf_13))]) + (delay (error))]) + values_4]) + [[map_18 (builtin unIData)] projection_54]]) + [(builtin unListData) checked_3]]) + [validate_19 a_2]]) + [(lam field_20 [(builtin listData) field_20]) [[(force (builtin mkCons)) - [(lam field_23 [(builtin iData) field_23]) [fromInt_22 (con integer 2)]]] + [(lam field_21 [(builtin iData) field_21]) [fromInt_22 (con integer 1)]]] [[(force (builtin mkCons)) - [(lam field_24 [(builtin iData) field_24]) [fromInt_22 (con integer 3)]]] - (con (list data) [])]]]]]) - (lam value_32 value_32)]) - (lam value_27 - [(lam candidate_28 - [(lam projection_51 - [(builtin listData) [[mapList_29 validate_30] projection_51]]) - [(builtin unListData) candidate_28]]) - [(lam coerce_31 coerce_31) value_27]])]) - (lam value_47 - [(lam candidate_48 - [(lam destruct_49 candidate_48) [(builtin unIData) candidate_48]]) - [(lam coerce_50 coerce_50) value_47]])]) - (lam f_40 - (lam values_41 - [[(lam self_9 [self_9 self_9]) - (lam self_9 - (lam values_41 - [(lam match_44 - (case - match_44 - (lam field_45 - (lam field_46 - [(lam first_42 - [(lam rest_43 - [[(force (builtin mkCons)) [f_40 first_42]] [[self_9 self_9] rest_43]]) - field_46]) - field_45])) - (con (list data) []))) - values_41]))] - values_41]))]) + [(lam field_23 [(builtin iData) field_23]) [fromInt_22 (con integer 2)]]] + [[(force (builtin mkCons)) + [(lam field_24 [(builtin iData) field_24]) [fromInt_22 (con integer 3)]]] + (con (list data) [])]]]]]) + (lam value_33 value_33)]) + (lam value_27 + [(lam candidate_28 + [[applyForward_29 + [[applyForward_29 [[applyForward_29 candidate_28] (builtin unListData)]] + [mapList_30 validate_31]]] + (builtin listData)]) + [(lam coerce_32 coerce_32) value_27]])]) + (lam value_50 + [(lam candidate_51 + [(lam destruct_52 candidate_51) [(builtin unIData) candidate_51]]) + [(lam coerce_53 coerce_53) value_50]])]) + (lam f_43 + (lam values_44 + [[(lam self_9 [self_9 self_9]) + (lam self_9 + (lam values_44 + [(lam match_47 + (case + match_47 + (lam field_48 + (lam field_49 + [(lam first_45 + [(lam rest_46 + [[(force (builtin mkCons)) [f_43 first_45]] [[self_9 self_9] rest_46]]) + field_49]) + field_48])) + (con (list data) []))) + values_44]))] + values_44]))]) + (lam x_41 (lam f_42 [f_42 x_41]))]) mapList_26]) - (lam f_33 - (lam values_34 + (lam f_34 + (lam values_35 [[(lam self_10 [self_10 self_10]) (lam self_10 - (lam values_34 - [(lam match_37 + (lam values_35 + [(lam match_38 (case - match_37 - (lam field_38 - (lam field_39 - [(lam x_35 - [(lam xs_36 [[(force (builtin mkCons)) [f_33 x_35]] [[self_10 self_10] xs_36]]) - field_39]) - field_38])) + match_38 + (lam field_39 + (lam field_40 + [(lam x_36 + [(lam xs_37 [[(force (builtin mkCons)) [f_34 x_36]] [[self_10 self_10] xs_37]]) + field_40]) + field_39])) (con (list integer) []))) - values_34]))] - values_34]))]) + values_35]))] + values_35]))]) (lam value_25 value_25)]) --- result (con bool True) --- logs [] --- budget -cpu: 6821955, memory: 36906 +cpu: 7109955, memory: 38706 diff --git a/crates/nash-codegen/src/build/tests/source/snapshots/nash_codegen__build__tests__source__patterns__validate_list_rejects_wrong_element.snap b/crates/nash-codegen/src/build/tests/source/snapshots/nash_codegen__build__tests__source__patterns__validate_list_rejects_wrong_element.snap index 079e87c7..1577f1b4 100644 --- a/crates/nash-codegen/src/build/tests/source/snapshots/nash_codegen__build__tests__source__patterns__validate_list_rejects_wrong_element.snap +++ b/crates/nash-codegen/src/build/tests/source/snapshots/nash_codegen__build__tests__source__patterns__validate_list_rejects_wrong_element.snap @@ -3,64 +3,68 @@ source: crates/nash-codegen/src/build/tests/source/patterns.rs description: "module Main exposing (..)\nimport Data exposing (ToData, Validate)\nmain : List Int\nmain = validate (toData (Builtin.listData [Builtin.bData #\"ff\"]))\n" --- --- core -let mapList#7 : ('erased -> Data) -> list 'erased -> list Data = \(f#12 : 'erased -> Data) values#13 : list 'erased -> (\self#24 : 'erased -> self#24 self#24) (\self#24 : 'erased values#13 : list 'erased -> let match#16 : list 'erased = values#13 in -case@List match#16 of +let applyForward#7 : 'erased -> ('erased -> 'erased) -> 'erased = \x#13 : 'erased (f#14 : 'erased -> 'erased) -> f#14 x#13 in +let mapList#8 : ('erased -> Data) -> list 'erased -> list Data = \(f#15 : 'erased -> Data) values#16 : list 'erased -> (\self#26 : 'erased -> self#26 self#26) (\self#26 : 'erased values#16 : list 'erased -> let match#19 : list 'erased = values#16 in +case@List match#19 of Nil -> (con (list data) []) - Cons field#17 field#18 -> let first#14 : 'erased = field#17 in - let rest#15 : list 'erased = field#18 in - mkCons (f#12 first#14) (self#24 self#24 rest#15)) values#13 in -let validate#8 : Data -> Int = \value#19 : Data -> let candidate#20 : Int = (\coerce#22 : 'erased -> coerce#22) value#19 in -let destruct#21 : int = unIData candidate#20 in -candidate#20 in -let validate#2 : Data -> List Data = \value#5 : Data -> let candidate#6 : List Data = (\coerce#9 : 'erased -> coerce#9) value#5 in -let projection#23 : list Data = unListData candidate#6 in -listData (mapList#7 validate#8 projection#23) in -let toData#3 : 'erased -> Data = \coerce#10 : 'erased -> coerce#10 in -let fromBytes#4 : bytes -> bytes = \value#11 : bytes -> value#11 in + Cons field#20 field#21 -> let first#17 : 'erased = field#20 in + let rest#18 : list 'erased = field#21 in + mkCons (f#15 first#17) (self#26 self#26 rest#18)) values#16 in +let validate#9 : Data -> Int = \value#22 : Data -> let candidate#23 : Int = (\coerce#25 : 'erased -> coerce#25) value#22 in +let destruct#24 : int = unIData candidate#23 in +candidate#23 in +let validate#2 : Data -> List Data = \value#5 : Data -> let candidate#6 : List Data = (\coerce#10 : 'erased -> coerce#10) value#5 in +applyForward#7 (applyForward#7 (applyForward#7 candidate#6 unListData) (mapList#8 validate#9)) listData in +let toData#3 : 'erased -> Data = \coerce#11 : 'erased -> coerce#11 in +let fromBytes#4 : bytes -> bytes = \value#12 : bytes -> value#12 in let main#1 : List Int = validate#2 (toData#3 (listData (mkCons (bData (fromBytes#4 (con bytestring #ff))) (con (list data) [])))) in main#1 --- uplc (program 1.1.0 - [(lam mapList_7 - [(lam validate_8 - [(lam validate_2 - [(lam toData_3 - [(lam fromBytes_4 - [(lam main_1 main_1) - [validate_2 - [toData_3 - [(builtin listData) - [[(force (builtin mkCons)) [(builtin bData) [fromBytes_4 (con bytestring #ff)]]] - (con (list data) [])]]]]]) - (lam value_11 value_11)]) - (lam coerce_10 coerce_10)]) - (lam value_5 - [(lam candidate_6 - [(lam projection_23 [(builtin listData) [[mapList_7 validate_8] projection_23]]) - [(builtin unListData) candidate_6]]) - [(lam coerce_9 coerce_9) value_5]])]) - (lam value_19 - [(lam candidate_20 - [(lam destruct_21 candidate_20) [(builtin unIData) candidate_20]]) - [(lam coerce_22 coerce_22) value_19]])]) - (lam f_12 - (lam values_13 - [[(lam self_24 [self_24 self_24]) - (lam self_24 - (lam values_13 - [(lam match_16 - (case - match_16 - (lam field_17 - (lam field_18 - [(lam first_14 - [(lam rest_15 - [[(force (builtin mkCons)) [f_12 first_14]] [[self_24 self_24] rest_15]]) - field_18]) - field_17])) - (con (list data) []))) - values_13]))] - values_13]))]) + [(lam applyForward_7 + [(lam mapList_8 + [(lam validate_9 + [(lam validate_2 + [(lam toData_3 + [(lam fromBytes_4 + [(lam main_1 main_1) + [validate_2 + [toData_3 + [(builtin listData) + [[(force (builtin mkCons)) [(builtin bData) [fromBytes_4 (con bytestring #ff)]]] + (con (list data) [])]]]]]) + (lam value_12 value_12)]) + (lam coerce_11 coerce_11)]) + (lam value_5 + [(lam candidate_6 + [[applyForward_7 + [[applyForward_7 [[applyForward_7 candidate_6] (builtin unListData)]] + [mapList_8 validate_9]]] + (builtin listData)]) + [(lam coerce_10 coerce_10) value_5]])]) + (lam value_22 + [(lam candidate_23 + [(lam destruct_24 candidate_23) [(builtin unIData) candidate_23]]) + [(lam coerce_25 coerce_25) value_22]])]) + (lam f_15 + (lam values_16 + [[(lam self_26 [self_26 self_26]) + (lam self_26 + (lam values_16 + [(lam match_19 + (case + match_19 + (lam field_20 + (lam field_21 + [(lam first_17 + [(lam rest_18 + [[(force (builtin mkCons)) [f_15 first_17]] [[self_26 self_26] rest_18]]) + field_21]) + field_20])) + (con (list data) []))) + values_16]))] + values_16]))]) + (lam x_13 (lam f_14 [f_14 x_13]))]) --- result error: Runtime(MalFormedData(ByteString([255]))) --- logs diff --git a/crates/nash-codegen/src/build/tests/source/snapshots/nash_codegen__build__tests__source__patterns__validate_map_rejects_wrong_key.snap b/crates/nash-codegen/src/build/tests/source/snapshots/nash_codegen__build__tests__source__patterns__validate_map_rejects_wrong_key.snap index 4aa1546d..d6568004 100644 --- a/crates/nash-codegen/src/build/tests/source/snapshots/nash_codegen__build__tests__source__patterns__validate_map_rejects_wrong_key.snap +++ b/crates/nash-codegen/src/build/tests/source/snapshots/nash_codegen__build__tests__source__patterns__validate_map_rejects_wrong_key.snap @@ -3,91 +3,96 @@ source: crates/nash-codegen/src/build/tests/source/patterns.rs description: "module Main exposing (..)\nimport Data exposing (ToData, Validate)\nmain : Map Int Bytes\nmain = validate (toData (Builtin.mapData [Builtin.mkPairData (Builtin.bData #\"aa\") (Builtin.bData #\"bb\")]))\n" --- --- core -let mapList#7 : ('erased -> pair Data Data) -> list 'erased -> list (pair Data Data) = \(f#12 : 'erased -> pair Data Data) values#13 : list 'erased -> (\self#35 : 'erased -> self#35 self#35) (\self#35 : 'erased values#13 : list 'erased -> let match#16 : list 'erased = values#13 in -case@List match#16 of +let applyForward#7 : 'erased -> ('erased -> 'erased) -> 'erased = \x#13 : 'erased (f#14 : 'erased -> 'erased) -> f#14 x#13 in +let mapList#8 : ('erased -> pair Data Data) -> list 'erased -> list (pair Data Data) = \(f#15 : 'erased -> pair Data Data) values#16 : list 'erased -> (\self#38 : 'erased -> self#38 self#38) (\self#38 : 'erased values#16 : list 'erased -> let match#19 : list 'erased = values#16 in +case@List match#19 of Nil -> (con (list (pair data data)) []) - Cons field#17 field#18 -> let first#14 : 'erased = field#17 in - let rest#15 : list 'erased = field#18 in - mkCons (f#12 first#14) (self#35 self#35 rest#15)) values#13 in -let validate#22 : Data -> Int = \value#27 : Data -> let candidate#28 : Int = (\coerce#30 : 'erased -> coerce#30) value#27 in -let destruct#29 : int = unIData candidate#28 in -candidate#28 in -let validate#23 : Data -> Bytes = \value#31 : Data -> let candidate#32 : Bytes = (\coerce#34 : 'erased -> coerce#34) value#31 in -let destruct#33 : bytes = unBData candidate#32 in -candidate#32 in -let pairValidate#8 : pair Data Data -> pair Data Data = \arg#19 : pair Data Data -> let match#24 : pair Data Data = arg#19 in -case@Pair match#24 of - Pair field#25 field#26 -> let key#20 : Data = field#25 in - let value#21 : Data = field#26 in - mkPairData (validate#22 key#20) (validate#23 value#21) in -let validate#2 : Data -> Map Data Data = \value#5 : Data -> let candidate#6 : Map Data Data = (\coerce#9 : 'erased -> coerce#9) value#5 in -mapData (mapList#7 pairValidate#8 (unMapData candidate#6)) in -let toData#3 : 'erased -> Data = \coerce#10 : 'erased -> coerce#10 in -let fromBytes#4 : bytes -> bytes = \value#11 : bytes -> value#11 in + Cons field#20 field#21 -> let first#17 : 'erased = field#20 in + let rest#18 : list 'erased = field#21 in + mkCons (f#15 first#17) (self#38 self#38 rest#18)) values#16 in +let validate#25 : Data -> Int = \value#30 : Data -> let candidate#31 : Int = (\coerce#33 : 'erased -> coerce#33) value#30 in +let destruct#32 : int = unIData candidate#31 in +candidate#31 in +let validate#26 : Data -> Bytes = \value#34 : Data -> let candidate#35 : Bytes = (\coerce#37 : 'erased -> coerce#37) value#34 in +let destruct#36 : bytes = unBData candidate#35 in +candidate#35 in +let pairValidate#9 : pair Data Data -> pair Data Data = \arg#22 : pair Data Data -> let match#27 : pair Data Data = arg#22 in +case@Pair match#27 of + Pair field#28 field#29 -> let key#23 : Data = field#28 in + let value#24 : Data = field#29 in + mkPairData (validate#25 key#23) (validate#26 value#24) in +let validate#2 : Data -> Map Data Data = \value#5 : Data -> let candidate#6 : Map Data Data = (\coerce#10 : 'erased -> coerce#10) value#5 in +applyForward#7 (applyForward#7 (applyForward#7 candidate#6 unMapData) (mapList#8 pairValidate#9)) mapData in +let toData#3 : 'erased -> Data = \coerce#11 : 'erased -> coerce#11 in +let fromBytes#4 : bytes -> bytes = \value#12 : bytes -> value#12 in let main#1 : Map Int Bytes = validate#2 (toData#3 (mapData (mkCons (mkPairData (bData (fromBytes#4 (con bytestring #aa))) (bData (fromBytes#4 (con bytestring #bb)))) (con (list (pair data data)) [])))) in main#1 --- uplc (program 1.1.0 - [(lam mapList_7 - [(lam validate_22 - [(lam validate_23 - [(lam pairValidate_8 - [(lam validate_2 - [(lam toData_3 - [(lam fromBytes_4 - [(lam main_1 main_1) - [validate_2 - [toData_3 - [(builtin mapData) - [[(force (builtin mkCons)) - [[(builtin mkPairData) [(builtin bData) [fromBytes_4 (con bytestring #aa)]]] - [(builtin bData) [fromBytes_4 (con bytestring #bb)]]]] - (con (list (pair data data)) [])]]]]]) - (lam value_11 value_11)]) - (lam coerce_10 coerce_10)]) - (lam value_5 - [(lam candidate_6 - [(builtin mapData) - [[mapList_7 pairValidate_8] [(builtin unMapData) candidate_6]]]) - [(lam coerce_9 coerce_9) value_5]])]) - (lam arg_19 - [(lam match_24 - (case - match_24 - (lam field_25 - (lam field_26 - [(lam key_20 - [(lam value_21 - [[(builtin mkPairData) [validate_22 key_20]] [validate_23 value_21]]) - field_26]) - field_25])))) - arg_19])]) - (lam value_31 - [(lam candidate_32 - [(lam destruct_33 candidate_32) [(builtin unBData) candidate_32]]) - [(lam coerce_34 coerce_34) value_31]])]) - (lam value_27 - [(lam candidate_28 - [(lam destruct_29 candidate_28) [(builtin unIData) candidate_28]]) - [(lam coerce_30 coerce_30) value_27]])]) - (lam f_12 - (lam values_13 - [[(lam self_35 [self_35 self_35]) - (lam self_35 - (lam values_13 - [(lam match_16 + [(lam applyForward_7 + [(lam mapList_8 + [(lam validate_25 + [(lam validate_26 + [(lam pairValidate_9 + [(lam validate_2 + [(lam toData_3 + [(lam fromBytes_4 + [(lam main_1 main_1) + [validate_2 + [toData_3 + [(builtin mapData) + [[(force (builtin mkCons)) + [[(builtin mkPairData) [(builtin bData) [fromBytes_4 (con bytestring #aa)]]] + [(builtin bData) [fromBytes_4 (con bytestring #bb)]]]] + (con (list (pair data data)) [])]]]]]) + (lam value_12 value_12)]) + (lam coerce_11 coerce_11)]) + (lam value_5 + [(lam candidate_6 + [[applyForward_7 + [[applyForward_7 [[applyForward_7 candidate_6] (builtin unMapData)]] + [mapList_8 pairValidate_9]]] + (builtin mapData)]) + [(lam coerce_10 coerce_10) value_5]])]) + (lam arg_22 + [(lam match_27 (case - match_16 - (lam field_17 - (lam field_18 - [(lam first_14 - [(lam rest_15 - [[(force (builtin mkCons)) [f_12 first_14]] [[self_35 self_35] rest_15]]) - field_18]) - field_17])) - (con (list (pair data data)) []))) - values_13]))] - values_13]))]) + match_27 + (lam field_28 + (lam field_29 + [(lam key_23 + [(lam value_24 + [[(builtin mkPairData) [validate_25 key_23]] [validate_26 value_24]]) + field_29]) + field_28])))) + arg_22])]) + (lam value_34 + [(lam candidate_35 + [(lam destruct_36 candidate_35) [(builtin unBData) candidate_35]]) + [(lam coerce_37 coerce_37) value_34]])]) + (lam value_30 + [(lam candidate_31 + [(lam destruct_32 candidate_31) [(builtin unIData) candidate_31]]) + [(lam coerce_33 coerce_33) value_30]])]) + (lam f_15 + (lam values_16 + [[(lam self_38 [self_38 self_38]) + (lam self_38 + (lam values_16 + [(lam match_19 + (case + match_19 + (lam field_20 + (lam field_21 + [(lam first_17 + [(lam rest_18 + [[(force (builtin mkCons)) [f_15 first_17]] [[self_38 self_38] rest_18]]) + field_21]) + field_20])) + (con (list (pair data data)) []))) + values_16]))] + values_16]))]) + (lam x_13 (lam f_14 [f_14 x_13]))]) --- result error: Runtime(MalFormedData(ByteString([170]))) --- logs diff --git a/crates/nash-codegen/src/tests/snapshots/nash_codegen__tests__integration__assertion_json_preserves_nested_call_delimiters_and_string_parentheses_1_V3_Silent.snap b/crates/nash-codegen/src/tests/snapshots/nash_codegen__tests__integration__assertion_json_preserves_nested_call_delimiters_and_string_parentheses_1_V3_Silent.snap index 2e7bd10e..fcb44b87 100644 --- a/crates/nash-codegen/src/tests/snapshots/nash_codegen__tests__integration__assertion_json_preserves_nested_call_delimiters_and_string_parentheses_1_V3_Silent.snap +++ b/crates/nash-codegen/src/tests/snapshots/nash_codegen__tests__integration__assertion_json_preserves_nested_call_delimiters_and_string_parentheses_1_V3_Silent.snap @@ -37,200 +37,218 @@ description: "module Main exposing (..)\nimport Primitive exposing (..)\nimport [(lam i27 [(lam i28 [(lam i29 - [[[i13 i2] - [[(lam i30 - [(lam i31 - [(lam i32 i32) - (lam i32 - (case - [[(builtin lessThanInteger) i32] [i28 (con integer 0)]] - [(builtin decodeUtf8) [i30 i32]] - [[(builtin appendString) [i31 i27]] - [(builtin decodeUtf8) - [i30 [[(builtin subtractInteger) [i28 (con integer 0)]] i32]]]]))]) - (lam i31 i31)]) - [(lam i30 [i30 i30]) - (lam i30 + [(lam i30 + [[[i13 i2] + [[(lam i31 + [(lam i32 + [(lam i33 i33) + (lam i33 + (case + [[(builtin lessThanInteger) i33] [i29 (con integer 0)]] + [[i28 [[i28 i33] i31]] (builtin decodeUtf8)] + [[i28 + [[i28 [[i28 [[i28 i33] [(builtin subtractInteger) [i29 (con integer 0)]]]] i31]] + (builtin decodeUtf8)]] + [(builtin appendString) [i32 i27]]]))]) + (lam i32 i32)]) + [(lam i31 [i31 i31]) (lam i31 - (case - [[(builtin lessThanInteger) i31] [i28 (con integer 10)]] - [[(builtin appendByteString) - [[i30 i30] [[(builtin quotientInteger) i31] [i28 (con integer 10)]]]] - [[(builtin consByteString) - [[(builtin addInteger) [i28 (con integer 48)]] - [[(builtin remainderInteger) i31] [i28 (con integer 10)]]]] - [i29 (con bytestring #)]]] - [[(builtin consByteString) [[(builtin addInteger) [i28 (con integer 48)]] i31]] - [i29 (con bytestring #)]])))]] - i15]] - (lam i30 - [(lam i31 + (lam i32 + (case + [[(builtin lessThanInteger) i32] [i29 (con integer 10)]] + [[(builtin appendByteString) + [[i31 i31] [[(builtin quotientInteger) i32] [i29 (con integer 10)]]]] + [[(builtin consByteString) + [[(builtin addInteger) [i29 (con integer 48)]] + [[(builtin remainderInteger) i32] [i29 (con integer 10)]]]] + [i30 (con bytestring #)]]] + [[(builtin consByteString) [[(builtin addInteger) [i29 (con integer 48)]] i32]] + [i30 (con bytestring #)]])))]] + i15]] + (lam i31 [(lam i32 [(lam i33 - [[[i13 i3] - [[(lam i34 - [(lam i35 - [(lam i36 i36) - (lam i36 - (case - [[(builtin lessThanInteger) i36] [i32 (con integer 0)]] - [(builtin decodeUtf8) [i34 i36]] - [[(builtin appendString) [i35 i31]] - [(builtin decodeUtf8) - [i34 [[(builtin subtractInteger) [i32 (con integer 0)]] i36]]]]))]) - (lam i35 i35)]) - [(lam i34 [i34 i34]) - (lam i34 - (lam i35 - (case - [[(builtin lessThanInteger) i35] [i32 (con integer 10)]] - [[(builtin appendByteString) - [[i34 i34] [[(builtin quotientInteger) i35] [i32 (con integer 10)]]]] - [[(builtin consByteString) - [[(builtin addInteger) [i32 (con integer 48)]] - [[(builtin remainderInteger) i35] [i32 (con integer 10)]]]] - [i33 (con bytestring #)]]] - [[(builtin consByteString) [[(builtin addInteger) [i32 (con integer 48)]] i35]] - [i33 (con bytestring #)]])))]] - i25]] - (lam i34 - [(lam i35 - [(lam i36 + [(lam i34 + [(lam i35 + [[[i13 i3] + [[(lam i36 [(lam i37 - [[[i13 i4] - [[(lam i38 - [(lam i39 - [(lam i40 i40) - (lam i40 - (case - [[(builtin lessThanInteger) i40] [i36 (con integer 0)]] - [(builtin decodeUtf8) [i38 i40]] - [[(builtin appendString) [i39 i35]] - [(builtin decodeUtf8) - [i38 [[(builtin subtractInteger) [i36 (con integer 0)]] i40]]]]))]) - (lam i39 i39)]) - [(lam i38 [i38 i38]) - (lam i38 - (lam i39 - (case - [[(builtin lessThanInteger) i39] [i36 (con integer 10)]] - [[(builtin appendByteString) - [[i38 i38] [[(builtin quotientInteger) i39] [i36 (con integer 10)]]]] - [[(builtin consByteString) - [[(builtin addInteger) [i36 (con integer 48)]] - [[(builtin remainderInteger) i39] [i36 (con integer 10)]]]] - [i37 (con bytestring #)]]] - [[(builtin consByteString) [[(builtin addInteger) [i36 (con integer 48)]] i39]] - [i37 (con bytestring #)]])))]] - i18]] + [(lam i38 i38) (lam i38 - [(lam i39 - [(lam i40 - [(lam i41 - [[[i13 i5] - [[(lam i42 - [(lam i43 - [(lam i44 i44) - (lam i44 - (case - [[(builtin lessThanInteger) i44] [i40 (con integer 0)]] - [(builtin decodeUtf8) [i42 i44]] - [[(builtin appendString) [i43 i39]] - [(builtin decodeUtf8) - [i42 [[(builtin subtractInteger) [i40 (con integer 0)]] i44]]]]))]) - (lam i43 i43)]) - [(lam i42 [i42 i42]) - (lam i42 - (lam i43 - (case - [[(builtin lessThanInteger) i43] [i40 (con integer 10)]] - [[(builtin appendByteString) - [[i42 i42] [[(builtin quotientInteger) i43] [i40 (con integer 10)]]]] - [[(builtin consByteString) - [[(builtin addInteger) [i40 (con integer 48)]] - [[(builtin remainderInteger) i43] [i40 (con integer 10)]]]] - [i41 (con bytestring #)]]] - [[(builtin consByteString) [[(builtin addInteger) [i40 (con integer 48)]] i43]] - [i41 (con bytestring #)]])))]] - i24]] - (lam i42 - [(lam i43 - [(lam i44 - [(lam i45 - [[[i13 i6] - [[(lam i46 - [(lam i47 - [(lam i48 i48) - (lam i48 - (case - [[(builtin lessThanInteger) i48] [i44 (con integer 0)]] - [(builtin decodeUtf8) [i46 i48]] - [[(builtin appendString) [i47 i43]] - [(builtin decodeUtf8) - [i46 [[(builtin subtractInteger) [i44 (con integer 0)]] i48]]]]))]) - (lam i47 i47)]) - [(lam i46 [i46 i46]) - (lam i46 - (lam i47 - (case - [[(builtin lessThanInteger) i47] [i44 (con integer 10)]] - [[(builtin appendByteString) - [[i46 i46] [[(builtin quotientInteger) i47] [i44 (con integer 10)]]]] - [[(builtin consByteString) - [[(builtin addInteger) [i44 (con integer 48)]] - [[(builtin remainderInteger) i47] [i44 (con integer 10)]]]] - [i45 (con bytestring #)]]] - [[(builtin consByteString) [[(builtin addInteger) [i44 (con integer 48)]] i47]] - [i45 (con bytestring #)]])))]] - i21]] - (lam i46 - [(lam i47 - [(lam i48 - [(lam i49 - [[[i13 i7] - [[(lam i50 - [(lam i51 - [(lam i52 i52) - (lam i52 + (case + [[(builtin lessThanInteger) i38] [i34 (con integer 0)]] + [[i33 [[i33 i38] i36]] (builtin decodeUtf8)] + [[i33 + [[i33 [[i33 [[i33 i38] [(builtin subtractInteger) [i34 (con integer 0)]]]] i36]] + (builtin decodeUtf8)]] + [(builtin appendString) [i37 i32]]]))]) + (lam i37 i37)]) + [(lam i36 [i36 i36]) + (lam i36 + (lam i37 + (case + [[(builtin lessThanInteger) i37] [i34 (con integer 10)]] + [[(builtin appendByteString) + [[i36 i36] [[(builtin quotientInteger) i37] [i34 (con integer 10)]]]] + [[(builtin consByteString) + [[(builtin addInteger) [i34 (con integer 48)]] + [[(builtin remainderInteger) i37] [i34 (con integer 10)]]]] + [i35 (con bytestring #)]]] + [[(builtin consByteString) [[(builtin addInteger) [i34 (con integer 48)]] i37]] + [i35 (con bytestring #)]])))]] + i25]] + (lam i36 + [(lam i37 + [(lam i38 + [(lam i39 + [(lam i40 + [[[i13 i4] + [[(lam i41 + [(lam i42 + [(lam i43 i43) + (lam i43 + (case + [[(builtin lessThanInteger) i43] [i39 (con integer 0)]] + [[i38 [[i38 i43] i41]] (builtin decodeUtf8)] + [[i38 + [[i38 [[i38 [[i38 i43] [(builtin subtractInteger) [i39 (con integer 0)]]]] i41]] + (builtin decodeUtf8)]] + [(builtin appendString) [i42 i37]]]))]) + (lam i42 i42)]) + [(lam i41 [i41 i41]) + (lam i41 + (lam i42 + (case + [[(builtin lessThanInteger) i42] [i39 (con integer 10)]] + [[(builtin appendByteString) + [[i41 i41] [[(builtin quotientInteger) i42] [i39 (con integer 10)]]]] + [[(builtin consByteString) + [[(builtin addInteger) [i39 (con integer 48)]] + [[(builtin remainderInteger) i42] [i39 (con integer 10)]]]] + [i40 (con bytestring #)]]] + [[(builtin consByteString) [[(builtin addInteger) [i39 (con integer 48)]] i42]] + [i40 (con bytestring #)]])))]] + i18]] + (lam i41 + [(lam i42 + [(lam i43 + [(lam i44 + [(lam i45 + [[[i13 i5] + [[(lam i46 + [(lam i47 + [(lam i48 i48) + (lam i48 + (case + [[(builtin lessThanInteger) i48] [i44 (con integer 0)]] + [[i43 [[i43 i48] i46]] (builtin decodeUtf8)] + [[i43 + [[i43 [[i43 [[i43 i48] [(builtin subtractInteger) [i44 (con integer 0)]]]] i46]] + (builtin decodeUtf8)]] + [(builtin appendString) [i47 i42]]]))]) + (lam i47 i47)]) + [(lam i46 [i46 i46]) + (lam i46 + (lam i47 + (case + [[(builtin lessThanInteger) i47] [i44 (con integer 10)]] + [[(builtin appendByteString) + [[i46 i46] [[(builtin quotientInteger) i47] [i44 (con integer 10)]]]] + [[(builtin consByteString) + [[(builtin addInteger) [i44 (con integer 48)]] + [[(builtin remainderInteger) i47] [i44 (con integer 10)]]]] + [i45 (con bytestring #)]]] + [[(builtin consByteString) [[(builtin addInteger) [i44 (con integer 48)]] i47]] + [i45 (con bytestring #)]])))]] + i24]] + (lam i46 + [(lam i47 + [(lam i48 + [(lam i49 + [(lam i50 + [[[i13 i6] + [[(lam i51 + [(lam i52 + [(lam i53 i53) + (lam i53 (case - [[(builtin lessThanInteger) i52] [i48 (con integer 0)]] - [(builtin decodeUtf8) [i50 i52]] - [[(builtin appendString) [i51 i47]] - [(builtin decodeUtf8) - [i50 [[(builtin subtractInteger) [i48 (con integer 0)]] i52]]]]))]) - (lam i51 i51)]) - [(lam i50 [i50 i50]) - (lam i50 - (lam i51 + [[(builtin lessThanInteger) i53] [i49 (con integer 0)]] + [[i48 [[i48 i53] i51]] (builtin decodeUtf8)] + [[i48 + [[i48 [[i48 [[i48 i53] [(builtin subtractInteger) [i49 (con integer 0)]]]] i51]] + (builtin decodeUtf8)]] + [(builtin appendString) [i52 i47]]]))]) + (lam i52 i52)]) + [(lam i51 [i51 i51]) + (lam i51 + (lam i52 (case - [[(builtin lessThanInteger) i51] [i48 (con integer 10)]] + [[(builtin lessThanInteger) i52] [i49 (con integer 10)]] [[(builtin appendByteString) - [[i50 i50] [[(builtin quotientInteger) i51] [i48 (con integer 10)]]]] + [[i51 i51] [[(builtin quotientInteger) i52] [i49 (con integer 10)]]]] [[(builtin consByteString) - [[(builtin addInteger) [i48 (con integer 48)]] - [[(builtin remainderInteger) i51] [i48 (con integer 10)]]]] - [i49 (con bytestring #)]]] - [[(builtin consByteString) [[(builtin addInteger) [i48 (con integer 48)]] i51]] - [i49 (con bytestring #)]])))]] - i23]] - (lam i50 (error))]) - (lam i49 i49)]) - (lam i48 i48)]) - i1])]) - (lam i45 i45)]) - (lam i44 i44)]) - i1])]) - (lam i41 i41)]) - (lam i40 i40)]) - i1])]) - (lam i37 i37)]) - (lam i36 i36)]) - i1])]) - (lam i33 i33)]) - (lam i32 i32)]) - i1])]) + [[(builtin addInteger) [i49 (con integer 48)]] + [[(builtin remainderInteger) i52] [i49 (con integer 10)]]]] + [i50 (con bytestring #)]]] + [[(builtin consByteString) [[(builtin addInteger) [i49 (con integer 48)]] i52]] + [i50 (con bytestring #)]])))]] + i21]] + (lam i51 + [(lam i52 + [(lam i53 + [(lam i54 + [(lam i55 + [[[i13 i7] + [[(lam i56 + [(lam i57 + [(lam i58 i58) + (lam i58 + (case + [[(builtin lessThanInteger) i58] [i54 (con integer 0)]] + [[i53 [[i53 i58] i56]] (builtin decodeUtf8)] + [[i53 + [[i53 [[i53 [[i53 i58] [(builtin subtractInteger) [i54 (con integer 0)]]]] i56]] + (builtin decodeUtf8)]] + [(builtin appendString) [i57 i52]]]))]) + (lam i57 i57)]) + [(lam i56 [i56 i56]) + (lam i56 + (lam i57 + (case + [[(builtin lessThanInteger) i57] [i54 (con integer 10)]] + [[(builtin appendByteString) + [[i56 i56] [[(builtin quotientInteger) i57] [i54 (con integer 10)]]]] + [[(builtin consByteString) + [[(builtin addInteger) [i54 (con integer 48)]] + [[(builtin remainderInteger) i57] [i54 (con integer 10)]]]] + [i55 (con bytestring #)]]] + [[(builtin consByteString) [[(builtin addInteger) [i54 (con integer 48)]] i57]] + [i55 (con bytestring #)]])))]] + i23]] + (lam i56 (error))]) + (lam i55 i55)]) + (lam i54 i54)]) + (lam i53 (lam i54 [i54 i53]))]) + i1])]) + (lam i50 i50)]) + (lam i49 i49)]) + (lam i48 (lam i49 [i49 i48]))]) + i1])]) + (lam i45 i45)]) + (lam i44 i44)]) + (lam i43 (lam i44 [i44 i43]))]) + i1])]) + (lam i40 i40)]) + (lam i39 i39)]) + (lam i38 (lam i39 [i39 i38]))]) + i1])]) + (lam i35 i35)]) + (lam i34 i34)]) + (lam i33 (lam i34 [i34 i33]))]) + i1])]) + (lam i30 i30)]) (lam i29 i29)]) - (lam i28 i28)]) + (lam i28 (lam i29 [i29 i28]))]) i1])] (con unit ()))) [i19 i24]]) @@ -400,266 +418,290 @@ description: "module Main exposing (..)\nimport Primitive exposing (..)\nimport [(lam i33 [(lam i34 [(lam i35 - [[[i15 i2] - [[(lam i36 - [(lam i37 - [(lam i38 i38) - (lam i38 - (case - [[(builtin lessThanInteger) i38] [i34 (con integer 0)]] - [(builtin decodeUtf8) [i36 i38]] - [[(builtin appendString) [i37 i33]] - [(builtin decodeUtf8) - [i36 [[(builtin subtractInteger) [i34 (con integer 0)]] i38]]]]))]) - (lam i37 i37)]) - [(lam i36 [i36 i36]) - (lam i36 + [(lam i36 + [[[i15 i2] + [[(lam i37 + [(lam i38 + [(lam i39 i39) + (lam i39 + (case + [[(builtin lessThanInteger) i39] [i35 (con integer 0)]] + [[i34 [[i34 i39] i37]] (builtin decodeUtf8)] + [[i34 + [[i34 [[i34 [[i34 i39] [(builtin subtractInteger) [i35 (con integer 0)]]]] i37]] + (builtin decodeUtf8)]] + [(builtin appendString) [i38 i33]]]))]) + (lam i38 i38)]) + [(lam i37 [i37 i37]) (lam i37 - (case - [[(builtin lessThanInteger) i37] [i34 (con integer 10)]] - [[(builtin appendByteString) - [[i36 i36] [[(builtin quotientInteger) i37] [i34 (con integer 10)]]]] - [[(builtin consByteString) - [[(builtin addInteger) [i34 (con integer 48)]] - [[(builtin remainderInteger) i37] [i34 (con integer 10)]]]] - [i35 (con bytestring #)]]] - [[(builtin consByteString) [[(builtin addInteger) [i34 (con integer 48)]] i37]] - [i35 (con bytestring #)]])))]] - i21]] - (lam i36 - [(lam i37 + (lam i38 + (case + [[(builtin lessThanInteger) i38] [i35 (con integer 10)]] + [[(builtin appendByteString) + [[i37 i37] [[(builtin quotientInteger) i38] [i35 (con integer 10)]]]] + [[(builtin consByteString) + [[(builtin addInteger) [i35 (con integer 48)]] + [[(builtin remainderInteger) i38] [i35 (con integer 10)]]]] + [i36 (con bytestring #)]]] + [[(builtin consByteString) [[(builtin addInteger) [i35 (con integer 48)]] i38]] + [i36 (con bytestring #)]])))]] + i21]] + (lam i37 [(lam i38 [(lam i39 - [[[i15 i3] - [[(lam i40 - [(lam i41 - [(lam i42 i42) - (lam i42 - (case - [[(builtin lessThanInteger) i42] [i38 (con integer 0)]] - [(builtin decodeUtf8) [i40 i42]] - [[(builtin appendString) [i41 i37]] - [(builtin decodeUtf8) - [i40 [[(builtin subtractInteger) [i38 (con integer 0)]] i42]]]]))]) - (lam i41 i41)]) - [(lam i40 [i40 i40]) - (lam i40 - (lam i41 - (case - [[(builtin lessThanInteger) i41] [i38 (con integer 10)]] - [[(builtin appendByteString) - [[i40 i40] [[(builtin quotientInteger) i41] [i38 (con integer 10)]]]] - [[(builtin consByteString) - [[(builtin addInteger) [i38 (con integer 48)]] - [[(builtin remainderInteger) i41] [i38 (con integer 10)]]]] - [i39 (con bytestring #)]]] - [[(builtin consByteString) [[(builtin addInteger) [i38 (con integer 48)]] i41]] - [i39 (con bytestring #)]])))]] - i18]] - (lam i40 - [(lam i41 - [(lam i42 + [(lam i40 + [(lam i41 + [[[i15 i3] + [[(lam i42 [(lam i43 - [[[i15 i4] - [[(lam i44 - [(lam i45 - [(lam i46 i46) - (lam i46 - (case - [[(builtin lessThanInteger) i46] [i42 (con integer 0)]] - [(builtin decodeUtf8) [i44 i46]] - [[(builtin appendString) [i45 i41]] - [(builtin decodeUtf8) - [i44 [[(builtin subtractInteger) [i42 (con integer 0)]] i46]]]]))]) - (lam i45 i45)]) - [(lam i44 [i44 i44]) - (lam i44 - (lam i45 - (case - [[(builtin lessThanInteger) i45] [i42 (con integer 10)]] - [[(builtin appendByteString) - [[i44 i44] [[(builtin quotientInteger) i45] [i42 (con integer 10)]]]] - [[(builtin consByteString) - [[(builtin addInteger) [i42 (con integer 48)]] - [[(builtin remainderInteger) i45] [i42 (con integer 10)]]]] - [i43 (con bytestring #)]]] - [[(builtin consByteString) [[(builtin addInteger) [i42 (con integer 48)]] i45]] - [i43 (con bytestring #)]])))]] - i20]] + [(lam i44 i44) (lam i44 - [(lam i45 - [(lam i46 - [(lam i47 - [[[i15 i5] - [[(lam i48 - [(lam i49 - [(lam i50 i50) - (lam i50 - (case - [[(builtin lessThanInteger) i50] [i46 (con integer 0)]] - [(builtin decodeUtf8) [i48 i50]] - [[(builtin appendString) [i49 i45]] - [(builtin decodeUtf8) - [i48 [[(builtin subtractInteger) [i46 (con integer 0)]] i50]]]]))]) - (lam i49 i49)]) - [(lam i48 [i48 i48]) - (lam i48 - (lam i49 - (case - [[(builtin lessThanInteger) i49] [i46 (con integer 10)]] - [[(builtin appendByteString) - [[i48 i48] [[(builtin quotientInteger) i49] [i46 (con integer 10)]]]] - [[(builtin consByteString) - [[(builtin addInteger) [i46 (con integer 48)]] - [[(builtin remainderInteger) i49] [i46 (con integer 10)]]]] - [i47 (con bytestring #)]]] - [[(builtin consByteString) [[(builtin addInteger) [i46 (con integer 48)]] i49]] - [i47 (con bytestring #)]])))]] - i31]] - (lam i48 - [(lam i49 - [(lam i50 - [(lam i51 - [[[i15 i6] - [[(lam i52 - [(lam i53 - [(lam i54 i54) - (lam i54 - (case - [[(builtin lessThanInteger) i54] [i50 (con integer 0)]] - [(builtin decodeUtf8) [i52 i54]] - [[(builtin appendString) [i53 i49]] - [(builtin decodeUtf8) - [i52 [[(builtin subtractInteger) [i50 (con integer 0)]] i54]]]]))]) - (lam i53 i53)]) - [(lam i52 [i52 i52]) - (lam i52 - (lam i53 - (case - [[(builtin lessThanInteger) i53] [i50 (con integer 10)]] - [[(builtin appendByteString) - [[i52 i52] [[(builtin quotientInteger) i53] [i50 (con integer 10)]]]] - [[(builtin consByteString) - [[(builtin addInteger) [i50 (con integer 48)]] - [[(builtin remainderInteger) i53] [i50 (con integer 10)]]]] - [i51 (con bytestring #)]]] - [[(builtin consByteString) [[(builtin addInteger) [i50 (con integer 48)]] i53]] - [i51 (con bytestring #)]])))]] - i24]] - (lam i52 - [(lam i53 - [(lam i54 - [(lam i55 - [[[i15 i7] - [[(lam i56 - [(lam i57 - [(lam i58 i58) - (lam i58 + (case + [[(builtin lessThanInteger) i44] [i40 (con integer 0)]] + [[i39 [[i39 i44] i42]] (builtin decodeUtf8)] + [[i39 + [[i39 [[i39 [[i39 i44] [(builtin subtractInteger) [i40 (con integer 0)]]]] i42]] + (builtin decodeUtf8)]] + [(builtin appendString) [i43 i38]]]))]) + (lam i43 i43)]) + [(lam i42 [i42 i42]) + (lam i42 + (lam i43 + (case + [[(builtin lessThanInteger) i43] [i40 (con integer 10)]] + [[(builtin appendByteString) + [[i42 i42] [[(builtin quotientInteger) i43] [i40 (con integer 10)]]]] + [[(builtin consByteString) + [[(builtin addInteger) [i40 (con integer 48)]] + [[(builtin remainderInteger) i43] [i40 (con integer 10)]]]] + [i41 (con bytestring #)]]] + [[(builtin consByteString) [[(builtin addInteger) [i40 (con integer 48)]] i43]] + [i41 (con bytestring #)]])))]] + i18]] + (lam i42 + [(lam i43 + [(lam i44 + [(lam i45 + [(lam i46 + [[[i15 i4] + [[(lam i47 + [(lam i48 + [(lam i49 i49) + (lam i49 + (case + [[(builtin lessThanInteger) i49] [i45 (con integer 0)]] + [[i44 [[i44 i49] i47]] (builtin decodeUtf8)] + [[i44 + [[i44 [[i44 [[i44 i49] [(builtin subtractInteger) [i45 (con integer 0)]]]] i47]] + (builtin decodeUtf8)]] + [(builtin appendString) [i48 i43]]]))]) + (lam i48 i48)]) + [(lam i47 [i47 i47]) + (lam i47 + (lam i48 + (case + [[(builtin lessThanInteger) i48] [i45 (con integer 10)]] + [[(builtin appendByteString) + [[i47 i47] [[(builtin quotientInteger) i48] [i45 (con integer 10)]]]] + [[(builtin consByteString) + [[(builtin addInteger) [i45 (con integer 48)]] + [[(builtin remainderInteger) i48] [i45 (con integer 10)]]]] + [i46 (con bytestring #)]]] + [[(builtin consByteString) [[(builtin addInteger) [i45 (con integer 48)]] i48]] + [i46 (con bytestring #)]])))]] + i20]] + (lam i47 + [(lam i48 + [(lam i49 + [(lam i50 + [(lam i51 + [[[i15 i5] + [[(lam i52 + [(lam i53 + [(lam i54 i54) + (lam i54 + (case + [[(builtin lessThanInteger) i54] [i50 (con integer 0)]] + [[i49 [[i49 i54] i52]] (builtin decodeUtf8)] + [[i49 + [[i49 [[i49 [[i49 i54] [(builtin subtractInteger) [i50 (con integer 0)]]]] i52]] + (builtin decodeUtf8)]] + [(builtin appendString) [i53 i48]]]))]) + (lam i53 i53)]) + [(lam i52 [i52 i52]) + (lam i52 + (lam i53 + (case + [[(builtin lessThanInteger) i53] [i50 (con integer 10)]] + [[(builtin appendByteString) + [[i52 i52] [[(builtin quotientInteger) i53] [i50 (con integer 10)]]]] + [[(builtin consByteString) + [[(builtin addInteger) [i50 (con integer 48)]] + [[(builtin remainderInteger) i53] [i50 (con integer 10)]]]] + [i51 (con bytestring #)]]] + [[(builtin consByteString) [[(builtin addInteger) [i50 (con integer 48)]] i53]] + [i51 (con bytestring #)]])))]] + i31]] + (lam i52 + [(lam i53 + [(lam i54 + [(lam i55 + [(lam i56 + [[[i15 i6] + [[(lam i57 + [(lam i58 + [(lam i59 i59) + (lam i59 (case - [[(builtin lessThanInteger) i58] [i54 (con integer 0)]] - [(builtin decodeUtf8) [i56 i58]] - [[(builtin appendString) [i57 i53]] - [(builtin decodeUtf8) - [i56 [[(builtin subtractInteger) [i54 (con integer 0)]] i58]]]]))]) - (lam i57 i57)]) - [(lam i56 [i56 i56]) - (lam i56 - (lam i57 + [[(builtin lessThanInteger) i59] [i55 (con integer 0)]] + [[i54 [[i54 i59] i57]] (builtin decodeUtf8)] + [[i54 + [[i54 [[i54 [[i54 i59] [(builtin subtractInteger) [i55 (con integer 0)]]]] i57]] + (builtin decodeUtf8)]] + [(builtin appendString) [i58 i53]]]))]) + (lam i58 i58)]) + [(lam i57 [i57 i57]) + (lam i57 + (lam i58 (case - [[(builtin lessThanInteger) i57] [i54 (con integer 10)]] + [[(builtin lessThanInteger) i58] [i55 (con integer 10)]] [[(builtin appendByteString) - [[i56 i56] [[(builtin quotientInteger) i57] [i54 (con integer 10)]]]] + [[i57 i57] [[(builtin quotientInteger) i58] [i55 (con integer 10)]]]] [[(builtin consByteString) - [[(builtin addInteger) [i54 (con integer 48)]] - [[(builtin remainderInteger) i57] [i54 (con integer 10)]]]] - [i55 (con bytestring #)]]] - [[(builtin consByteString) [[(builtin addInteger) [i54 (con integer 48)]] i57]] - [i55 (con bytestring #)]])))]] - i30]] - (lam i56 - [(lam i57 - [(lam i58 - [(lam i59 - [[[i15 i8] - [[(lam i60 - [(lam i61 - [(lam i62 i62) + [[(builtin addInteger) [i55 (con integer 48)]] + [[(builtin remainderInteger) i58] [i55 (con integer 10)]]]] + [i56 (con bytestring #)]]] + [[(builtin consByteString) [[(builtin addInteger) [i55 (con integer 48)]] i58]] + [i56 (con bytestring #)]])))]] + i24]] + (lam i57 + [(lam i58 + [(lam i59 + [(lam i60 + [(lam i61 + [[[i15 i7] + [[(lam i62 + [(lam i63 + [(lam i64 i64) + (lam i64 + (case + [[(builtin lessThanInteger) i64] [i60 (con integer 0)]] + [[i59 [[i59 i64] i62]] (builtin decodeUtf8)] + [[i59 + [[i59 [[i59 [[i59 i64] [(builtin subtractInteger) [i60 (con integer 0)]]]] i62]] + (builtin decodeUtf8)]] + [(builtin appendString) [i63 i58]]]))]) + (lam i63 i63)]) + [(lam i62 [i62 i62]) (lam i62 - (case - [[(builtin lessThanInteger) i62] [i58 (con integer 0)]] - [(builtin decodeUtf8) [i60 i62]] - [[(builtin appendString) [i61 i57]] - [(builtin decodeUtf8) - [i60 [[(builtin subtractInteger) [i58 (con integer 0)]] i62]]]]))]) - (lam i61 i61)]) - [(lam i60 [i60 i60]) - (lam i60 - (lam i61 - (case - [[(builtin lessThanInteger) i61] [i58 (con integer 10)]] - [[(builtin appendByteString) - [[i60 i60] [[(builtin quotientInteger) i61] [i58 (con integer 10)]]]] - [[(builtin consByteString) - [[(builtin addInteger) [i58 (con integer 48)]] - [[(builtin remainderInteger) i61] [i58 (con integer 10)]]]] - [i59 (con bytestring #)]]] - [[(builtin consByteString) [[(builtin addInteger) [i58 (con integer 48)]] i61]] - [i59 (con bytestring #)]])))]] - i27]] - (lam i60 - [(lam i61 - [(lam i62 - [(lam i63 - [[[i15 i9] - [[(lam i64 - [(lam i65 - [(lam i66 i66) - (lam i66 - (case - [[(builtin lessThanInteger) i66] [i62 (con integer 0)]] - [(builtin decodeUtf8) [i64 i66]] - [[(builtin appendString) [i65 i61]] - [(builtin decodeUtf8) - [i64 [[(builtin subtractInteger) [i62 (con integer 0)]] i66]]]]))]) - (lam i65 i65)]) - [(lam i64 [i64 i64]) - (lam i64 - (lam i65 - (case - [[(builtin lessThanInteger) i65] [i62 (con integer 10)]] - [[(builtin appendByteString) - [[i64 i64] [[(builtin quotientInteger) i65] [i62 (con integer 10)]]]] - [[(builtin consByteString) - [[(builtin addInteger) [i62 (con integer 48)]] - [[(builtin remainderInteger) i65] [i62 (con integer 10)]]]] - [i63 (con bytestring #)]]] - [[(builtin consByteString) [[(builtin addInteger) [i62 (con integer 48)]] i65]] - [i63 (con bytestring #)]])))]] - i29]] - (lam i64 (error))]) - (lam i63 i63)]) - (lam i62 i62)]) - i1])]) - (lam i59 i59)]) - (lam i58 i58)]) + (lam i63 + (case + [[(builtin lessThanInteger) i63] [i60 (con integer 10)]] + [[(builtin appendByteString) + [[i62 i62] [[(builtin quotientInteger) i63] [i60 (con integer 10)]]]] + [[(builtin consByteString) + [[(builtin addInteger) [i60 (con integer 48)]] + [[(builtin remainderInteger) i63] [i60 (con integer 10)]]]] + [i61 (con bytestring #)]]] + [[(builtin consByteString) [[(builtin addInteger) [i60 (con integer 48)]] i63]] + [i61 (con bytestring #)]])))]] + i30]] + (lam i62 + [(lam i63 + [(lam i64 + [(lam i65 + [(lam i66 + [[[i15 i8] + [[(lam i67 + [(lam i68 + [(lam i69 i69) + (lam i69 + (case + [[(builtin lessThanInteger) i69] [i65 (con integer 0)]] + [[i64 [[i64 i69] i67]] (builtin decodeUtf8)] + [[i64 + [[i64 [[i64 [[i64 i69] [(builtin subtractInteger) [i65 (con integer 0)]]]] i67]] + (builtin decodeUtf8)]] + [(builtin appendString) [i68 i63]]]))]) + (lam i68 i68)]) + [(lam i67 [i67 i67]) + (lam i67 + (lam i68 + (case + [[(builtin lessThanInteger) i68] [i65 (con integer 10)]] + [[(builtin appendByteString) + [[i67 i67] [[(builtin quotientInteger) i68] [i65 (con integer 10)]]]] + [[(builtin consByteString) + [[(builtin addInteger) [i65 (con integer 48)]] + [[(builtin remainderInteger) i68] [i65 (con integer 10)]]]] + [i66 (con bytestring #)]]] + [[(builtin consByteString) [[(builtin addInteger) [i65 (con integer 48)]] i68]] + [i66 (con bytestring #)]])))]] + i27]] + (lam i67 + [(lam i68 + [(lam i69 + [(lam i70 + [(lam i71 + [[[i15 i9] + [[(lam i72 + [(lam i73 + [(lam i74 i74) + (lam i74 + (case + [[(builtin lessThanInteger) i74] [i70 (con integer 0)]] + [[i69 [[i69 i74] i72]] (builtin decodeUtf8)] + [[i69 + [[i69 [[i69 [[i69 i74] [(builtin subtractInteger) [i70 (con integer 0)]]]] i72]] + (builtin decodeUtf8)]] + [(builtin appendString) [i73 i68]]]))]) + (lam i73 i73)]) + [(lam i72 [i72 i72]) + (lam i72 + (lam i73 + (case + [[(builtin lessThanInteger) i73] [i70 (con integer 10)]] + [[(builtin appendByteString) + [[i72 i72] [[(builtin quotientInteger) i73] [i70 (con integer 10)]]]] + [[(builtin consByteString) + [[(builtin addInteger) [i70 (con integer 48)]] + [[(builtin remainderInteger) i73] [i70 (con integer 10)]]]] + [i71 (con bytestring #)]]] + [[(builtin consByteString) [[(builtin addInteger) [i70 (con integer 48)]] i73]] + [i71 (con bytestring #)]])))]] + i29]] + (lam i72 (error))]) + (lam i71 i71)]) + (lam i70 i70)]) + (lam i69 (lam i70 [i70 i69]))]) + i1])]) + (lam i66 i66)]) + (lam i65 i65)]) + (lam i64 (lam i65 [i65 i64]))]) + i1])]) + (lam i61 i61)]) + (lam i60 i60)]) + (lam i59 (lam i60 [i60 i59]))]) i1])]) - (lam i55 i55)]) - (lam i54 i54)]) - i1])]) - (lam i51 i51)]) - (lam i50 i50)]) - i1])]) - (lam i47 i47)]) - (lam i46 i46)]) - i1])]) - (lam i43 i43)]) - (lam i42 i42)]) - i1])]) - (lam i39 i39)]) - (lam i38 i38)]) - i1])]) + (lam i56 i56)]) + (lam i55 i55)]) + (lam i54 (lam i55 [i55 i54]))]) + i1])]) + (lam i51 i51)]) + (lam i50 i50)]) + (lam i49 (lam i50 [i50 i49]))]) + i1])]) + (lam i46 i46)]) + (lam i45 i45)]) + (lam i44 (lam i45 [i45 i44]))]) + i1])]) + (lam i41 i41)]) + (lam i40 i40)]) + (lam i39 (lam i40 [i40 i39]))]) + i1])]) + (lam i36 i36)]) (lam i35 i35)]) - (lam i34 i34)]) + (lam i34 (lam i35 [i35 i34]))]) i1])] (con unit ()))) [i25 i30]]) diff --git a/crates/nash-codegen/src/tests/snapshots/nash_codegen__tests__integration__captures_preserve_partial_application_order_and_lazy_branches_1_V3_Verbose.snap b/crates/nash-codegen/src/tests/snapshots/nash_codegen__tests__integration__captures_preserve_partial_application_order_and_lazy_branches_1_V3_Verbose.snap index 12611183..a0d8227a 100644 --- a/crates/nash-codegen/src/tests/snapshots/nash_codegen__tests__integration__captures_preserve_partial_application_order_and_lazy_branches_1_V3_Verbose.snap +++ b/crates/nash-codegen/src/tests/snapshots/nash_codegen__tests__integration__captures_preserve_partial_application_order_and_lazy_branches_1_V3_Verbose.snap @@ -34,35 +34,38 @@ description: "module Main exposing (..)\nimport Primitive exposing (..)\nimport [(lam i24 [(lam i25 [(lam i26 - [[[i12 i5] - [[(lam i27 - [(lam i28 - [(lam i29 i29) - (lam i29 - (case - [[(builtin lessThanInteger) i29] [i25 (con integer 0)]] - [(builtin decodeUtf8) [i27 i29]] - [[(builtin appendString) [i28 i24]] - [(builtin decodeUtf8) - [i27 [[(builtin subtractInteger) [i25 (con integer 0)]] i29]]]]))]) - (lam i28 i28)]) - [(lam i27 [i27 i27]) - (lam i27 + [(lam i27 + [[[i12 i5] + [[(lam i28 + [(lam i29 + [(lam i30 i30) + (lam i30 + (case + [[(builtin lessThanInteger) i30] [i26 (con integer 0)]] + [[i25 [[i25 i30] i28]] (builtin decodeUtf8)] + [[i25 + [[i25 [[i25 [[i25 i30] [(builtin subtractInteger) [i26 (con integer 0)]]]] i28]] + (builtin decodeUtf8)]] + [(builtin appendString) [i29 i24]]]))]) + (lam i29 i29)]) + [(lam i28 [i28 i28]) (lam i28 - (case - [[(builtin lessThanInteger) i28] [i25 (con integer 10)]] - [[(builtin appendByteString) - [[i27 i27] [[(builtin quotientInteger) i28] [i25 (con integer 10)]]]] - [[(builtin consByteString) - [[(builtin addInteger) [i25 (con integer 48)]] - [[(builtin remainderInteger) i28] [i25 (con integer 10)]]]] - [i26 (con bytestring #)]]] - [[(builtin consByteString) [[(builtin addInteger) [i25 (con integer 48)]] i28]] - [i26 (con bytestring #)]])))]] - i16]] - (lam i27 (error))]) + (lam i29 + (case + [[(builtin lessThanInteger) i29] [i26 (con integer 10)]] + [[(builtin appendByteString) + [[i28 i28] [[(builtin quotientInteger) i29] [i26 (con integer 10)]]]] + [[(builtin consByteString) + [[(builtin addInteger) [i26 (con integer 48)]] + [[(builtin remainderInteger) i29] [i26 (con integer 10)]]]] + [i27 (con bytestring #)]]] + [[(builtin consByteString) [[(builtin addInteger) [i26 (con integer 48)]] i29]] + [i27 (con bytestring #)]])))]] + i16]] + (lam i28 (error))]) + (lam i27 i27)]) (lam i26 i26)]) - (lam i25 i25)]) + (lam i25 (lam i26 [i26 i25]))]) i4])] (con unit ()))) i21]) diff --git a/crates/nash-codegen/src/tests/snapshots/nash_codegen__tests__integration__custom_show_runs_only_on_failure_and_test_local_native_layouts_specialize_1_V3_Verbose.snap b/crates/nash-codegen/src/tests/snapshots/nash_codegen__tests__integration__custom_show_runs_only_on_failure_and_test_local_native_layouts_specialize_1_V3_Verbose.snap index 217bfcc8..2bc7d927 100644 --- a/crates/nash-codegen/src/tests/snapshots/nash_codegen__tests__integration__custom_show_runs_only_on_failure_and_test_local_native_layouts_specialize_1_V3_Verbose.snap +++ b/crates/nash-codegen/src/tests/snapshots/nash_codegen__tests__integration__custom_show_runs_only_on_failure_and_test_local_native_layouts_specialize_1_V3_Verbose.snap @@ -157,34 +157,35 @@ description: "module Main exposing (..)\nimport Primitive exposing (..)\nimport [(lam i23 [(lam i24 [(lam i25 - [[[i11 i2] - [[(lam i26 - [(lam i27 - [(lam i28 i28) - (lam i28 - (case - [[(builtin lessThanInteger) i28] [i24 (con integer 0)]] - [(builtin decodeUtf8) [i26 i28]] - [[(builtin appendString) [i27 i23]] - [(builtin decodeUtf8) - [i26 [[(builtin subtractInteger) [i24 (con integer 0)]] i28]]]]))]) - (lam i27 i27)]) - [(lam i26 [i26 i26]) - (lam i26 + [(lam i26 + [[[i11 i2] + [[(lam i27 + [(lam i28 + [(lam i29 i29) + (lam i29 + (case + [[(builtin lessThanInteger) i29] [i25 (con integer 0)]] + [[i24 [[i24 i29] i27]] (builtin decodeUtf8)] + [[i24 + [[i24 [[i24 [[i24 i29] [(builtin subtractInteger) [i25 (con integer 0)]]]] i27]] + (builtin decodeUtf8)]] + [(builtin appendString) [i28 i23]]]))]) + (lam i28 i28)]) + [(lam i27 [i27 i27]) (lam i27 - (case - [[(builtin lessThanInteger) i27] [i24 (con integer 10)]] - [[(builtin appendByteString) - [[i26 i26] [[(builtin quotientInteger) i27] [i24 (con integer 10)]]]] - [[(builtin consByteString) - [[(builtin addInteger) [i24 (con integer 48)]] - [[(builtin remainderInteger) i27] [i24 (con integer 10)]]]] - [i25 (con bytestring #)]]] - [[(builtin consByteString) [[(builtin addInteger) [i24 (con integer 48)]] i27]] - [i25 (con bytestring #)]])))]] - i19]] - (lam i26 - [(lam i27 + (lam i28 + (case + [[(builtin lessThanInteger) i28] [i25 (con integer 10)]] + [[(builtin appendByteString) + [[i27 i27] [[(builtin quotientInteger) i28] [i25 (con integer 10)]]]] + [[(builtin consByteString) + [[(builtin addInteger) [i25 (con integer 48)]] + [[(builtin remainderInteger) i28] [i25 (con integer 10)]]]] + [i26 (con bytestring #)]]] + [[(builtin consByteString) [[(builtin addInteger) [i25 (con integer 48)]] i28]] + [i26 (con bytestring #)]])))]] + i19]] + (lam i27 [(lam i28 [(lam i29 [(lam i30 @@ -192,71 +193,76 @@ description: "module Main exposing (..)\nimport Primitive exposing (..)\nimport [(lam i32 [(lam i33 [(lam i34 - [[[i11 i7] - [[(lam i35 - [(lam i36 - [(lam i37 - [(lam i38 i38) + [(lam i35 + [(lam i36 + [[[i11 i7] + [[(lam i37 + [(lam i38 + [(lam i39 + [(lam i40 i40) + (lam i40 + [[(builtin appendString) [i33 i31]] + [[(builtin appendString) [i39 i40]] [i33 i32]]])]) + [(lam i39 [i39 i39]) + (lam i39 + (lam i40 + [(lam i41 + (case + i41 + (lam i42 + (lam i43 + [(lam i44 + [(lam i45 + [(lam i46 + [(lam i47 + (case + i47 + (lam i48 + (lam i49 + [[(builtin appendString) i46] + [[(builtin appendString) [i33 i30]] [[i39 i39] i45]]])) + i46)) + i45]) + [i38 i44]]) + i43]) + i42])) + [i33 i29])) + i40]))]]) (lam i38 - [[(builtin appendString) [i32 i30]] - [[(builtin appendString) [i37 i38]] [i32 i31]]])]) + (case + [[(builtin lessThanInteger) i38] [i35 (con integer 0)]] + [[i34 [[i34 i38] i37]] (builtin decodeUtf8)] + [[i34 + [[i34 [[i34 [[i34 i38] [(builtin subtractInteger) [i35 (con integer 0)]]]] i37]] + (builtin decodeUtf8)]] + [(builtin appendString) [i33 i28]]]))]) [(lam i37 [i37 i37]) (lam i37 (lam i38 - [(lam i39 - (case - i39 - (lam i40 - (lam i41 - [(lam i42 - [(lam i43 - [(lam i44 - [(lam i45 - (case - i45 - (lam i46 - (lam i47 - [[(builtin appendString) i44] - [[(builtin appendString) [i32 i29]] [[i37 i37] i43]]])) - i44)) - i43]) - [i36 i42]]) - i41]) - i40])) - [i32 i28])) - i38]))]]) - (lam i36 - (case - [[(builtin lessThanInteger) i36] [i33 (con integer 0)]] - [(builtin decodeUtf8) [i35 i36]] - [[(builtin appendString) [i32 i27]] - [(builtin decodeUtf8) - [i35 [[(builtin subtractInteger) [i33 (con integer 0)]] i36]]]]))]) - [(lam i35 [i35 i35]) - (lam i35 - (lam i36 - (case - [[(builtin lessThanInteger) i36] [i33 (con integer 10)]] - [[(builtin appendByteString) - [[i35 i35] [[(builtin quotientInteger) i36] [i33 (con integer 10)]]]] - [[(builtin consByteString) - [[(builtin addInteger) [i33 (con integer 48)]] - [[(builtin remainderInteger) i36] [i33 (con integer 10)]]]] - [i34 (con bytestring #)]]] - [[(builtin consByteString) [[(builtin addInteger) [i33 (con integer 48)]] i36]] - [i34 (con bytestring #)]])))]] - i18]] - (lam i35 (error))]) - (lam i34 i34)]) + (case + [[(builtin lessThanInteger) i38] [i35 (con integer 10)]] + [[(builtin appendByteString) + [[i37 i37] [[(builtin quotientInteger) i38] [i35 (con integer 10)]]]] + [[(builtin consByteString) + [[(builtin addInteger) [i35 (con integer 48)]] + [[(builtin remainderInteger) i38] [i35 (con integer 10)]]]] + [i36 (con bytestring #)]]] + [[(builtin consByteString) [[(builtin addInteger) [i35 (con integer 48)]] i38]] + [i36 (con bytestring #)]])))]] + i18]] + (lam i37 (error))]) + (lam i36 i36)]) + (lam i35 i35)]) + (lam i34 (lam i35 [i35 i34]))]) (lam i33 i33)]) - (lam i32 i32)]) - i6]) - i5]) - i4]) - i3]) - i1])]) + i6]) + i5]) + i4]) + i3]) + i1])]) + (lam i26 i26)]) (lam i25 i25)]) - (lam i24 i24)]) + (lam i24 (lam i25 [i25 i24]))]) i1])] (con unit ()))) [i8 (con integer 1)]]) diff --git a/crates/nash-codegen/src/tests/snapshots/nash_codegen__tests__integration__properties_thread_prng_bind_patterns_and_draw_without_running_body_1_V3_Verbose.snap b/crates/nash-codegen/src/tests/snapshots/nash_codegen__tests__integration__properties_thread_prng_bind_patterns_and_draw_without_running_body_1_V3_Verbose.snap index 8f6beefb..106dde0c 100644 --- a/crates/nash-codegen/src/tests/snapshots/nash_codegen__tests__integration__properties_thread_prng_bind_patterns_and_draw_without_running_body_1_V3_Verbose.snap +++ b/crates/nash-codegen/src/tests/snapshots/nash_codegen__tests__integration__properties_thread_prng_bind_patterns_and_draw_without_running_body_1_V3_Verbose.snap @@ -92,35 +92,38 @@ description: "module Main exposing (..)\nimport Primitive exposing (..)\nimport [(lam i31 [(lam i32 [(lam i33 - [[(force (builtin mkCons)) - [[(lam i34 - [(lam i35 - [(lam i36 i36) - (lam i36 - (case - [[(builtin lessThanInteger) i36] [i32 (con integer 0)]] - [(builtin decodeUtf8) [i34 i36]] - [[(builtin appendString) [i35 i31]] - [(builtin decodeUtf8) - [i34 [[(builtin subtractInteger) [i32 (con integer 0)]] i36]]]]))]) - (lam i35 i35)]) - [(lam i34 [i34 i34]) - (lam i34 + [(lam i34 + [[(force (builtin mkCons)) + [[(lam i35 + [(lam i36 + [(lam i37 i37) + (lam i37 + (case + [[(builtin lessThanInteger) i37] [i33 (con integer 0)]] + [[i32 [[i32 i37] i35]] (builtin decodeUtf8)] + [[i32 + [[i32 [[i32 [[i32 i37] [(builtin subtractInteger) [i33 (con integer 0)]]]] i35]] + (builtin decodeUtf8)]] + [(builtin appendString) [i36 i31]]]))]) + (lam i36 i36)]) + [(lam i35 [i35 i35]) (lam i35 - (case - [[(builtin lessThanInteger) i35] [i32 (con integer 10)]] - [[(builtin appendByteString) - [[i34 i34] [[(builtin quotientInteger) i35] [i32 (con integer 10)]]]] - [[(builtin consByteString) - [[(builtin addInteger) [i32 (con integer 48)]] - [[(builtin remainderInteger) i35] [i32 (con integer 10)]]]] - [i33 (con bytestring #)]]] - [[(builtin consByteString) [[(builtin addInteger) [i32 (con integer 48)]] i35]] - [i33 (con bytestring #)]])))]] - i21]] - [[(force (builtin mkCons)) i8] (con (list string) [])]]) + (lam i36 + (case + [[(builtin lessThanInteger) i36] [i33 (con integer 10)]] + [[(builtin appendByteString) + [[i35 i35] [[(builtin quotientInteger) i36] [i33 (con integer 10)]]]] + [[(builtin consByteString) + [[(builtin addInteger) [i33 (con integer 48)]] + [[(builtin remainderInteger) i36] [i33 (con integer 10)]]]] + [i34 (con bytestring #)]]] + [[(builtin consByteString) [[(builtin addInteger) [i33 (con integer 48)]] i36]] + [i34 (con bytestring #)]])))]] + i21]] + [[(force (builtin mkCons)) i8] (con (list string) [])]]) + (lam i34 i34)]) (lam i33 i33)]) - (lam i32 i32)]) + (lam i32 (lam i33 [i33 i32]))]) i7]) i28]) i27])))) diff --git a/crates/nash-codegen/src/tests/snapshots/nash_codegen__tests__integration__rejected_generator_does_not_initialize_later_generators_V3_Verbose.snap b/crates/nash-codegen/src/tests/snapshots/nash_codegen__tests__integration__rejected_generator_does_not_initialize_later_generators_V3_Verbose.snap index 247308ce..005146ab 100644 --- a/crates/nash-codegen/src/tests/snapshots/nash_codegen__tests__integration__rejected_generator_does_not_initialize_later_generators_V3_Verbose.snap +++ b/crates/nash-codegen/src/tests/snapshots/nash_codegen__tests__integration__rejected_generator_does_not_initialize_later_generators_V3_Verbose.snap @@ -40,68 +40,74 @@ description: "module Main exposing (..)\nimport Builtin\nimport Prop exposing (r [(lam i27 [(lam i28 [(lam i29 - [[[i11 i3] - [[(lam i30 - [(lam i31 - [(lam i32 i32) - (lam i32 - (case - [[(builtin lessThanInteger) i32] [i28 (con integer 0)]] - [(builtin decodeUtf8) [i30 i32]] - [[(builtin appendString) [i31 i27]] - [(builtin decodeUtf8) - [i30 [[(builtin subtractInteger) [i28 (con integer 0)]] i32]]]]))]) - (lam i31 i31)]) - [(lam i30 [i30 i30]) - (lam i30 + [(lam i30 + [[[i11 i3] + [[(lam i31 + [(lam i32 + [(lam i33 i33) + (lam i33 + (case + [[(builtin lessThanInteger) i33] [i29 (con integer 0)]] + [[i28 [[i28 i33] i31]] (builtin decodeUtf8)] + [[i28 + [[i28 [[i28 [[i28 i33] [(builtin subtractInteger) [i29 (con integer 0)]]]] i31]] + (builtin decodeUtf8)]] + [(builtin appendString) [i32 i27]]]))]) + (lam i32 i32)]) + [(lam i31 [i31 i31]) (lam i31 - (case - [[(builtin lessThanInteger) i31] [i28 (con integer 10)]] - [[(builtin appendByteString) - [[i30 i30] [[(builtin quotientInteger) i31] [i28 (con integer 10)]]]] - [[(builtin consByteString) - [[(builtin addInteger) [i28 (con integer 48)]] - [[(builtin remainderInteger) i31] [i28 (con integer 10)]]]] - [i29 (con bytestring #)]]] - [[(builtin consByteString) [[(builtin addInteger) [i28 (con integer 48)]] i31]] - [i29 (con bytestring #)]])))]] - i23]] - (lam i30 - [(lam i31 + (lam i32 + (case + [[(builtin lessThanInteger) i32] [i29 (con integer 10)]] + [[(builtin appendByteString) + [[i31 i31] [[(builtin quotientInteger) i32] [i29 (con integer 10)]]]] + [[(builtin consByteString) + [[(builtin addInteger) [i29 (con integer 48)]] + [[(builtin remainderInteger) i32] [i29 (con integer 10)]]]] + [i30 (con bytestring #)]]] + [[(builtin consByteString) [[(builtin addInteger) [i29 (con integer 48)]] i32]] + [i30 (con bytestring #)]])))]] + i23]] + (lam i31 [(lam i32 [(lam i33 - [[[i11 i4] - [[(lam i34 - [(lam i35 - [(lam i36 i36) - (lam i36 - (case - [[(builtin lessThanInteger) i36] [i32 (con integer 0)]] - [(builtin decodeUtf8) [i34 i36]] - [[(builtin appendString) [i35 i31]] - [(builtin decodeUtf8) - [i34 [[(builtin subtractInteger) [i32 (con integer 0)]] i36]]]]))]) - (lam i35 i35)]) - [(lam i34 [i34 i34]) - (lam i34 - (lam i35 - (case - [[(builtin lessThanInteger) i35] [i32 (con integer 10)]] - [[(builtin appendByteString) - [[i34 i34] [[(builtin quotientInteger) i35] [i32 (con integer 10)]]]] - [[(builtin consByteString) - [[(builtin addInteger) [i32 (con integer 48)]] - [[(builtin remainderInteger) i35] [i32 (con integer 10)]]]] - [i33 (con bytestring #)]]] - [[(builtin consByteString) [[(builtin addInteger) [i32 (con integer 48)]] i35]] - [i33 (con bytestring #)]])))]] - i25]] - (lam i34 (error))]) - (lam i33 i33)]) - (lam i32 i32)]) - i2])]) + [(lam i34 + [(lam i35 + [[[i11 i4] + [[(lam i36 + [(lam i37 + [(lam i38 i38) + (lam i38 + (case + [[(builtin lessThanInteger) i38] [i34 (con integer 0)]] + [[i33 [[i33 i38] i36]] (builtin decodeUtf8)] + [[i33 + [[i33 [[i33 [[i33 i38] [(builtin subtractInteger) [i34 (con integer 0)]]]] i36]] + (builtin decodeUtf8)]] + [(builtin appendString) [i37 i32]]]))]) + (lam i37 i37)]) + [(lam i36 [i36 i36]) + (lam i36 + (lam i37 + (case + [[(builtin lessThanInteger) i37] [i34 (con integer 10)]] + [[(builtin appendByteString) + [[i36 i36] [[(builtin quotientInteger) i37] [i34 (con integer 10)]]]] + [[(builtin consByteString) + [[(builtin addInteger) [i34 (con integer 48)]] + [[(builtin remainderInteger) i37] [i34 (con integer 10)]]]] + [i35 (con bytestring #)]]] + [[(builtin consByteString) [[(builtin addInteger) [i34 (con integer 48)]] i37]] + [i35 (con bytestring #)]])))]] + i25]] + (lam i36 (error))]) + (lam i35 i35)]) + (lam i34 i34)]) + (lam i33 (lam i34 [i34 i33]))]) + i2])]) + (lam i30 i30)]) (lam i29 i29)]) - (lam i28 i28)]) + (lam i28 (lam i29 [i29 i28]))]) i2])] (con unit ()))) i21]) @@ -132,64 +138,70 @@ description: "module Main exposing (..)\nimport Builtin\nimport Prop exposing (r [(lam i26 [(lam i27 [(lam i28 - [[(force (builtin mkCons)) i25] - [[(force (builtin mkCons)) - [[(lam i29 - [(lam i30 - [(lam i31 i31) - (lam i31 - (case - [[(builtin lessThanInteger) i31] [i27 (con integer 0)]] - [(builtin decodeUtf8) [i29 i31]] - [[(builtin appendString) [i30 i26]] - [(builtin decodeUtf8) - [i29 [[(builtin subtractInteger) [i27 (con integer 0)]] i31]]]]))]) - (lam i30 i30)]) - [(lam i29 [i29 i29]) - (lam i29 - (lam i30 - (case - [[(builtin lessThanInteger) i30] [i27 (con integer 10)]] - [[(builtin appendByteString) - [[i29 i29] [[(builtin quotientInteger) i30] [i27 (con integer 10)]]]] - [[(builtin consByteString) - [[(builtin addInteger) [i27 (con integer 48)]] - [[(builtin remainderInteger) i30] [i27 (con integer 10)]]]] - [i28 (con bytestring #)]]] - [[(builtin consByteString) [[(builtin addInteger) [i27 (con integer 48)]] i30]] - [i28 (con bytestring #)]])))]] - i19]] - (con (list string) [])]]) - (lam i28 i28)]) - (lam i27 i27)]) - i2]) - [[(lam i25 - [(lam i26 - [(lam i27 i27) - (lam i27 - (case - [[(builtin lessThanInteger) i27] [i23 (con integer 0)]] - [(builtin decodeUtf8) [i25 i27]] - [[(builtin appendString) [i26 i22]] - [(builtin decodeUtf8) - [i25 [[(builtin subtractInteger) [i23 (con integer 0)]] i27]]]]))]) - (lam i26 i26)]) - [(lam i25 [i25 i25]) - (lam i25 + [(lam i29 + [(lam i30 + [[(force (builtin mkCons)) i26] + [[(force (builtin mkCons)) + [[(lam i31 + [(lam i32 + [(lam i33 i33) + (lam i33 + (case + [[(builtin lessThanInteger) i33] [i29 (con integer 0)]] + [[i28 [[i28 i33] i31]] (builtin decodeUtf8)] + [[i28 + [[i28 [[i28 [[i28 i33] [(builtin subtractInteger) [i29 (con integer 0)]]]] i31]] + (builtin decodeUtf8)]] + [(builtin appendString) [i32 i27]]]))]) + (lam i32 i32)]) + [(lam i31 [i31 i31]) + (lam i31 + (lam i32 + (case + [[(builtin lessThanInteger) i32] [i29 (con integer 10)]] + [[(builtin appendByteString) + [[i31 i31] [[(builtin quotientInteger) i32] [i29 (con integer 10)]]]] + [[(builtin consByteString) + [[(builtin addInteger) [i29 (con integer 48)]] + [[(builtin remainderInteger) i32] [i29 (con integer 10)]]]] + [i30 (con bytestring #)]]] + [[(builtin consByteString) [[(builtin addInteger) [i29 (con integer 48)]] i32]] + [i30 (con bytestring #)]])))]] + i19]] + (con (list string) [])]]) + (lam i30 i30)]) + (lam i29 i29)]) + (lam i28 (lam i29 [i29 i28]))]) + i2]) + [[(lam i26 + [(lam i27 + [(lam i28 i28) + (lam i28 + (case + [[(builtin lessThanInteger) i28] [i24 (con integer 0)]] + [[i23 [[i23 i28] i26]] (builtin decodeUtf8)] + [[i23 + [[i23 [[i23 [[i23 i28] [(builtin subtractInteger) [i24 (con integer 0)]]]] i26]] + (builtin decodeUtf8)]] + [(builtin appendString) [i27 i22]]]))]) + (lam i27 i27)]) + [(lam i26 [i26 i26]) (lam i26 - (case - [[(builtin lessThanInteger) i26] [i23 (con integer 10)]] - [[(builtin appendByteString) - [[i25 i25] [[(builtin quotientInteger) i26] [i23 (con integer 10)]]]] - [[(builtin consByteString) - [[(builtin addInteger) [i23 (con integer 48)]] - [[(builtin remainderInteger) i26] [i23 (con integer 10)]]]] - [i24 (con bytestring #)]]] - [[(builtin consByteString) [[(builtin addInteger) [i23 (con integer 48)]] i26]] - [i24 (con bytestring #)]])))]] - i15]]) + (lam i27 + (case + [[(builtin lessThanInteger) i27] [i24 (con integer 10)]] + [[(builtin appendByteString) + [[i26 i26] [[(builtin quotientInteger) i27] [i24 (con integer 10)]]]] + [[(builtin consByteString) + [[(builtin addInteger) [i24 (con integer 48)]] + [[(builtin remainderInteger) i27] [i24 (con integer 10)]]]] + [i25 (con bytestring #)]]] + [[(builtin consByteString) [[(builtin addInteger) [i24 (con integer 48)]] i27]] + [i25 (con bytestring #)]])))]] + i15]]) + (lam i25 i25)]) (lam i24 i24)]) - (lam i23 i23)]) + (lam i23 (lam i24 [i24 i23]))]) i2]) i20]) i19]) diff --git a/crates/nash-codegen/src/tests/snapshots/nash_codegen__tests__integration__rejected_generator_skips_body_and_selected_target_is_enforced_2_V1_Silent.snap b/crates/nash-codegen/src/tests/snapshots/nash_codegen__tests__integration__rejected_generator_skips_body_and_selected_target_is_enforced_2_V1_Silent.snap index 6c6a8f71..6fb73fde 100644 --- a/crates/nash-codegen/src/tests/snapshots/nash_codegen__tests__integration__rejected_generator_skips_body_and_selected_target_is_enforced_2_V1_Silent.snap +++ b/crates/nash-codegen/src/tests/snapshots/nash_codegen__tests__integration__rejected_generator_skips_body_and_selected_target_is_enforced_2_V1_Silent.snap @@ -40,68 +40,74 @@ description: "module Main exposing (..)\nimport Primitive exposing (..)\nimport [(lam i18 [(lam i19 [(lam i20 - [[[i7 i2] - [[(lam i21 - [(lam i22 - [(lam i23 i23) - (lam i23 - (case - [[(builtin lessThanInteger) i23] [i19 (con integer 0)]] - [(builtin decodeUtf8) [i21 i23]] - [[(builtin appendString) [i22 i18]] - [(builtin decodeUtf8) - [i21 [[(builtin subtractInteger) [i19 (con integer 0)]] i23]]]]))]) - (lam i22 i22)]) - [(lam i21 [i21 i21]) - (lam i21 + [(lam i21 + [[[i7 i2] + [[(lam i22 + [(lam i23 + [(lam i24 i24) + (lam i24 + (case + [[(builtin lessThanInteger) i24] [i20 (con integer 0)]] + [[i19 [[i19 i24] i22]] (builtin decodeUtf8)] + [[i19 + [[i19 [[i19 [[i19 i24] [(builtin subtractInteger) [i20 (con integer 0)]]]] i22]] + (builtin decodeUtf8)]] + [(builtin appendString) [i23 i18]]]))]) + (lam i23 i23)]) + [(lam i22 [i22 i22]) (lam i22 - (case - [[(builtin lessThanInteger) i22] [i19 (con integer 10)]] - [[(builtin appendByteString) - [[i21 i21] [[(builtin quotientInteger) i22] [i19 (con integer 10)]]]] - [[(builtin consByteString) - [[(builtin addInteger) [i19 (con integer 48)]] - [[(builtin remainderInteger) i22] [i19 (con integer 10)]]]] - [i20 (con bytestring #)]]] - [[(builtin consByteString) [[(builtin addInteger) [i19 (con integer 48)]] i22]] - [i20 (con bytestring #)]])))]] - i14]] - (lam i21 - [(lam i22 + (lam i23 + (case + [[(builtin lessThanInteger) i23] [i20 (con integer 10)]] + [[(builtin appendByteString) + [[i22 i22] [[(builtin quotientInteger) i23] [i20 (con integer 10)]]]] + [[(builtin consByteString) + [[(builtin addInteger) [i20 (con integer 48)]] + [[(builtin remainderInteger) i23] [i20 (con integer 10)]]]] + [i21 (con bytestring #)]]] + [[(builtin consByteString) [[(builtin addInteger) [i20 (con integer 48)]] i23]] + [i21 (con bytestring #)]])))]] + i14]] + (lam i22 [(lam i23 [(lam i24 - [[[i7 i3] - [[(lam i25 - [(lam i26 - [(lam i27 i27) - (lam i27 - (case - [[(builtin lessThanInteger) i27] [i23 (con integer 0)]] - [(builtin decodeUtf8) [i25 i27]] - [[(builtin appendString) [i26 i22]] - [(builtin decodeUtf8) - [i25 [[(builtin subtractInteger) [i23 (con integer 0)]] i27]]]]))]) - (lam i26 i26)]) - [(lam i25 [i25 i25]) - (lam i25 - (lam i26 - (case - [[(builtin lessThanInteger) i26] [i23 (con integer 10)]] - [[(builtin appendByteString) - [[i25 i25] [[(builtin quotientInteger) i26] [i23 (con integer 10)]]]] - [[(builtin consByteString) - [[(builtin addInteger) [i23 (con integer 48)]] - [[(builtin remainderInteger) i26] [i23 (con integer 10)]]]] - [i24 (con bytestring #)]]] - [[(builtin consByteString) [[(builtin addInteger) [i23 (con integer 48)]] i26]] - [i24 (con bytestring #)]])))]] - i16]] - (lam i25 (error))]) - (lam i24 i24)]) - (lam i23 i23)]) - i1])]) + [(lam i25 + [(lam i26 + [[[i7 i3] + [[(lam i27 + [(lam i28 + [(lam i29 i29) + (lam i29 + (case + [[(builtin lessThanInteger) i29] [i25 (con integer 0)]] + [[i24 [[i24 i29] i27]] (builtin decodeUtf8)] + [[i24 + [[i24 [[i24 [[i24 i29] [(builtin subtractInteger) [i25 (con integer 0)]]]] i27]] + (builtin decodeUtf8)]] + [(builtin appendString) [i28 i23]]]))]) + (lam i28 i28)]) + [(lam i27 [i27 i27]) + (lam i27 + (lam i28 + (case + [[(builtin lessThanInteger) i28] [i25 (con integer 10)]] + [[(builtin appendByteString) + [[i27 i27] [[(builtin quotientInteger) i28] [i25 (con integer 10)]]]] + [[(builtin consByteString) + [[(builtin addInteger) [i25 (con integer 48)]] + [[(builtin remainderInteger) i28] [i25 (con integer 10)]]]] + [i26 (con bytestring #)]]] + [[(builtin consByteString) [[(builtin addInteger) [i25 (con integer 48)]] i28]] + [i26 (con bytestring #)]])))]] + i16]] + (lam i27 (error))]) + (lam i26 i26)]) + (lam i25 i25)]) + (lam i24 (lam i25 [i25 i24]))]) + i1])]) + (lam i21 i21)]) (lam i20 i20)]) - (lam i19 i19)]) + (lam i19 (lam i20 [i20 i19]))]) i1])] (con unit ()))) i12]) @@ -118,35 +124,38 @@ description: "module Main exposing (..)\nimport Primitive exposing (..)\nimport [(lam i13 [(lam i14 [(lam i15 - [[(force (builtin mkCons)) - [[(lam i16 - [(lam i17 - [(lam i18 i18) - (lam i18 - (case - [[(builtin lessThanInteger) i18] [i14 (con integer 0)]] - [(builtin decodeUtf8) [i16 i18]] - [[(builtin appendString) [i17 i13]] - [(builtin decodeUtf8) - [i16 [[(builtin subtractInteger) [i14 (con integer 0)]] i18]]]]))]) - (lam i17 i17)]) - [(lam i16 [i16 i16]) - (lam i16 + [(lam i16 + [[(force (builtin mkCons)) + [[(lam i17 + [(lam i18 + [(lam i19 i19) + (lam i19 + (case + [[(builtin lessThanInteger) i19] [i15 (con integer 0)]] + [[i14 [[i14 i19] i17]] (builtin decodeUtf8)] + [[i14 + [[i14 [[i14 [[i14 i19] [(builtin subtractInteger) [i15 (con integer 0)]]]] i17]] + (builtin decodeUtf8)]] + [(builtin appendString) [i18 i13]]]))]) + (lam i18 i18)]) + [(lam i17 [i17 i17]) (lam i17 - (case - [[(builtin lessThanInteger) i17] [i14 (con integer 10)]] - [[(builtin appendByteString) - [[i16 i16] [[(builtin quotientInteger) i17] [i14 (con integer 10)]]]] - [[(builtin consByteString) - [[(builtin addInteger) [i14 (con integer 48)]] - [[(builtin remainderInteger) i17] [i14 (con integer 10)]]]] - [i15 (con bytestring #)]]] - [[(builtin consByteString) [[(builtin addInteger) [i14 (con integer 48)]] i17]] - [i15 (con bytestring #)]])))]] - i10]] - (con (list string) [])]) + (lam i18 + (case + [[(builtin lessThanInteger) i18] [i15 (con integer 10)]] + [[(builtin appendByteString) + [[i17 i17] [[(builtin quotientInteger) i18] [i15 (con integer 10)]]]] + [[(builtin consByteString) + [[(builtin addInteger) [i15 (con integer 48)]] + [[(builtin remainderInteger) i18] [i15 (con integer 10)]]]] + [i16 (con bytestring #)]]] + [[(builtin consByteString) [[(builtin addInteger) [i15 (con integer 48)]] i18]] + [i16 (con bytestring #)]])))]] + i10]] + (con (list string) [])]) + (lam i16 i16)]) (lam i15 i15)]) - (lam i14 i14)]) + (lam i14 (lam i15 [i15 i14]))]) i1]) i11]) i10]) diff --git a/crates/nash-codegen/src/tests/snapshots/nash_codegen__tests__integration__rejected_generator_skips_body_and_selected_target_is_enforced_2_V2_Silent.snap b/crates/nash-codegen/src/tests/snapshots/nash_codegen__tests__integration__rejected_generator_skips_body_and_selected_target_is_enforced_2_V2_Silent.snap index 6c6a8f71..6fb73fde 100644 --- a/crates/nash-codegen/src/tests/snapshots/nash_codegen__tests__integration__rejected_generator_skips_body_and_selected_target_is_enforced_2_V2_Silent.snap +++ b/crates/nash-codegen/src/tests/snapshots/nash_codegen__tests__integration__rejected_generator_skips_body_and_selected_target_is_enforced_2_V2_Silent.snap @@ -40,68 +40,74 @@ description: "module Main exposing (..)\nimport Primitive exposing (..)\nimport [(lam i18 [(lam i19 [(lam i20 - [[[i7 i2] - [[(lam i21 - [(lam i22 - [(lam i23 i23) - (lam i23 - (case - [[(builtin lessThanInteger) i23] [i19 (con integer 0)]] - [(builtin decodeUtf8) [i21 i23]] - [[(builtin appendString) [i22 i18]] - [(builtin decodeUtf8) - [i21 [[(builtin subtractInteger) [i19 (con integer 0)]] i23]]]]))]) - (lam i22 i22)]) - [(lam i21 [i21 i21]) - (lam i21 + [(lam i21 + [[[i7 i2] + [[(lam i22 + [(lam i23 + [(lam i24 i24) + (lam i24 + (case + [[(builtin lessThanInteger) i24] [i20 (con integer 0)]] + [[i19 [[i19 i24] i22]] (builtin decodeUtf8)] + [[i19 + [[i19 [[i19 [[i19 i24] [(builtin subtractInteger) [i20 (con integer 0)]]]] i22]] + (builtin decodeUtf8)]] + [(builtin appendString) [i23 i18]]]))]) + (lam i23 i23)]) + [(lam i22 [i22 i22]) (lam i22 - (case - [[(builtin lessThanInteger) i22] [i19 (con integer 10)]] - [[(builtin appendByteString) - [[i21 i21] [[(builtin quotientInteger) i22] [i19 (con integer 10)]]]] - [[(builtin consByteString) - [[(builtin addInteger) [i19 (con integer 48)]] - [[(builtin remainderInteger) i22] [i19 (con integer 10)]]]] - [i20 (con bytestring #)]]] - [[(builtin consByteString) [[(builtin addInteger) [i19 (con integer 48)]] i22]] - [i20 (con bytestring #)]])))]] - i14]] - (lam i21 - [(lam i22 + (lam i23 + (case + [[(builtin lessThanInteger) i23] [i20 (con integer 10)]] + [[(builtin appendByteString) + [[i22 i22] [[(builtin quotientInteger) i23] [i20 (con integer 10)]]]] + [[(builtin consByteString) + [[(builtin addInteger) [i20 (con integer 48)]] + [[(builtin remainderInteger) i23] [i20 (con integer 10)]]]] + [i21 (con bytestring #)]]] + [[(builtin consByteString) [[(builtin addInteger) [i20 (con integer 48)]] i23]] + [i21 (con bytestring #)]])))]] + i14]] + (lam i22 [(lam i23 [(lam i24 - [[[i7 i3] - [[(lam i25 - [(lam i26 - [(lam i27 i27) - (lam i27 - (case - [[(builtin lessThanInteger) i27] [i23 (con integer 0)]] - [(builtin decodeUtf8) [i25 i27]] - [[(builtin appendString) [i26 i22]] - [(builtin decodeUtf8) - [i25 [[(builtin subtractInteger) [i23 (con integer 0)]] i27]]]]))]) - (lam i26 i26)]) - [(lam i25 [i25 i25]) - (lam i25 - (lam i26 - (case - [[(builtin lessThanInteger) i26] [i23 (con integer 10)]] - [[(builtin appendByteString) - [[i25 i25] [[(builtin quotientInteger) i26] [i23 (con integer 10)]]]] - [[(builtin consByteString) - [[(builtin addInteger) [i23 (con integer 48)]] - [[(builtin remainderInteger) i26] [i23 (con integer 10)]]]] - [i24 (con bytestring #)]]] - [[(builtin consByteString) [[(builtin addInteger) [i23 (con integer 48)]] i26]] - [i24 (con bytestring #)]])))]] - i16]] - (lam i25 (error))]) - (lam i24 i24)]) - (lam i23 i23)]) - i1])]) + [(lam i25 + [(lam i26 + [[[i7 i3] + [[(lam i27 + [(lam i28 + [(lam i29 i29) + (lam i29 + (case + [[(builtin lessThanInteger) i29] [i25 (con integer 0)]] + [[i24 [[i24 i29] i27]] (builtin decodeUtf8)] + [[i24 + [[i24 [[i24 [[i24 i29] [(builtin subtractInteger) [i25 (con integer 0)]]]] i27]] + (builtin decodeUtf8)]] + [(builtin appendString) [i28 i23]]]))]) + (lam i28 i28)]) + [(lam i27 [i27 i27]) + (lam i27 + (lam i28 + (case + [[(builtin lessThanInteger) i28] [i25 (con integer 10)]] + [[(builtin appendByteString) + [[i27 i27] [[(builtin quotientInteger) i28] [i25 (con integer 10)]]]] + [[(builtin consByteString) + [[(builtin addInteger) [i25 (con integer 48)]] + [[(builtin remainderInteger) i28] [i25 (con integer 10)]]]] + [i26 (con bytestring #)]]] + [[(builtin consByteString) [[(builtin addInteger) [i25 (con integer 48)]] i28]] + [i26 (con bytestring #)]])))]] + i16]] + (lam i27 (error))]) + (lam i26 i26)]) + (lam i25 i25)]) + (lam i24 (lam i25 [i25 i24]))]) + i1])]) + (lam i21 i21)]) (lam i20 i20)]) - (lam i19 i19)]) + (lam i19 (lam i20 [i20 i19]))]) i1])] (con unit ()))) i12]) @@ -118,35 +124,38 @@ description: "module Main exposing (..)\nimport Primitive exposing (..)\nimport [(lam i13 [(lam i14 [(lam i15 - [[(force (builtin mkCons)) - [[(lam i16 - [(lam i17 - [(lam i18 i18) - (lam i18 - (case - [[(builtin lessThanInteger) i18] [i14 (con integer 0)]] - [(builtin decodeUtf8) [i16 i18]] - [[(builtin appendString) [i17 i13]] - [(builtin decodeUtf8) - [i16 [[(builtin subtractInteger) [i14 (con integer 0)]] i18]]]]))]) - (lam i17 i17)]) - [(lam i16 [i16 i16]) - (lam i16 + [(lam i16 + [[(force (builtin mkCons)) + [[(lam i17 + [(lam i18 + [(lam i19 i19) + (lam i19 + (case + [[(builtin lessThanInteger) i19] [i15 (con integer 0)]] + [[i14 [[i14 i19] i17]] (builtin decodeUtf8)] + [[i14 + [[i14 [[i14 [[i14 i19] [(builtin subtractInteger) [i15 (con integer 0)]]]] i17]] + (builtin decodeUtf8)]] + [(builtin appendString) [i18 i13]]]))]) + (lam i18 i18)]) + [(lam i17 [i17 i17]) (lam i17 - (case - [[(builtin lessThanInteger) i17] [i14 (con integer 10)]] - [[(builtin appendByteString) - [[i16 i16] [[(builtin quotientInteger) i17] [i14 (con integer 10)]]]] - [[(builtin consByteString) - [[(builtin addInteger) [i14 (con integer 48)]] - [[(builtin remainderInteger) i17] [i14 (con integer 10)]]]] - [i15 (con bytestring #)]]] - [[(builtin consByteString) [[(builtin addInteger) [i14 (con integer 48)]] i17]] - [i15 (con bytestring #)]])))]] - i10]] - (con (list string) [])]) + (lam i18 + (case + [[(builtin lessThanInteger) i18] [i15 (con integer 10)]] + [[(builtin appendByteString) + [[i17 i17] [[(builtin quotientInteger) i18] [i15 (con integer 10)]]]] + [[(builtin consByteString) + [[(builtin addInteger) [i15 (con integer 48)]] + [[(builtin remainderInteger) i18] [i15 (con integer 10)]]]] + [i16 (con bytestring #)]]] + [[(builtin consByteString) [[(builtin addInteger) [i15 (con integer 48)]] i18]] + [i16 (con bytestring #)]])))]] + i10]] + (con (list string) [])]) + (lam i16 i16)]) (lam i15 i15)]) - (lam i14 i14)]) + (lam i14 (lam i15 [i15 i14]))]) i1]) i11]) i10]) diff --git a/crates/nash-codegen/src/tests/snapshots/nash_codegen__tests__integration__rejected_generator_skips_body_and_selected_target_is_enforced_3_V3_Silent.snap b/crates/nash-codegen/src/tests/snapshots/nash_codegen__tests__integration__rejected_generator_skips_body_and_selected_target_is_enforced_3_V3_Silent.snap index 6c6a8f71..6fb73fde 100644 --- a/crates/nash-codegen/src/tests/snapshots/nash_codegen__tests__integration__rejected_generator_skips_body_and_selected_target_is_enforced_3_V3_Silent.snap +++ b/crates/nash-codegen/src/tests/snapshots/nash_codegen__tests__integration__rejected_generator_skips_body_and_selected_target_is_enforced_3_V3_Silent.snap @@ -40,68 +40,74 @@ description: "module Main exposing (..)\nimport Primitive exposing (..)\nimport [(lam i18 [(lam i19 [(lam i20 - [[[i7 i2] - [[(lam i21 - [(lam i22 - [(lam i23 i23) - (lam i23 - (case - [[(builtin lessThanInteger) i23] [i19 (con integer 0)]] - [(builtin decodeUtf8) [i21 i23]] - [[(builtin appendString) [i22 i18]] - [(builtin decodeUtf8) - [i21 [[(builtin subtractInteger) [i19 (con integer 0)]] i23]]]]))]) - (lam i22 i22)]) - [(lam i21 [i21 i21]) - (lam i21 + [(lam i21 + [[[i7 i2] + [[(lam i22 + [(lam i23 + [(lam i24 i24) + (lam i24 + (case + [[(builtin lessThanInteger) i24] [i20 (con integer 0)]] + [[i19 [[i19 i24] i22]] (builtin decodeUtf8)] + [[i19 + [[i19 [[i19 [[i19 i24] [(builtin subtractInteger) [i20 (con integer 0)]]]] i22]] + (builtin decodeUtf8)]] + [(builtin appendString) [i23 i18]]]))]) + (lam i23 i23)]) + [(lam i22 [i22 i22]) (lam i22 - (case - [[(builtin lessThanInteger) i22] [i19 (con integer 10)]] - [[(builtin appendByteString) - [[i21 i21] [[(builtin quotientInteger) i22] [i19 (con integer 10)]]]] - [[(builtin consByteString) - [[(builtin addInteger) [i19 (con integer 48)]] - [[(builtin remainderInteger) i22] [i19 (con integer 10)]]]] - [i20 (con bytestring #)]]] - [[(builtin consByteString) [[(builtin addInteger) [i19 (con integer 48)]] i22]] - [i20 (con bytestring #)]])))]] - i14]] - (lam i21 - [(lam i22 + (lam i23 + (case + [[(builtin lessThanInteger) i23] [i20 (con integer 10)]] + [[(builtin appendByteString) + [[i22 i22] [[(builtin quotientInteger) i23] [i20 (con integer 10)]]]] + [[(builtin consByteString) + [[(builtin addInteger) [i20 (con integer 48)]] + [[(builtin remainderInteger) i23] [i20 (con integer 10)]]]] + [i21 (con bytestring #)]]] + [[(builtin consByteString) [[(builtin addInteger) [i20 (con integer 48)]] i23]] + [i21 (con bytestring #)]])))]] + i14]] + (lam i22 [(lam i23 [(lam i24 - [[[i7 i3] - [[(lam i25 - [(lam i26 - [(lam i27 i27) - (lam i27 - (case - [[(builtin lessThanInteger) i27] [i23 (con integer 0)]] - [(builtin decodeUtf8) [i25 i27]] - [[(builtin appendString) [i26 i22]] - [(builtin decodeUtf8) - [i25 [[(builtin subtractInteger) [i23 (con integer 0)]] i27]]]]))]) - (lam i26 i26)]) - [(lam i25 [i25 i25]) - (lam i25 - (lam i26 - (case - [[(builtin lessThanInteger) i26] [i23 (con integer 10)]] - [[(builtin appendByteString) - [[i25 i25] [[(builtin quotientInteger) i26] [i23 (con integer 10)]]]] - [[(builtin consByteString) - [[(builtin addInteger) [i23 (con integer 48)]] - [[(builtin remainderInteger) i26] [i23 (con integer 10)]]]] - [i24 (con bytestring #)]]] - [[(builtin consByteString) [[(builtin addInteger) [i23 (con integer 48)]] i26]] - [i24 (con bytestring #)]])))]] - i16]] - (lam i25 (error))]) - (lam i24 i24)]) - (lam i23 i23)]) - i1])]) + [(lam i25 + [(lam i26 + [[[i7 i3] + [[(lam i27 + [(lam i28 + [(lam i29 i29) + (lam i29 + (case + [[(builtin lessThanInteger) i29] [i25 (con integer 0)]] + [[i24 [[i24 i29] i27]] (builtin decodeUtf8)] + [[i24 + [[i24 [[i24 [[i24 i29] [(builtin subtractInteger) [i25 (con integer 0)]]]] i27]] + (builtin decodeUtf8)]] + [(builtin appendString) [i28 i23]]]))]) + (lam i28 i28)]) + [(lam i27 [i27 i27]) + (lam i27 + (lam i28 + (case + [[(builtin lessThanInteger) i28] [i25 (con integer 10)]] + [[(builtin appendByteString) + [[i27 i27] [[(builtin quotientInteger) i28] [i25 (con integer 10)]]]] + [[(builtin consByteString) + [[(builtin addInteger) [i25 (con integer 48)]] + [[(builtin remainderInteger) i28] [i25 (con integer 10)]]]] + [i26 (con bytestring #)]]] + [[(builtin consByteString) [[(builtin addInteger) [i25 (con integer 48)]] i28]] + [i26 (con bytestring #)]])))]] + i16]] + (lam i27 (error))]) + (lam i26 i26)]) + (lam i25 i25)]) + (lam i24 (lam i25 [i25 i24]))]) + i1])]) + (lam i21 i21)]) (lam i20 i20)]) - (lam i19 i19)]) + (lam i19 (lam i20 [i20 i19]))]) i1])] (con unit ()))) i12]) @@ -118,35 +124,38 @@ description: "module Main exposing (..)\nimport Primitive exposing (..)\nimport [(lam i13 [(lam i14 [(lam i15 - [[(force (builtin mkCons)) - [[(lam i16 - [(lam i17 - [(lam i18 i18) - (lam i18 - (case - [[(builtin lessThanInteger) i18] [i14 (con integer 0)]] - [(builtin decodeUtf8) [i16 i18]] - [[(builtin appendString) [i17 i13]] - [(builtin decodeUtf8) - [i16 [[(builtin subtractInteger) [i14 (con integer 0)]] i18]]]]))]) - (lam i17 i17)]) - [(lam i16 [i16 i16]) - (lam i16 + [(lam i16 + [[(force (builtin mkCons)) + [[(lam i17 + [(lam i18 + [(lam i19 i19) + (lam i19 + (case + [[(builtin lessThanInteger) i19] [i15 (con integer 0)]] + [[i14 [[i14 i19] i17]] (builtin decodeUtf8)] + [[i14 + [[i14 [[i14 [[i14 i19] [(builtin subtractInteger) [i15 (con integer 0)]]]] i17]] + (builtin decodeUtf8)]] + [(builtin appendString) [i18 i13]]]))]) + (lam i18 i18)]) + [(lam i17 [i17 i17]) (lam i17 - (case - [[(builtin lessThanInteger) i17] [i14 (con integer 10)]] - [[(builtin appendByteString) - [[i16 i16] [[(builtin quotientInteger) i17] [i14 (con integer 10)]]]] - [[(builtin consByteString) - [[(builtin addInteger) [i14 (con integer 48)]] - [[(builtin remainderInteger) i17] [i14 (con integer 10)]]]] - [i15 (con bytestring #)]]] - [[(builtin consByteString) [[(builtin addInteger) [i14 (con integer 48)]] i17]] - [i15 (con bytestring #)]])))]] - i10]] - (con (list string) [])]) + (lam i18 + (case + [[(builtin lessThanInteger) i18] [i15 (con integer 10)]] + [[(builtin appendByteString) + [[i17 i17] [[(builtin quotientInteger) i18] [i15 (con integer 10)]]]] + [[(builtin consByteString) + [[(builtin addInteger) [i15 (con integer 48)]] + [[(builtin remainderInteger) i18] [i15 (con integer 10)]]]] + [i16 (con bytestring #)]]] + [[(builtin consByteString) [[(builtin addInteger) [i15 (con integer 48)]] i18]] + [i16 (con bytestring #)]])))]] + i10]] + (con (list string) [])]) + (lam i16 i16)]) (lam i15 i15)]) - (lam i14 i14)]) + (lam i14 (lam i15 [i15 i14]))]) i1]) i11]) i10]) diff --git a/crates/nash-codegen/src/tests/snapshots/nash_codegen__tests__integration__unit_roots_and_power_assert_payloads_1_V3_Silent.snap b/crates/nash-codegen/src/tests/snapshots/nash_codegen__tests__integration__unit_roots_and_power_assert_payloads_1_V3_Silent.snap index c9920b9e..4f417a85 100644 --- a/crates/nash-codegen/src/tests/snapshots/nash_codegen__tests__integration__unit_roots_and_power_assert_payloads_1_V3_Silent.snap +++ b/crates/nash-codegen/src/tests/snapshots/nash_codegen__tests__integration__unit_roots_and_power_assert_payloads_1_V3_Silent.snap @@ -43,35 +43,38 @@ description: "module Main exposing (..)\nimport Primitive exposing (..)\nimport [(lam i13 [(lam i14 [(lam i15 - [[[i7 i2] - [[(lam i16 - [(lam i17 - [(lam i18 i18) - (lam i18 - (case - [[(builtin lessThanInteger) i18] [i14 (con integer 0)]] - [(builtin decodeUtf8) [i16 i18]] - [[(builtin appendString) [i17 i13]] - [(builtin decodeUtf8) - [i16 [[(builtin subtractInteger) [i14 (con integer 0)]] i18]]]]))]) - (lam i17 i17)]) - [(lam i16 [i16 i16]) - (lam i16 + [(lam i16 + [[[i7 i2] + [[(lam i17 + [(lam i18 + [(lam i19 i19) + (lam i19 + (case + [[(builtin lessThanInteger) i19] [i15 (con integer 0)]] + [[i14 [[i14 i19] i17]] (builtin decodeUtf8)] + [[i14 + [[i14 [[i14 [[i14 i19] [(builtin subtractInteger) [i15 (con integer 0)]]]] i17]] + (builtin decodeUtf8)]] + [(builtin appendString) [i18 i13]]]))]) + (lam i18 i18)]) + [(lam i17 [i17 i17]) (lam i17 - (case - [[(builtin lessThanInteger) i17] [i14 (con integer 10)]] - [[(builtin appendByteString) - [[i16 i16] [[(builtin quotientInteger) i17] [i14 (con integer 10)]]]] - [[(builtin consByteString) - [[(builtin addInteger) [i14 (con integer 48)]] - [[(builtin remainderInteger) i17] [i14 (con integer 10)]]]] - [i15 (con bytestring #)]]] - [[(builtin consByteString) [[(builtin addInteger) [i14 (con integer 48)]] i17]] - [i15 (con bytestring #)]])))]] - i9]] - (lam i16 (error))]) + (lam i18 + (case + [[(builtin lessThanInteger) i18] [i15 (con integer 10)]] + [[(builtin appendByteString) + [[i17 i17] [[(builtin quotientInteger) i18] [i15 (con integer 10)]]]] + [[(builtin consByteString) + [[(builtin addInteger) [i15 (con integer 48)]] + [[(builtin remainderInteger) i18] [i15 (con integer 10)]]]] + [i16 (con bytestring #)]]] + [[(builtin consByteString) [[(builtin addInteger) [i15 (con integer 48)]] i18]] + [i16 (con bytestring #)]])))]] + i9]] + (lam i17 (error))]) + (lam i16 i16)]) (lam i15 i15)]) - (lam i14 i14)]) + (lam i14 (lam i15 [i15 i14]))]) i1])] (con unit ()))) [i3 (con integer 2)]]) diff --git a/crates/nash-driver/base/src/Array.nash b/crates/nash-driver/base/src/Array.nash index 150f4876..540c8860 100644 --- a/crates/nash-driver/base/src/Array.nash +++ b/crates/nash-driver/base/src/Array.nash @@ -1,12 +1,16 @@ module Array exposing (fromList, length, at, get) import Builtin +import Function exposing ((|>)) import Lift exposing (Lift) import Literal import Option exposing (type option(..)) fromList : Lift (list 'a) ('f 'a) => 'f 'a -> array 'a -fromList values = Builtin.listToArray (lower values) +fromList values = + values + |> lower + |> Builtin.listToArray length : array 'a -> int length = Builtin.lengthOfArray diff --git a/crates/nash-driver/base/src/Bytes.nash b/crates/nash-driver/base/src/Bytes.nash index d41b7fea..819cf401 100644 --- a/crates/nash-driver/base/src/Bytes.nash +++ b/crates/nash-driver/base/src/Bytes.nash @@ -3,6 +3,7 @@ module Bytes exposing (append, compare, min, max, length, at, slice, take, drop, import Literal import Lift exposing (Lift) import Builtin +import Function exposing ((|>)) import List import Show import Ord @@ -30,7 +31,10 @@ maxBytes : bytes -> bytes -> bytes maxBytes = Ord.max length : Lift bytes 'b => 'b -> int -length value = Builtin.lengthOfByteString (lower value) +length value = + value + |> lower + |> Builtin.lengthOfByteString at : (Lift bytes 'b, Lift int 'n) => 'b -> 'n -> int at value index = Builtin.indexByteString (lower value) (lower index) @@ -51,13 +55,22 @@ concat values = List.foldr (\x acc -> Builtin.appendByteString (lower x) acc) #" complement : Lift bytes 'b => 'b -> bytes -complement value = Builtin.complementByteString (lower value) +complement value = + value + |> lower + |> Builtin.complementByteString countSetBits : Lift bytes 'b => 'b -> int -countSetBits value = Builtin.countSetBits (lower value) +countSetBits value = + value + |> lower + |> Builtin.countSetBits findFirstSetBit : Lift bytes 'b => 'b -> int -findFirstSetBit value = Builtin.findFirstSetBit (lower value) +findFirstSetBit value = + value + |> lower + |> Builtin.findFirstSetBit and : (Lift bool 'p, Lift bytes 'a, Lift bytes 'b) => 'p -> 'a -> 'b -> bytes and pad left right = Builtin.andByteString (lower pad) (lower left) (lower right) @@ -81,4 +94,7 @@ writeBits : (Lift bytes 'b, Lift (list 'n) ('f 'n), Lift int 'n, Lift bool 'v) = writeBits value indices bit = Builtin.writeBits (lower value) (List.map lower indices) (lower bit) toHex : Lift bytes 'b => 'b -> string -toHex value = Show.hexBytes (lower value) +toHex value = + value + |> lower + |> Show.hexBytes diff --git a/crates/nash-driver/base/src/Cardano/Value.nash b/crates/nash-driver/base/src/Cardano/Value.nash index add3e596..15807062 100644 --- a/crates/nash-driver/base/src/Cardano/Value.nash +++ b/crates/nash-driver/base/src/Cardano/Value.nash @@ -1,5 +1,6 @@ module Cardano.Value exposing (Value, empty, singleton, insert, lovelace, quantityOf, add, contains, scale) +import Function exposing ((|>)) import Option exposing (type option(..)) import Lift exposing (Lift) import Data exposing (FromData, ToData, Validate, Decode) @@ -7,8 +8,14 @@ import Data exposing (FromData, ToData, Validate, Decode) type alias Value = Map Bytes (Map Bytes Int) impl Lift value Value where - lift value = fromData (Builtin.valueData value) - lower value = Builtin.unValueData (toData value) + lift value = + value + |> Builtin.valueData + |> fromData + lower value = + value + |> toData + |> Builtin.unValueData impl Validate Value where validate raw = @@ -16,7 +23,9 @@ impl Validate Value where checked : Map Bytes (Map Bytes Int) checked = validate raw in - fromData (toData checked) + checked + |> toData + |> fromData empty : value empty = Builtin.unValueData (toData emptyMap) diff --git a/crates/nash-driver/base/src/Cons.nash b/crates/nash-driver/base/src/Cons.nash index 28f79af4..e97a40ef 100644 --- a/crates/nash-driver/base/src/Cons.nash +++ b/crates/nash-driver/base/src/Cons.nash @@ -1,6 +1,7 @@ module Cons exposing (type cons(..), map, indexedMap, map2, foldr, foldl, append, length, singleton, fromList, toList) import Builtin +import Function exposing ((|>)) import Lift exposing (Lift) import Primitive exposing (type bool(..)) @@ -60,7 +61,10 @@ length : cons 'a -> int length values = foldl (\_ count -> Builtin.addInteger count 1) 0 values fromList : Lift (list 'a) ('f 'a) => 'f 'a -> cons 'a -fromList values = fromLittleList (lower values) +fromList values = + values + |> lower + |> fromLittleList fromLittleList : list 'a -> cons 'a fromLittleList values = diff --git a/crates/nash-driver/base/src/Crypto.nash b/crates/nash-driver/base/src/Crypto.nash index 9163a2a2..e72aa39b 100644 --- a/crates/nash-driver/base/src/Crypto.nash +++ b/crates/nash-driver/base/src/Crypto.nash @@ -2,24 +2,43 @@ module Crypto exposing (sha2_256, sha3_256, blake2b_224, blake2b_256, keccak_256 import Lift exposing (Lift) import Builtin +import Function exposing ((|>)) sha2_256 : Lift bytes 'a => 'a -> bytes -sha2_256 value = Builtin.sha2_256 (lower value) +sha2_256 value = + value + |> lower + |> Builtin.sha2_256 sha3_256 : Lift bytes 'a => 'a -> bytes -sha3_256 value = Builtin.sha3_256 (lower value) +sha3_256 value = + value + |> lower + |> Builtin.sha3_256 blake2b_224 : Lift bytes 'a => 'a -> bytes -blake2b_224 value = Builtin.blake2b_224 (lower value) +blake2b_224 value = + value + |> lower + |> Builtin.blake2b_224 blake2b_256 : Lift bytes 'a => 'a -> bytes -blake2b_256 value = Builtin.blake2b_256 (lower value) +blake2b_256 value = + value + |> lower + |> Builtin.blake2b_256 keccak_256 : Lift bytes 'a => 'a -> bytes -keccak_256 value = Builtin.keccak_256 (lower value) +keccak_256 value = + value + |> lower + |> Builtin.keccak_256 ripemd_160 : Lift bytes 'a => 'a -> bytes -ripemd_160 value = Builtin.ripemd_160 (lower value) +ripemd_160 value = + value + |> lower + |> Builtin.ripemd_160 verifyEd25519Signature : (Lift bytes 'k, Lift bytes 'm, Lift bytes 's) => 'k -> 'm -> 's -> bool verifyEd25519Signature key message signature = Builtin.verifyEd25519Signature (lower key) (lower message) (lower signature) diff --git a/crates/nash-driver/base/src/Data.nash b/crates/nash-driver/base/src/Data.nash index bfa52ce6..d1f33eea 100644 --- a/crates/nash-driver/base/src/Data.nash +++ b/crates/nash-driver/base/src/Data.nash @@ -60,7 +60,10 @@ impl Validate 'a => Validate (List 'a) where candidate : List Data candidate = Primitive.coerce value in - Builtin.listData (mapList validate (Builtin.unListData candidate)) + candidate + |> Builtin.unListData + |> mapList validate + |> Builtin.listData impl (Validate 'k, Validate 'v) => Validate (Map 'k 'v) where validate value = @@ -68,7 +71,10 @@ impl (Validate 'k, Validate 'v) => Validate (Map 'k 'v) where candidate : Map Data Data candidate = Primitive.coerce value in - Builtin.mapData (mapList pairValidate (Builtin.unMapData candidate)) + candidate + |> Builtin.unMapData + |> mapList pairValidate + |> Builtin.mapData mapList : ('a -> 'b) -> list 'a -> list 'b mapList f values = @@ -90,10 +96,16 @@ trait Decode 'a where decode : Data -> option 'a impl ToData int where - toData value = Primitive.coerce (Builtin.iData value) + toData value = + value + |> Builtin.iData + |> Primitive.coerce impl FromData int where - fromData value = Builtin.unIData (Primitive.coerce value) + fromData value = + value + |> Primitive.coerce + |> Builtin.unIData impl Decode int where decode value = case value of @@ -101,10 +113,16 @@ impl Decode int where _ -> None impl ToData bytes where - toData value = Primitive.coerce (Builtin.bData value) + toData value = + value + |> Builtin.bData + |> Primitive.coerce impl FromData bytes where - fromData value = Builtin.unBData (Primitive.coerce value) + fromData value = + value + |> Primitive.coerce + |> Builtin.unBData impl Decode bytes where decode value = case value of @@ -112,10 +130,16 @@ impl Decode bytes where _ -> None impl ToData string where - toData value = toData (Builtin.encodeUtf8 value) + toData value = + value + |> Builtin.encodeUtf8 + |> toData impl FromData string where - fromData value = Builtin.decodeUtf8 (fromData value) + fromData value = + value + |> fromData + |> Builtin.decodeUtf8 impl Decode Data where decode value = Some value diff --git a/crates/nash-driver/base/src/Int.nash b/crates/nash-driver/base/src/Int.nash index 75699dcc..24e75485 100644 --- a/crates/nash-driver/base/src/Int.nash +++ b/crates/nash-driver/base/src/Int.nash @@ -30,7 +30,10 @@ rem : (Lift int 'a, Lift int 'b) => 'a -> 'b -> int rem a b = Builtin.remainderInteger (lower a) (lower b) negate : Lift int 'a => 'a -> int -negate value = Builtin.subtractInteger 0 (lower value) +negate value = + value + |> lower + |> Builtin.subtractInteger 0 compare : (Lift int 'a, Lift int 'b) => 'a -> 'b -> ordering compare a b = compareInt (lower a) (lower b) @@ -103,7 +106,10 @@ fromBytes : (Lift bool 'b, Lift bytes 'v) => 'b -> 'v -> int fromBytes bigEndian value = Builtin.byteStringToInteger (lower bigEndian) (lower value) toString : Lift int 'n => 'n -> string -toString value = showInt (lower value) +toString value = + value + |> lower + |> showInt showInt : int -> string showInt = Show.show diff --git a/crates/nash-driver/base/src/List.nash b/crates/nash-driver/base/src/List.nash index 5434870f..8972e09d 100644 --- a/crates/nash-driver/base/src/List.nash +++ b/crates/nash-driver/base/src/List.nash @@ -37,10 +37,16 @@ tail values = case lowerOuter values of _ :: xs -> Some xs isEmpty : Lift (list 'a) ('f 'a) => ('f 'a) -> bool -isEmpty values = Builtin.nullList (lowerOuter values) +isEmpty values = + values + |> lowerOuter + |> Builtin.nullList length : Lift (list 'a) ('f 'a) => ('f 'a) -> int -length values = foldlList (\_ n -> n + 1) 0 (lowerOuter values) +length values = + values + |> lowerOuter + |> foldlList (\_ n -> n + 1) 0 isLength : (Lift (list 'a) ('f 'a), Lift int 'n) => ('f 'a) -> 'n -> bool isLength values mustLength = @@ -58,7 +64,10 @@ isLength values mustLength = _ -> False reverse : Lift (list 'a) ('f 'a) => ('f 'a) -> list 'a -reverse values = foldlList (\x xs -> x :: xs) [] (lowerOuter values) +reverse values = + values + |> lowerOuter + |> foldlList (\x xs -> x :: xs) [] append : (Lift (list 'a) ('f 'a), Lift (list 'a) ('g 'a)) => ('f 'a) -> ('g 'a) -> list 'a append xs ys = appendList (lowerOuter xs) (lowerOuter ys) @@ -69,7 +78,10 @@ appendList xs ys = case xs of x :: rest -> x :: appendList rest ys concat : (Lift (list ('f 'a)) ('g ('f 'a)), Lift (list 'a) ('f 'a)) => 'g ('f 'a) -> list 'a -concat values = foldrList (\xs acc -> appendList (lowerOuter xs) acc) [] (lowerOuter values) +concat values = + values + |> lowerOuter + |> foldrList (\xs acc -> appendList (lowerOuter xs) acc) [] map : Lift (list 'a) ('f 'a) => ('a -> 'b) -> ('f 'a) -> list 'b map f values = Functor.map f (lowerOuter values) @@ -172,7 +184,10 @@ map2List f xs ys = case (xs, ys) of _ -> [] sort : (Ord 'a, Lift (list 'a) ('f 'a)) => ('f 'a) -> list 'a -sort values = sortList compare (lowerOuter values) +sort values = + values + |> lowerOuter + |> sortList compare sortBy : (Lift (list 'a) ('f 'a), Lift ordering 'o) => ('a -> 'a -> 'o) -> ('f 'a) -> list 'a sortBy cmp values = sortList (\a b -> lower (cmp a b)) (lowerOuter values) @@ -188,7 +203,10 @@ insert cmp x xs = case xs of _ -> x :: xs sum : (Lift int 'a, Lift (list 'a) ('f 'a)) => 'f 'a -> int -sum values = foldlList (\x acc -> Builtin.addInteger (lower x) acc) 0 (lowerOuter values) +sum values = + values + |> lowerOuter + |> foldlList (\x acc -> Builtin.addInteger (lower x) acc) 0 partition : (Lift (list 'a) ('f 'a), Lift bool 'p) => ('a -> 'p) -> ('f 'a) -> (list 'a, list 'a) partition p values = partitionList (\x -> lower (p x)) (lowerOuter values) @@ -201,7 +219,10 @@ partitionList p xs = case xs of if p x then (x :: yes, no) else (yes, x :: no) toArray : Lift (list 'a) ('f 'a) => ('f 'a) -> array 'a -toArray values = Builtin.listToArray (lowerOuter values) +toArray values = + values + |> lowerOuter + |> Builtin.listToArray lowerOuter : Lift (list 'a) ('f 'a) => 'f 'a -> list 'a lowerOuter = lower diff --git a/crates/nash-driver/base/src/Map.nash b/crates/nash-driver/base/src/Map.nash index d80e2617..624eb1ab 100644 --- a/crates/nash-driver/base/src/Map.nash +++ b/crates/nash-driver/base/src/Map.nash @@ -1,6 +1,7 @@ module Map exposing (empty, singleton, insert, get, remove, keys, values, toList, foldl, union) import Builtin +import Function exposing ((|>)) import Primitive exposing (type bool(..)) import Lift exposing (Lift) import Eq exposing (Eq) @@ -52,10 +53,16 @@ remove : (Eq 'k, Lift (list (pair 'k 'v)) 'm) => 'k -> 'm -> list (pair 'k 'v) remove key entries = List.filter (\entry -> let pair(k, _) = entry in neq key k) (toList entries) keys : Lift (list (pair 'k 'v)) 'input => 'input -> list 'k -keys entries = Functor.map Pair.fst (lower entries) +keys entries = + entries + |> lower + |> Functor.map Pair.fst values : Lift (list (pair 'k 'v)) 'input => 'input -> list 'v -values entries = Functor.map Pair.snd (lower entries) +values entries = + entries + |> lower + |> Functor.map Pair.snd toList : Lift (list (pair 'k 'v)) 'm => 'm -> list (pair 'k 'v) toList = lower diff --git a/crates/nash-driver/base/src/Prop.nash b/crates/nash-driver/base/src/Prop.nash index f2ae5020..4963814a 100644 --- a/crates/nash-driver/base/src/Prop.nash +++ b/crates/nash-driver/base/src/Prop.nash @@ -18,7 +18,10 @@ type prng = Seeded bytes (cons choiceTree) | Replayed (cons choiceTree) (cons ch type alias generator 'a = prng -> option ('a, prng) choice : Lift int 'n => 'n -> generator int -choice bound = choiceInt (lower bound) +choice bound = + bound + |> lower + |> choiceInt choiceInt : int -> generator int choiceInt bound state = diff --git a/crates/nash-driver/base/src/Show.nash b/crates/nash-driver/base/src/Show.nash index 8d385f52..91ba8293 100644 --- a/crates/nash-driver/base/src/Show.nash +++ b/crates/nash-driver/base/src/Show.nash @@ -1,6 +1,7 @@ module Show exposing (Show, hexBytes) import Builtin +import Function exposing ((|>)) import Primitive exposing (Data(..), type bool(..)) import Literal @@ -11,8 +12,15 @@ trait Show 'a where impl Show int where show n = if Builtin.lessThanInteger n 0 then - Builtin.appendString "-" (Builtin.decodeUtf8 (decimal (Builtin.subtractInteger 0 n))) - else Builtin.decodeUtf8 (decimal n) + n + |> Builtin.subtractInteger 0 + |> decimal + |> Builtin.decodeUtf8 + |> Builtin.appendString "-" + else + n + |> decimal + |> Builtin.decodeUtf8 decimal : int -> bytes decimal n = @@ -109,16 +117,29 @@ impl Show Data where B bytes -> Builtin.appendString "B " (show bytes) impl Show Int where - show value = show (Builtin.unIData value) + show value = + value + |> Builtin.unIData + |> show impl Show Bytes where - show value = show (Builtin.unBData value) + show value = + value + |> Builtin.unBData + |> show impl Show 'a => Show (List 'a) where - show value = show (Builtin.unListData value) + show value = + value + |> Builtin.unListData + |> show impl (Show 'k, Show 'v) => Show (Map 'k 'v) where - show value = Builtin.appendString "Map " (show (Builtin.unMapData value)) + show value = + value + |> Builtin.unMapData + |> show + |> Builtin.appendString "Map " hexBytes : bytes -> string hexBytes value = Builtin.decodeUtf8 (hex value 0) diff --git a/crates/nash-driver/base/src/String.nash b/crates/nash-driver/base/src/String.nash index ad50d942..b9298511 100644 --- a/crates/nash-driver/base/src/String.nash +++ b/crates/nash-driver/base/src/String.nash @@ -3,13 +3,17 @@ module String exposing (toBytes, fromBytes, fromInt, concat, join) import Literal import Lift exposing (Lift) import Builtin +import Function exposing ((|>)) import Int toBytes : string -> bytes toBytes = Builtin.encodeUtf8 fromBytes : Lift bytes 'b => 'b -> string -fromBytes value = Builtin.decodeUtf8 (lower value) +fromBytes value = + value + |> lower + |> Builtin.decodeUtf8 fromInt : Lift int 'n => 'n -> string fromInt = Int.toString From 4c3c9dbb0e0e7cc077e113e9d4e31fa0e3ab9a72 Mon Sep 17 00:00:00 2001 From: microproofs Date: Fri, 25 Sep 2026 19:59:05 -0400 Subject: [PATCH 09/10] feat(can): default-import Function Signed-off-by: microproofs --- .sampo/changesets/function-default-import.md | 5 +++++ crates/nash-can/src/defaults.rs | 1 + docs/stdlib.md | 1 + tests/base/app/src/Main.nash | 3 +++ 4 files changed, 10 insertions(+) create mode 100644 .sampo/changesets/function-default-import.md diff --git a/.sampo/changesets/function-default-import.md b/.sampo/changesets/function-default-import.md new file mode 100644 index 00000000..03f91528 --- /dev/null +++ b/.sampo/changesets/function-default-import.md @@ -0,0 +1,5 @@ +--- +cargo/nash-can: minor +--- + +Add Function to the application default imports for qualified access to applyForward and applyBackward. diff --git a/crates/nash-can/src/defaults.rs b/crates/nash-can/src/defaults.rs index 22b74274..d9eb3ee9 100644 --- a/crates/nash-can/src/defaults.rs +++ b/crates/nash-can/src/defaults.rs @@ -30,6 +30,7 @@ pub const MODULES: &[(&str, &[&str])] = &[ ("Option", &["Option", "type option"]), ("Ordering", &["Ordering", "type ordering"]), ("Cons", &["type cons"]), + ("Function", &[]), ("Int", &[]), ("Rational", &[]), ("Crypto", &[]), diff --git a/docs/stdlib.md b/docs/stdlib.md index e24bbf91..66e13daf 100644 --- a/docs/stdlib.md +++ b/docs/stdlib.md @@ -108,6 +108,7 @@ import Option exposing (Option, type option(..)) import Ordering exposing (Ordering, type ordering(..)) import Cons exposing (type cons(..)) import Builtin +import Function import Int import Rational import Crypto diff --git a/tests/base/app/src/Main.nash b/tests/base/app/src/Main.nash index dbdf8af4..324bd86e 100644 --- a/tests/base/app/src/Main.nash +++ b/tests/base/app/src/Main.nash @@ -10,6 +10,9 @@ bigArray = Array.fromList [Packet integerLiteral] bigArrayResult : option Packet bigArrayResult = Array.get bigArray 0 +forwardApplied = Function.applyForward 1 (\x -> x + 1) +backwardApplied = Function.applyBackward (\x -> x + 1) 1 + mappedOption = map (\x -> (x, x)) (Some ()) appliedOption = apply (Some (\x -> (x, x))) (Some ()) optionDo = do From 16c4546ca2a0271a29f9eae636752ce50e1d89a7 Mon Sep 17 00:00:00 2001 From: microproofs Date: Fri, 25 Sep 2026 22:15:38 -0400 Subject: [PATCH 10/10] chore: fmt a few of the base nash files Signed-off-by: microproofs --- crates/nash-driver/base/src/Cardano/Tx.nash | 130 ++++++++++++++++---- crates/nash-driver/base/src/Prelude.nash | 3 + crates/nash-driver/base/src/Prop.nash | 14 ++- 3 files changed, 117 insertions(+), 30 deletions(-) diff --git a/crates/nash-driver/base/src/Cardano/Tx.nash b/crates/nash-driver/base/src/Cardano/Tx.nash index 744febb7..ead64e6f 100644 --- a/crates/nash-driver/base/src/Cardano/Tx.nash +++ b/crates/nash-driver/base/src/Cardano/Tx.nash @@ -10,43 +10,116 @@ import Cardano.Address exposing (Address, Credential) import Cardano.Time exposing (Interval) import Cardano.Value exposing (Value) -type DRep = DRep Credential | DRepAlwaysAbstain | DRepAlwaysNoConfidence - -type Delegatee = DelegStake Bytes | DelegVote DRep | DelegStakeVote Bytes DRep - -type Certificate = RegStaking Credential (Option Int) | UnRegStaking Credential (Option Int) | DelegStaking Credential Delegatee | RegDeleg Credential Delegatee Int | RegDRep Credential Int | UpdateDRep Credential | UnRegDRep Credential Int | PoolRegister Bytes Bytes | PoolRetire Bytes Int | AuthHotCommittee Credential Credential | ResignColdCommittee Credential - -type Voter = CommitteeVoter Credential | DRepVoter Credential | StakePoolVoter Bytes - -type Vote = VoteNo | VoteYes | Abstain - -type GovernanceActionId = GovernanceActionId { txId : Bytes, index : Int } +type DRep + = DRep Credential + | DRepAlwaysAbstain + | DRepAlwaysNoConfidence + +type Delegatee + = DelegStake Bytes + | DelegVote DRep + | DelegStakeVote Bytes DRep + +type Certificate + = RegStaking Credential (Option Int) + | UnRegStaking Credential (Option Int) + | DelegStaking Credential Delegatee + | RegDeleg Credential Delegatee Int + | RegDRep Credential Int + | UpdateDRep Credential + | UnRegDRep Credential Int + | PoolRegister Bytes Bytes + | PoolRetire Bytes Int + | AuthHotCommittee Credential Credential + | ResignColdCommittee Credential + +type Voter + = CommitteeVoter Credential + | DRepVoter Credential + | StakePoolVoter Bytes + +type Vote + = VoteNo + | VoteYes + | Abstain + +type GovernanceActionId = GovernanceActionId + { txId : Bytes + , index : Int + } -type Rational = Rational { numerator : Int, denominator : Int } +type Rational = Rational + { numerator : Int + , denominator : Int + } -type Committee = Committee { members : Map Credential Int, quorum : Rational } +type Committee = Committee + { members : Map Credential Int + , quorum : Rational + } type Constitution = Constitution (Option Bytes) -type ProtocolVersion = ProtocolVersion { major : Int, minor : Int } - -type GovernanceAction = ParameterChange (Option GovernanceActionId) Data (Option Bytes) | HardForkInitiation (Option GovernanceActionId) ProtocolVersion | TreasuryWithdrawals (Map Credential Int) (Option Bytes) | NoConfidence (Option GovernanceActionId) | UpdateCommittee (Option GovernanceActionId) (List Credential) (Map Credential Int) Rational | NewConstitution (Option GovernanceActionId) Constitution | InfoAction - -type Proposal = Proposal { deposit : Int, returnAddress : Credential, governanceAction : GovernanceAction } - -type OutputReference = OutputReference { txId : Bytes, index : Int } +type ProtocolVersion = ProtocolVersion + { major : Int + , minor : Int + } -type Datum = NoDatum | DatumHash Bytes | InlineDatum Data +type GovernanceAction + = ParameterChange (Option GovernanceActionId) Data (Option Bytes) + | HardForkInitiation (Option GovernanceActionId) ProtocolVersion + | TreasuryWithdrawals (Map Credential Int) (Option Bytes) + | NoConfidence (Option GovernanceActionId) + | UpdateCommittee (Option GovernanceActionId) (List Credential) (Map Credential Int) Rational + | NewConstitution (Option GovernanceActionId) Constitution + | InfoAction + +type Proposal = Proposal + { deposit : Int + , returnAddress : Credential + , governanceAction : GovernanceAction + } -type Output = Output { address : Address, value : Value, datum : Datum, script : Option Bytes } +type OutputReference = OutputReference + { txId : Bytes + , index : Int + } -type Input = Input { outputReference : OutputReference, output : Output } +type Datum + = NoDatum + | DatumHash Bytes + | InlineDatum Data -type ScriptPurpose = Minting Bytes | Spending OutputReference | Rewarding Credential | Certifying Int Certificate | Voting Voter | Proposing Int Proposal +type Output = Output + { address : Address + , value : Value + , datum : Datum + , script : Option Bytes + } -type ScriptInfo = MintingScript Bytes | SpendingScript OutputReference (Option Data) | RewardingScript Credential | CertifyingScript Int Certificate | VotingScript Voter | ProposingScript Int Proposal +type Input = Input + { outputReference : OutputReference + , output : Output + } -type Tx = Tx { inputs : List Input +type ScriptPurpose + = Minting Bytes + | Spending OutputReference + | Rewarding Credential + | Certifying Int Certificate + | Voting Voter + | Proposing Int Proposal + +type ScriptInfo + = MintingScript Bytes + | SpendingScript OutputReference (Option Data) + | RewardingScript Credential + | CertifyingScript Int Certificate + | VotingScript Voter + | ProposingScript Int Proposal + +type Tx = Tx + { inputs : List Input , referenceInputs : List Input , outputs : List Output , fee : Int @@ -64,7 +137,10 @@ type Tx = Tx { inputs : List Input , treasuryDonation : Option Int } -type ScriptContext = ScriptContext { tx : Tx, redeemer : Data, scriptInfo : ScriptInfo } +type ScriptContext = ScriptContext + { tx : Tx, redeemer : Data + , scriptInfo : ScriptInfo + } impl Validate DRep where validate raw = diff --git a/crates/nash-driver/base/src/Prelude.nash b/crates/nash-driver/base/src/Prelude.nash index 7af0b180..57722023 100644 --- a/crates/nash-driver/base/src/Prelude.nash +++ b/crates/nash-driver/base/src/Prelude.nash @@ -51,6 +51,9 @@ applyForward = Function.applyForward applyBackward : ('a -> 'b) -> 'a -> 'b applyBackward = Function.applyBackward + + + composeLeft : ('b -> 'c) -> ('a -> 'b) -> 'a -> 'c composeLeft g f x = g (f x) diff --git a/crates/nash-driver/base/src/Prop.nash b/crates/nash-driver/base/src/Prop.nash index 4963814a..e5abef15 100644 --- a/crates/nash-driver/base/src/Prop.nash +++ b/crates/nash-driver/base/src/Prop.nash @@ -13,9 +13,17 @@ import Lift exposing (Lift) import Option exposing (type option(..)) import Cons exposing (type cons(..)) -type choiceTree = Choice int | Group (cons choiceTree) -type prng = Seeded bytes (cons choiceTree) | Replayed (cons choiceTree) (cons choiceTree) | Rebuilding (list int) (cons choiceTree) -type alias generator 'a = prng -> option ('a, prng) +type choiceTree + = Choice int + | Group (cons choiceTree) + +type prng + = Seeded bytes (cons choiceTree) + | Replayed (cons choiceTree) (cons choiceTree) + | Rebuilding (list int) (cons choiceTree) + +type alias generator 'a + = prng -> option ('a, prng) choice : Lift int 'n => 'n -> generator int choice bound =