piecewise_linear.rs (11301B)
1 /* This Source Code Form is subject to the terms of the Mozilla Public 2 * License, v. 2.0. If a copy of the MPL was not distributed with this 3 * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ 4 5 use euclid::approxeq::ApproxEq; 6 use style::piecewise_linear::{PiecewiseLinearFunction, PiecewiseLinearFunctionBuilder}; 7 8 fn get_linear_keyword_equivalent() -> PiecewiseLinearFunction { 9 PiecewiseLinearFunctionBuilder::default().build() 10 } 11 #[test] 12 fn linear_keyword_equivalent_in_bound() { 13 let function = get_linear_keyword_equivalent(); 14 assert!(function.at(0.).approx_eq(&0.)); 15 assert!(function.at(0.5).approx_eq(&0.5)); 16 assert!(function.at(1.0).approx_eq(&1.0)); 17 } 18 19 #[test] 20 fn linear_keyword_equivalent_out_of_bounds() { 21 let function = get_linear_keyword_equivalent(); 22 assert!(function.at(-0.1).approx_eq(&-0.1)); 23 assert!(function.at(1.1).approx_eq(&1.1)); 24 } 25 26 fn get_const_function() -> PiecewiseLinearFunction { 27 let mut builder = PiecewiseLinearFunctionBuilder::default(); 28 builder.push(CONST_VALUE as f32, None); 29 builder.build() 30 } 31 32 const CONST_VALUE: f32 = 0.2; 33 34 #[test] 35 fn const_function() { 36 let function = get_const_function(); 37 assert!(function.at(0.).approx_eq(&CONST_VALUE)); 38 assert!(function.at(0.5).approx_eq(&CONST_VALUE)); 39 assert!(function.at(1.0).approx_eq(&CONST_VALUE)); 40 } 41 42 #[test] 43 fn const_function_out_of_bounds() { 44 let function = get_const_function(); 45 assert!(function.at(-0.1).approx_eq(&CONST_VALUE)); 46 assert!(function.at(1.1).approx_eq(&CONST_VALUE)); 47 } 48 49 #[test] 50 fn implied_input_spacing() { 51 let explicit_spacing = { 52 let mut builder = PiecewiseLinearFunctionBuilder::default(); 53 builder.push(0.0, Some(0.0)); 54 builder.push(1.0, Some(1.0)); 55 builder.build() 56 }; 57 let implied_spacing = { 58 let mut builder = PiecewiseLinearFunctionBuilder::default(); 59 builder.push(0.0, None); 60 builder.push(1.0, None); 61 builder.build() 62 }; 63 64 assert!(implied_spacing.at(0.).approx_eq(&explicit_spacing.at(0.))); 65 assert!(implied_spacing.at(0.5).approx_eq(&explicit_spacing.at(0.5))); 66 assert!(implied_spacing.at(1.0).approx_eq(&explicit_spacing.at(1.0))); 67 } 68 69 #[test] 70 fn interpolation() { 71 let interpolate = { 72 let mut builder = PiecewiseLinearFunctionBuilder::default(); 73 builder.push(0.0, None); 74 builder.push(0.7, None); 75 builder.push(1.0, None); 76 builder.build() 77 }; 78 assert!(interpolate.at(0.1).approx_eq(&0.14)); 79 assert!(interpolate.at(0.25).approx_eq(&0.35)); 80 assert!(interpolate.at(0.45).approx_eq(&0.63)); 81 assert!(interpolate.at(0.7).approx_eq(&0.82)); 82 assert!(interpolate.at(0.75).approx_eq(&0.85)); 83 assert!(interpolate.at(0.95).approx_eq(&0.97)); 84 } 85 86 #[test] 87 fn implied_multiple_input_spacing() { 88 let multiple_implied = { 89 let mut builder = PiecewiseLinearFunctionBuilder::default(); 90 builder.push(0.0, None); 91 builder.push(0.8, None); 92 builder.push(0.6, None); 93 builder.push(0.4, None); 94 builder.push(0.5, Some(0.4)); 95 builder.push(0.1, None); 96 builder.push(0.9, None); 97 builder.push(1.0, None); 98 builder.build() 99 }; 100 assert!(multiple_implied.at(0.1).approx_eq(&0.8)); 101 assert!(multiple_implied.at(0.2).approx_eq(&0.6)); 102 assert!(multiple_implied.at(0.3).approx_eq(&0.4)); 103 assert!(multiple_implied.at(0.4).approx_eq(&0.5)); 104 assert!(multiple_implied.at(0.6).approx_eq(&0.1)); 105 assert!(multiple_implied.at(0.8).approx_eq(&0.9)); 106 assert!(multiple_implied.at(1.).approx_eq(&1.)); 107 } 108 109 #[test] 110 fn nonzero_edge_values() { 111 let nonzero_edges = { 112 let mut builder = PiecewiseLinearFunctionBuilder::default(); 113 builder.push(0.1, Some(0.0)); 114 builder.push(0.7, Some(1.0)); 115 builder.build() 116 }; 117 assert!(nonzero_edges.at(0.).approx_eq(&0.1)); 118 assert!(nonzero_edges.at(0.5).approx_eq(&0.4)); 119 assert!(nonzero_edges.at(1.0).approx_eq(&0.7)); 120 } 121 122 #[test] 123 fn out_of_bounds_extrapolate() { 124 // General case: extrapolate from the edges' slope 125 let oob_extend = { 126 let mut builder = PiecewiseLinearFunctionBuilder::default(); 127 builder.push(0.0, None); 128 builder.push(0.7, None); 129 builder.push(1.0, None); 130 builder.build() 131 }; 132 assert!(oob_extend.at(-0.25).approx_eq(&-0.35)); 133 assert!(oob_extend.at(1.25).approx_eq(&1.15)); 134 } 135 136 #[test] 137 fn out_of_bounds_flat() { 138 // Repeated endpoints: flat extrapolation out-of-bounds 139 let oob_flat = { 140 let mut builder = PiecewiseLinearFunctionBuilder::default(); 141 builder.push(0.0, Some(0.0)); 142 builder.push(0.0, Some(0.0)); 143 builder.push(0.7, None); 144 builder.push(1.0, Some(1.0)); 145 builder.push(1.0, Some(1.0)); 146 builder.build() 147 }; 148 assert!(oob_flat.at(0.0).approx_eq(&oob_flat.at(-0.25))); 149 assert!(oob_flat.at(1.0).approx_eq(&oob_flat.at(1.25))); 150 } 151 152 #[test] 153 fn flat_region() { 154 let flat = { 155 let mut builder = PiecewiseLinearFunctionBuilder::default(); 156 builder.push(0.0, Some(0.0)); 157 builder.push(0.5, Some(0.25)); 158 builder.push(0.5, Some(0.7)); 159 builder.push(1.0, Some(1.0)); 160 builder.build() 161 }; 162 assert!(flat.at(0.125).approx_eq(&0.25)); 163 assert!(flat.at(0.5).approx_eq(&0.5)); 164 assert!(flat.at(0.85).approx_eq(&0.75)); 165 } 166 167 #[test] 168 fn step() { 169 let step = { 170 let mut builder = PiecewiseLinearFunctionBuilder::default(); 171 builder.push(0.0, Some(0.0)); 172 builder.push(0.0, Some(0.5)); 173 builder.push(1.0, Some(0.5)); 174 builder.push(1.0, Some(1.0)); 175 builder.build() 176 }; 177 assert!(step.at(0.25).approx_eq(&0.0)); 178 // At the discontinuity, take the right hand side value 179 assert!(step.at(0.5).approx_eq(&1.0)); 180 assert!(step.at(0.75).approx_eq(&1.0)); 181 } 182 183 #[test] 184 fn step_multiple_conflicting() { 185 let step = { 186 let mut builder = PiecewiseLinearFunctionBuilder::default(); 187 builder.push(0.0, Some(0.0)); 188 builder.push(0.0, Some(0.5)); 189 builder.push(0.75, Some(0.5)); 190 builder.push(0.75, Some(0.5)); 191 builder.push(1.0, Some(0.5)); 192 builder.push(1.0, Some(1.0)); 193 builder.build() 194 }; 195 assert!(step.at(0.25).approx_eq(&0.0)); 196 assert!(step.at(0.5).approx_eq(&1.0)); 197 assert!(step.at(0.75).approx_eq(&1.0)); 198 } 199 200 #[test] 201 fn always_monotonic() { 202 let monotonic = { 203 let mut builder = PiecewiseLinearFunctionBuilder::default(); 204 builder.push(0.0, Some(0.0)); 205 builder.push(0.3, Some(0.5)); 206 builder.push(0.4, Some(0.4)); 207 builder.push(1.0, Some(1.0)); 208 builder.build() 209 }; 210 assert!(monotonic.at(0.25).approx_eq(&0.15)); 211 // A discontinuity at x = 0.5 from y = 0.3 to 0.4 212 assert!(monotonic.at(0.5).approx_eq(&0.4)); 213 assert!(monotonic.at(0.6).approx_eq(&0.52)); 214 } 215 216 #[test] 217 fn always_monotonic_flat() { 218 let monotonic = { 219 let mut builder = PiecewiseLinearFunctionBuilder::default(); 220 builder.push(0.0, Some(0.0)); 221 builder.push(0.2, Some(0.2)); 222 builder.push(0.4, Some(0.1)); 223 builder.push(0.4, Some(0.15)); 224 builder.push(1.0, Some(1.0)); 225 builder.build() 226 }; 227 assert!(monotonic.at(0.2).approx_eq(&0.4)); 228 // A discontinuity at x = 0.2 from y = 0.2 to 0.4 229 assert!(monotonic.at(0.3).approx_eq(&0.475)); 230 } 231 232 #[test] 233 fn always_monotonic_flat_backwards() { 234 let monotonic = { 235 let mut builder = PiecewiseLinearFunctionBuilder::default(); 236 builder.push(0.0, Some(0.0)); 237 builder.push(0.2, Some(0.2)); 238 builder.push(0.3, Some(0.3)); 239 builder.push(0.3, Some(0.2)); 240 builder.push(1.0, Some(1.0)); 241 builder.build() 242 }; 243 assert!(monotonic.at(0.2).approx_eq(&0.2)); 244 assert!(monotonic.at(0.3).approx_eq(&0.3)); 245 assert!(monotonic.at(0.4).approx_eq(&0.4)); 246 } 247 248 #[test] 249 fn input_out_of_bounds() { 250 let oob = { 251 let mut builder = PiecewiseLinearFunctionBuilder::default(); 252 builder.push(0.0, Some(-0.5)); 253 builder.push(1.0, Some(1.5)); 254 builder.build() 255 }; 256 assert!(oob.at(-0.5).approx_eq(&0.0)); 257 assert!(oob.at(0.0).approx_eq(&0.25)); 258 assert!(oob.at(0.5).approx_eq(&0.5)); 259 assert!(oob.at(1.0).approx_eq(&0.75)); 260 assert!(oob.at(1.5).approx_eq(&1.0)); 261 } 262 263 #[test] 264 fn invalid_builder_input() { 265 let built_from_invalid = { 266 let mut builder = PiecewiseLinearFunctionBuilder::default(); 267 builder.push(0.0, Some(f32::NEG_INFINITY)); 268 builder.push(0.7, Some(f32::NAN)); 269 builder.push(1.0, Some(f32::INFINITY)); 270 builder.build() 271 }; 272 let equivalent = { 273 let mut builder = PiecewiseLinearFunctionBuilder::default(); 274 builder.push(0.0, None); 275 builder.push(0.7, None); 276 builder.push(1.0, None); 277 builder.build() 278 }; 279 280 assert!(built_from_invalid.at(0.0).approx_eq(&equivalent.at(0.0))); 281 assert!(built_from_invalid.at(0.25).approx_eq(&equivalent.at(0.25))); 282 assert!(built_from_invalid.at(0.5).approx_eq(&equivalent.at(0.5))); 283 assert!(built_from_invalid.at(0.75).approx_eq(&equivalent.at(0.75))); 284 assert!(built_from_invalid.at(1.0).approx_eq(&equivalent.at(1.0))); 285 } 286 287 #[test] 288 fn input_domain_not_complete() { 289 let not_covered = { 290 let mut builder = PiecewiseLinearFunctionBuilder::default(); 291 builder.push(0.2, Some(0.2)); 292 builder.push(0.8, Some(0.8)); 293 builder.build() 294 }; 295 assert!(not_covered.at(0.0).approx_eq(&0.0)); 296 assert!(not_covered.at(0.5).approx_eq(&0.5)); 297 assert!(not_covered.at(1.0).approx_eq(&1.0)); 298 } 299 300 #[test] 301 fn input_second_negative() { 302 let function = { 303 let mut builder = PiecewiseLinearFunctionBuilder::default(); 304 builder.push(0.0, None); 305 builder.push(0.0, Some(-0.1)); 306 builder.push(0.3, Some(-0.05)); 307 builder.push(0.5, None); 308 builder.push(0.2, Some(0.6)); 309 builder.push(1.0, None); 310 builder.build() 311 }; 312 let equivalent = { 313 let mut builder = PiecewiseLinearFunctionBuilder::default(); 314 builder.push(0.0, Some(0.0)); 315 builder.push(0.0, Some(0.0)); 316 builder.push(0.3, Some(0.0)); 317 builder.push(0.5, Some(0.3)); 318 builder.push(0.2, Some(0.6)); 319 builder.push(1.0, Some(1.0)); 320 builder.build() 321 }; 322 assert!(function.at(-0.1).approx_eq(&equivalent.at(-0.1))); 323 assert!(function.at(0.0).approx_eq(&equivalent.at(0.0))); 324 assert!(function.at(0.3).approx_eq(&equivalent.at(0.3))); 325 assert!(function.at(0.6).approx_eq(&equivalent.at(0.6))); 326 assert!(function.at(1.0).approx_eq(&equivalent.at(1.0))); 327 } 328 329 #[test] 330 fn input_second_last_above_1() { 331 let function = { 332 let mut builder = PiecewiseLinearFunctionBuilder::default(); 333 builder.push(0.0, Some(0.0)); 334 builder.push(1.0, Some(2.0)); 335 builder.push(1.0, None); 336 builder.build() 337 }; 338 assert!(function.at(-0.5).approx_eq(&-0.25)); 339 assert!(function.at(0.0).approx_eq(&0.0)); 340 assert!(function.at(0.5).approx_eq(&0.25)); 341 assert!(function.at(1.0).approx_eq(&0.5)); 342 assert!(function.at(1.5).approx_eq(&0.75)); 343 assert!(function.at(2.0).approx_eq(&1.0)); 344 assert!(function.at(3.0).approx_eq(&1.0)); 345 }