aboutsummaryrefslogtreecommitdiff
path: root/stdlib/source/test/lux/abstract/interval.lux
blob: 62b56a5fb50be631f37641388fd5ef4b35c2a8f3 (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
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
(.module:
  [lux #*
   ["_" test (#+ Test)]
   [abstract/monad (#+ do)]
   [abstract
    ["." order]
    {[0 #test]
     [/
      ["$." equivalence]]}]
   [control
    [pipe (#+ case>)]]
   [data
    [number
     ["." nat]]
    [text
     format]
    [collection
     ["." set]
     ["." list]]]
   [math
    ["r" random (#+ Random)]]]
  {1
   ["." / (#+ Interval) (",@." equivalence)]})

(template [<name> <cmp>]
  [(def: #export <name>
     (Random (Interval Nat))
     (do r.monad
       [bottom r.nat
        top (|> r.nat (r.filter (|>> (n/= bottom) not)))]
       (if (<cmp> top bottom)
         (wrap (/.between nat.enum bottom top))
         (wrap (/.between nat.enum top bottom)))))]

  [inner n/<]
  [outer n/>]
  )

(def: #export singleton
  (Random (Interval Nat))
  (do r.monad
    [point r.nat]
    (wrap (/.singleton nat.enum point))))

(def: #export interval
  (Random (Interval Nat))
  ($_ r.either
      ..inner
      ..outer
      ..singleton))

(def: boundaries
  Test
  (do r.monad
    [bottom r.nat
     top r.nat
     #let [interval (/.between nat.enum bottom top)]]
    ($_ _.and
        (_.test "A boundary value belongs to its interval."
                (and (/.within? interval bottom)
                     (/.within? interval top)))
        (_.test "An interval starts with its bottom."
                (/.starts-with? bottom interval))
        (_.test "An interval ends with its top."
                (/.ends-with? top interval))
        (_.test "The boundary values border the interval."
                (and (/.borders? interval bottom)
                     (/.borders? interval top)))
        )))

(def: union
  Test
  (do r.monad
    [some-interval ..interval
     left-inner ..inner
     right-inner ..inner
     left-singleton ..singleton
     right-singleton ..singleton
     left-outer ..outer
     right-outer ..outer]
    ($_ _.and
        (_.test "The union of an interval to itself yields the same interval."
                (,@= some-interval (/.union some-interval some-interval)))
        (_.test "The union of 2 inner intervals is another inner interval."
                (/.inner? (/.union left-inner right-inner)))
        (_.test "The union of 2 outer intervals yields an inner interval when their complements don't overlap, and an outer when they do."
                (if (/.overlaps? (/.complement left-outer) (/.complement right-outer))
                  (/.outer? (/.union left-outer right-outer))
                  (/.inner? (/.union left-outer right-outer))))
        )))

(def: intersection
  Test
  (do r.monad
    [some-interval ..interval
     left-inner ..inner
     right-inner ..inner
     left-singleton ..singleton
     right-singleton ..singleton
     left-outer ..outer
     right-outer ..outer]
    ($_ _.and
        (_.test "The intersection of an interval to itself yields the same interval."
                (,@= some-interval (/.intersection some-interval some-interval)))
        (_.test "The intersection of 2 inner intervals yields an inner interval when they overlap, and an outer when they don't."
                (if (/.overlaps? left-inner right-inner)
                  (/.inner? (/.intersection left-inner right-inner))
                  (/.outer? (/.intersection left-inner right-inner))))
        (_.test "The intersection of 2 outer intervals is another outer interval."
                (/.outer? (/.intersection left-outer right-outer)))
        )))

(def: complement
  Test
  (do r.monad
    [some-interval ..interval]
    ($_ _.and
        (_.test "The complement of a complement is the same as the original."
                (,@= some-interval (|> some-interval /.complement /.complement)))
        (_.test "The complement of an interval does not overlap it."
                (not (/.overlaps? some-interval (/.complement some-interval))))
        )))

(def: location
  Test
  (do r.monad
    [[l m r] (|> (r.set nat.hash 3 r.nat)
                 (:: @ map (|>> set.to-list
                                (list.sort n/<)
                                (case> (^ (list b t1 t2))
                                       [b t1 t2]

                                       _
                                       (undefined)))))
     #let [left (/.singleton nat.enum l)
           right (/.singleton nat.enum r)]]
    ($_ _.and
        (_.test "'precedes?' and 'succeeds?' are symetric."
                (and (/.precedes? right left)
                     (/.succeeds? left right)))
        (_.test "Can check if an interval is before or after some element."
                (and (/.before? m left)
                     (/.after? m right)))
        )))

(def: touch
  Test
  (do r.monad
    [[b t1 t2] (|> (r.set nat.hash 3 r.nat)
                   (:: @ map (|>> set.to-list
                                  (list.sort n/<)
                                  (case> (^ (list b t1 t2))
                                         [b t1 t2]

                                         _
                                         (undefined)))))
     #let [int-left (/.between nat.enum t1 t2)
           int-right (/.between nat.enum b t1)]]
    ($_ _.and
        (_.test "An interval meets another if its top is the other's bottom."
                (/.meets? int-left int-right))
        (_.test "Two intervals touch one another if any one meets the other."
                (/.touches? int-left int-right))
        (_.test "Can check if 2 intervals start together."
                (/.starts? (/.between nat.enum b t2)
                           (/.between nat.enum b t1)))
        (_.test "Can check if 2 intervals finish together."
                (/.finishes? (/.between nat.enum b t2)
                             (/.between nat.enum t1 t2)))
        )))

(def: overlap
  Test
  (do r.monad
    [some-interval ..interval
     [x0 x1 x2 x3] (|> (r.set nat.hash 4 r.nat)
                       (:: @ map (|>> set.to-list
                                      (list.sort n/<)
                                      (case> (^ (list x0 x1 x2 x3))
                                             [x0 x1 x2 x3]

                                             _
                                             (undefined)))))]
    ($_ _.and
        (_.test "Every interval is nested into itself."
                (/.nested? some-interval some-interval))
        (_.test "No interval overlaps with itself."
                (not (/.overlaps? some-interval some-interval)))
        (let [small-inner (/.between nat.enum x1 x2)
              large-inner (/.between nat.enum x0 x3)]
          (_.test "Inner intervals can be nested inside one another."
                  (and (/.nested? large-inner small-inner)
                       (not (/.nested? small-inner large-inner)))))
        (let [left-inner (/.between nat.enum x0 x2)
              right-inner (/.between nat.enum x1 x3)]
          (_.test "Inner intervals can overlap one another."
                  (and (/.overlaps? left-inner right-inner)
                       (/.overlaps? right-inner left-inner))))
        (let [small-outer (/.between nat.enum x2 x1)
              large-outer (/.between nat.enum x3 x0)]
          (_.test "Outer intervals can be nested inside one another."
                  (and (/.nested? small-outer large-outer)
                       (not (/.nested? large-outer small-outer)))))
        (let [left-inner (/.between nat.enum x0 x1)
              right-inner (/.between nat.enum x2 x3)
              outer (/.between nat.enum x0 x3)]
          (_.test "Inners can be nested inside outers."
                  (and (/.nested? outer left-inner)
                       (/.nested? outer right-inner))))
        (let [left-inner (/.between nat.enum x0 x2)
              right-inner (/.between nat.enum x1 x3)
              outer (/.between nat.enum x1 x2)]
          (_.test "Inners can overlap outers."
                  (and (/.overlaps? outer left-inner)
                       (/.overlaps? outer right-inner))))
        )))

(def: #export test
  Test
  (<| (_.context (%name (name-of /.Interval)))
      ($_ _.and
          ($equivalence.spec /.equivalence ..interval)
          (<| (_.context "Boundaries.")
              ..boundaries)
          (<| (_.context "Union.")
              ..union)
          (<| (_.context "Intersection.")
              ..intersection)
          (<| (_.context "Complement.")
              ..complement)
          (<| (_.context "Positioning/location.")
              ..location)
          (<| (_.context "Touching intervals.")
              ..touch)
          (<| (_.context "Nesting & overlap.")
              ..overlap)
          )))

(def: #export (spec (^open ",@.") gen-sample)
  (All [a] (-> (Interval a) (Random a) Test))
  (<| (_.context (%name (name-of /.Interval)))
      (do r.monad
        [sample gen-sample]
        ($_ _.and
            (_.test "No value is bigger than the top."
                    (,@< ,@top sample))
            (_.test "No value is smaller than the bottom."
                    (order.> ,@&order ,@bottom sample))
            ))))