aboutsummaryrefslogtreecommitdiff
path: root/stdlib/source/test/lux/control/function/memo.lux
blob: b532b78f2e72febcef7c677195330ea6a6e9040e (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
... This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
... If a copy of the MPL was not distributed with this file, You can obtain one at https://mozilla.org/MPL/2.0/.

(.require
 [library
  [lux (.except)
   [abstract
    [monad (.only do)]]
   [control
    ["[0]" io (.only IO)]
    ["[0]" state (.only State) (.use "[1]#[0]" monad)]]
   [data
    ["[0]" product]
    [collection
     ["[0]" dictionary (.only Dictionary)]
     ["[0]" list (.use "[1]#[0]" functor mix)]]]
   [math
    ["[0]" random]
    [number
     ["n" nat]
     ["[0]" i64]]]
   [meta
    [macro
     ["^" pattern]]]
   [world
    [time
     ["[0]" instant]
     ["[0]" duration (.only Duration)]]]
   [test
    ["_" property (.only Test)]]]]
 [\\library
  ["[0]" / (.only)
   ["/[1]" //
    ["[1]" mixin]]]])

(def (fibonacci again input)
  (/.Memo Nat Nat)
  (when input
    0 (state#in 0)
    1 (state#in 1)
    _ (do state.monad
        [output_1 (again (n.- 1 input))
         output_2 (again (n.- 2 input))]
        (in (n.+ output_1 output_2)))))

(def (time function input)
  (All (_ i o) (-> (-> i o) i (IO [Duration o])))
  (do io.monad
    [before instant.now
     .let [output (function input)]
     after instant.now]
    (in [(instant.span before after)
         output])))

(def milli_seconds
  (-> Duration Nat)
  (|>> (duration.ticks duration.milli_second) .nat))

... the wiggle room is there to account for GC pauses
... and other issues that might mess with duration
(def wiggle_room
  Nat
  (i64.left_shifted 4 1))

(def .public test
  Test
  (<| (_.covering /._)
      (do [! random.monad]
        [input (|> random.nat (of ! each (|>> (n.% 5) (n.+ 21))))])
      (_.for [/.Memo])
      (all _.and
           (_.coverage [/.closed /.none]
             (io.run!
              (do io.monad
                [.let [slow (/.none n.hash ..fibonacci)
                       fast (/.closed n.hash fibonacci)]
                 [slow_time slow_output] (..time slow input)
                 [fast_time fast_output] (..time fast input)
                 .let [same_output!
                       (n.= slow_output
                            fast_output)

                       memo_is_faster!
                       (n.< (n.+ ..wiggle_room (milli_seconds slow_time))
                            (milli_seconds fast_time))]]
                (in (and same_output!
                         memo_is_faster!)))))
           (_.coverage [/.open]
             (io.run!
              (do io.monad
                [.let [none (/.none n.hash ..fibonacci)
                       memory (dictionary.empty n.hash)
                       open (/.open fibonacci)]
                 [none_time none_output] (..time none input)
                 [open_time [memory open_output]] (..time open [memory input])
                 [open_time/+1 _] (..time open [memory (++ input)])
                 .let [same_output!
                       (n.= none_output
                            open_output)

                       memo_is_faster!
                       (n.< (n.+ ..wiggle_room (milli_seconds none_time))
                            (milli_seconds open_time))

                       incrementalism_is_faster!
                       (n.< (n.+ ..wiggle_room (milli_seconds open_time))
                            (milli_seconds open_time/+1))]]
                (in (and same_output!
                         memo_is_faster!
                         incrementalism_is_faster!)))))
           (_.coverage [/.memoization]
             (let [memo (<| //.fixed
                            (//.mixed /.memoization)
                            (is (//.Mixin Nat (State (Dictionary Nat Nat) Nat))
                                (function (factorial delegate again input)
                                  (when input
                                    (^.or 0 1) (of state.monad in 1)
                                    _ (do state.monad
                                        [output' (again (-- input))]
                                        (in (n.* input output')))))))
                   expected (|> (list.indices input)
                                (list#each ++)
                                (list#mix n.* 1))
                   actual (|> (memo input)
                              (state.result (dictionary.empty n.hash))
                              product.right)]
               (n.= expected actual)))
           )))