summaryrefslogtreecommitdiff
path: root/src/unheard/interval_test.clj
blob: 6878923614306ff3848c0d9987b871c7cf4d0999 (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
(ns unheard.interval-test
  (:require [unheard.interval :as sut]
            [hyperfiddle.rcf :refer [tests]]))

(tests "Create interval with ratio boundaries and value"
       (let [iv (sut/ratio-interval 1/4 3/4 {:note :C, :velocity 64})]
         (.getNormStart iv)
         :=
         1/4
         (.getNormEnd iv)
         :=
         3/4 (sut/interval-value iv)
         := {:note :C, :velocity 64}))

(tests "Interval unique identifier"
       (let [iv (sut/ratio-interval 1/4 3/4 :test)]
         (.getUniqueIdentifier iv)
         :=
         "[1/4,3/4]"))

(tests "Interval comparison - start takes precedence"
       (let [iv1 (sut/ratio-interval 1/4 1/2 :first)
             iv2 (sut/ratio-interval 1/2 3/4 :second)
             iv3 (sut/ratio-interval 1/3 2/3 :third)]
         ;; iv1 starts before iv2
         (.compareTo iv1 iv2)
         :=
         -1
         (.compareTo iv2 iv1)
         :=
         1
         ;; iv1 starts before iv3
         (.compareTo iv1 iv3)
         :=
         -1
           ;; iv3 starts after iv1 but before iv2
           (.compareTo iv3 iv2)
         := -1))

(tests "Interval comparison - end is tiebreaker when starts are equal"
       (let [iv1 (sut/ratio-interval 1/4 1/2 :a)
             iv2 (sut/ratio-interval 1/4 3/4 :b)]
         ;; Same start, iv1 ends before iv2
         (.compareTo iv1 iv2)
         :=
         -1 (.compareTo iv2 iv1)
         := 1))

(tests "Interval comparison - equal intervals"
       (let [iv1 (sut/ratio-interval 1/4 1/2 :a)
             iv2 (sut/ratio-interval 1/4 1/2 :b)]
         ;; Same boundaries (values don't matter for comparison)
         (.compareTo iv1 iv2)
         :=
         0))

(tests "Find overlaps - basic cases"
       (let [coll (sut/create-ratio-interval-collection)
             iv1 (sut/ratio-interval 1/4 1/2 :first)
             iv2 (sut/ratio-interval 1/2 3/4 :second)
             iv3 (sut/ratio-interval 1/3 2/3 :third)]
         (.add coll iv1)
         (.add coll iv2)
         (.add coll iv3)
         ;; Query [1/3, 1/2] should overlap with all three
         (let [query (sut/ratio-interval 1/3 1/2 nil)
               overlaps (sut/find-overlaps coll query)]
           (count overlaps)
           :=
           3)
         ;; Query [5/8, 7/8] should overlap with iv2 and iv3
         (let [query (sut/ratio-interval 5/8 7/8 nil)
               overlaps (sut/find-overlaps coll query)]
           (count overlaps)
           :=
           2)))

(tests "Find overlaps - touching intervals at boundaries"
       (let [coll (sut/create-ratio-interval-collection)
             iv1 (sut/ratio-interval 0 1/4 :left)
             iv2 (sut/ratio-interval 1/4 1/2 :right)]
         (.add coll iv1)
         (.add coll iv2)
         ;; Query at exact boundary point should overlap both
         (let [query (sut/ratio-interval 1/4 1/4 :boundary)
               overlaps (sut/find-overlaps coll query)]
           (count overlaps)
           :=
           2 (set (map sut/interval-value overlaps))
           := #{:left :right})))

(tests "Find overlaps - point interval"
       (let [coll (sut/create-ratio-interval-collection)
             iv1 (sut/ratio-interval 1/4 1/2 :wide)
             iv2 (sut/ratio-interval 1/3 1/3 :point)]
         (.add coll iv1)
         (.add coll iv2)
         ;; Point query inside wide interval
         (let [query (sut/ratio-interval 1/3 1/3 nil)
               overlaps (sut/find-overlaps coll query)]
           (count overlaps)
           :=
           2)))

(tests "Find overlaps - no overlap"
       (let [coll (sut/create-ratio-interval-collection)
             iv1 (sut/ratio-interval 1/4 1/2 :first)
             iv2 (sut/ratio-interval 3/4 7/8 :second)]
         (.add coll iv1)
         (.add coll iv2)
         ;; Query between the two intervals
         (let [query (sut/ratio-interval 5/8 11/16 nil)
               overlaps (sut/find-overlaps coll query)]
           (count overlaps)
           :=
           0)))

(tests "Interval with improper fractions"
       (let [iv (sut/ratio-interval 5/4 7/4 :improper)]
         (.getNormStart iv)
         :=
         5/4
         (.getNormEnd iv)
         :=
         7/4 (sut/interval-value iv)
         := :improper))

(tests "Interval with negative ratios"
       (let [iv (sut/ratio-interval -3/4 -1/4 :negative)]
         (.getNormStart iv)
         :=
         -3/4 (.getNormEnd iv)
         := -1/4))

(tests "Interval toString representation"
       (let [iv (sut/ratio-interval 1/4 3/4 {:data :test})]
         (str iv)
         :=
         "[1/4, 3/4] -> {:data :test}"))

(tests "Collection operations"
       (let [coll (sut/create-ratio-interval-collection)
             iv1 (sut/ratio-interval 1/4 1/2 :first)
             iv2 (sut/ratio-interval 1/2 3/4 :second)]
         (.add coll iv1)
         (.add coll iv2)
         (.size coll)
         :=
         2
         ;; Can iterate over collection
         (set (map sut/interval-value coll))
         :=
         #{:first :second}
         ;; Can clear collection
         (.clear coll)
         (.size coll)
         :=
         0))

(tests "Value field can hold various types"
       (let [iv-map (sut/ratio-interval 0 1 {:key :value})
             iv-keyword (sut/ratio-interval 0 1 :keyword)
             iv-string (sut/ratio-interval 0 1 "string")
             iv-number (sut/ratio-interval 0 1 42)
             iv-nil (sut/ratio-interval 0 1 nil)]
         (sut/interval-value iv-map)
         :=
         {:key :value}
         (sut/interval-value iv-keyword)
         :=
         :keyword
         (sut/interval-value iv-string)
         :=
         "string"
         (sut/interval-value iv-number)
         :=
         42 (sut/interval-value iv-nil)
         := nil))