diff --git a/CHANGELOG.md b/CHANGELOG.md index 7ff77298..fc125acd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -81,6 +81,7 @@ - Add `Int.range` and `Int.rangeWithOptions`, https://github.com/rescript-association/rescript-core/pull/52 - Remove `Array.fromIterator` and `Array.fromIteratorWithMap`. The same functions exist in `Iterator` as `Iterator.fromArray` and `Iterator.fromArrayWithMapper`. https://github.com/rescript-association/rescript-core/pull/78 - Remove unsafe `Array.from` and `Array.fromWithMap`. https://github.com/rescript-association/rescript-core/pull/78 +- Add `Int.clamp` and `Float.clamp`, https://github.com/rescript-association/rescript-core/pull/90 ### Documentation diff --git a/src/Core__Float.mjs b/src/Core__Float.mjs index 6dd2171c..44a3d032 100644 --- a/src/Core__Float.mjs +++ b/src/Core__Float.mjs @@ -26,10 +26,20 @@ function fromString(i) { } } +function clamp(min, max, value) { + var value$1 = max !== undefined && max < value ? max : value; + if (min !== undefined && min > value$1) { + return min; + } else { + return value$1; + } +} + export { Constants , equal , compare , fromString , + clamp , } /* No side effect */ diff --git a/src/Core__Float.res b/src/Core__Float.res index 4332faee..413d070d 100644 --- a/src/Core__Float.res +++ b/src/Core__Float.res @@ -42,3 +42,14 @@ external toInt: float => int = "%intoffloat" external fromInt: int => float = "%identity" @unboxed @noalloc external mod: (float, float) => float = "?fmod_float" + +let clamp = (~min=?, ~max=?, value): float => { + let value = switch max { + | Some(max) if max < value => max + | _ => value + } + switch min { + | Some(min) if min > value => min + | _ => value + } +} diff --git a/src/Core__Float.resi b/src/Core__Float.resi index d77a1110..82bc5fcd 100644 --- a/src/Core__Float.resi +++ b/src/Core__Float.resi @@ -408,3 +408,19 @@ Int.mod(7.0, 4.0) == 3 ``` */ external mod: (float, float) => float = "?fmod_float" + +/** +`clamp(~min=?, ~max=?, value)` returns `value`, optionally bounded by `min` and `max`. + +if `max` < `min` returns `min`. + +## Examples + +```rescript +Int.clamp(4.2) == 4.2 +Int.clamp(4.2, ~min=4.3) == 4.3 +Int.clamp(4.2, ~max=4.1) == 4.1 +Int.clamp(4.2, ~min=4.3, ~max=4.1) == 4.3 +``` +*/ +let clamp: (~min: float=?, ~max: float=?, float) => float diff --git a/src/Core__Int.mjs b/src/Core__Int.mjs index d96a042c..c870441b 100644 --- a/src/Core__Int.mjs +++ b/src/Core__Int.mjs @@ -61,6 +61,15 @@ function range(start, end) { return rangeWithOptions(start, end, {}); } +function clamp(min, max, value) { + var value$1 = max !== undefined && max < value ? max : value; + if (min !== undefined && min > value$1) { + return min; + } else { + return value$1; + } +} + var Constants = { minValue: -2147483648, maxValue: 2147483647 @@ -73,5 +82,6 @@ export { fromString , range , rangeWithOptions , + clamp , } /* No side effect */ diff --git a/src/Core__Int.res b/src/Core__Int.res index 743aaf64..bd107749 100644 --- a/src/Core__Int.res +++ b/src/Core__Int.res @@ -66,3 +66,14 @@ let rangeWithOptions = (start, end, options) => { } let range = (start, end) => rangeWithOptions(start, end, {}) + +let clamp = (~min=?, ~max=?, value): int => { + let value = switch max { + | Some(max) if max < value => max + | _ => value + } + switch min { + | Some(min) if min > value => min + | _ => value + } +} diff --git a/src/Core__Int.resi b/src/Core__Int.resi index 6c182c85..44729e1b 100644 --- a/src/Core__Int.resi +++ b/src/Core__Int.resi @@ -326,3 +326,19 @@ Int.rangeWithOptions(3, 6, {step: -2}) // RangeError - Raises `RangeError` if `step == 0 && start != end`. */ let rangeWithOptions: (int, int, rangeOptions) => array + +/** +`clamp(~min=?, ~max=?, value)` returns `value`, optionally bounded by `min` and `max`. + +if `max` < `min` returns `min`. + +## Examples + +```rescript +Int.clamp(42) == 42 +Int.clamp(42, ~min=50) == 50 +Int.clamp(42, ~max=40) == 40 +Int.clamp(42, ~min=50, ~max=40) == 50 +``` +*/ +let clamp: (~min: int=?, ~max: int=?, int) => int diff --git a/test/FloatTests.mjs b/test/FloatTests.mjs new file mode 100644 index 00000000..31bdae36 --- /dev/null +++ b/test/FloatTests.mjs @@ -0,0 +1,243 @@ +// Generated by ReScript, PLEASE EDIT WITH CARE + +import * as Test from "./Test.mjs"; +import * as Caml_obj from "rescript/lib/es6/caml_obj.js"; +import * as Pervasives from "rescript/lib/es6/pervasives.js"; +import * as Core__Float from "../src/Core__Float.mjs"; + +var eq = Caml_obj.equal; + +Test.run([ + [ + "FloatTests.res", + 5, + 20, + 27 + ], + "clamp" + ], Core__Float.clamp(undefined, undefined, 4.2), eq, 4.2); + +Test.run([ + [ + "FloatTests.res", + 6, + 20, + 35 + ], + "clamp - < min" + ], Core__Float.clamp(4.3, undefined, 4.1), eq, 4.3); + +Test.run([ + [ + "FloatTests.res", + 7, + 20, + 35 + ], + "clamp - > min" + ], Core__Float.clamp(4.1, undefined, 4.2), eq, 4.2); + +Test.run([ + [ + "FloatTests.res", + 8, + 20, + 35 + ], + "clamp - < max" + ], Core__Float.clamp(undefined, 4.3, 4.2), eq, 4.2); + +Test.run([ + [ + "FloatTests.res", + 9, + 20, + 35 + ], + "clamp - > max" + ], Core__Float.clamp(undefined, 4.1, 4.2), eq, 4.1); + +Test.run([ + [ + "FloatTests.res", + 10, + 20, + 42 + ], + "clamp - < min, < max" + ], Core__Float.clamp(4.3, 4.5, 4.2), eq, 4.3); + +Test.run([ + [ + "FloatTests.res", + 11, + 20, + 42 + ], + "clamp - < min, > max" + ], Core__Float.clamp(4.3, 4.1, 4.2), eq, 4.3); + +Test.run([ + [ + "FloatTests.res", + 12, + 20, + 42 + ], + "clamp - > min, < max" + ], Core__Float.clamp(4.1, 4.5, 4.2), eq, 4.2); + +Test.run([ + [ + "FloatTests.res", + 13, + 20, + 42 + ], + "clamp - > min, > max" + ], Core__Float.clamp(4.1, 4.1, 4.2), eq, 4.1); + +Test.run([ + [ + "FloatTests.res", + 14, + 20, + 33 + ], + "clamp - nan" + ], isNaN(Core__Float.clamp(4.1, 4.3, Number.NaN)), eq, true); + +Test.run([ + [ + "FloatTests.res", + 15, + 20, + 38 + ], + "clamp - infinity" + ], Core__Float.clamp(4.1, 4.3, Pervasives.infinity), eq, 4.3); + +Test.run([ + [ + "FloatTests.res", + 16, + 20, + 39 + ], + "clamp - -infinity" + ], Core__Float.clamp(4.1, 4.3, Pervasives.neg_infinity), eq, 4.1); + +Test.run([ + [ + "FloatTests.res", + 17, + 20, + 37 + ], + "clamp - min nan" + ], Core__Float.clamp(Number.NaN, undefined, 4.2), eq, 4.2); + +Test.run([ + [ + "FloatTests.res", + 18, + 20, + 37 + ], + "clamp - max nan" + ], Core__Float.clamp(undefined, Number.NaN, 4.2), eq, 4.2); + +Test.run([ + [ + "FloatTests.res", + 19, + 20, + 46 + ], + "clamp - min nan, max nan" + ], Core__Float.clamp(Number.NaN, Number.NaN, 4.2), eq, 4.2); + +Test.run([ + [ + "FloatTests.res", + 20, + 20, + 42 + ], + "clamp - min infinity" + ], Core__Float.clamp(Pervasives.infinity, undefined, 4.2), eq, Pervasives.infinity); + +Test.run([ + [ + "FloatTests.res", + 21, + 20, + 42 + ], + "clamp - max infinity" + ], Core__Float.clamp(undefined, Pervasives.infinity, 4.2), eq, 4.2); + +Test.run([ + [ + "FloatTests.res", + 22, + 20, + 43 + ], + "clamp - min -infinity" + ], Core__Float.clamp(Pervasives.neg_infinity, undefined, 4.2), eq, 4.2); + +Test.run([ + [ + "FloatTests.res", + 23, + 20, + 43 + ], + "clamp - max -infinity" + ], Core__Float.clamp(undefined, Pervasives.neg_infinity, 4.2), eq, Pervasives.neg_infinity); + +Test.run([ + [ + "FloatTests.res", + 25, + 13, + 49 + ], + "clamp - min infinity, max infinity" + ], Core__Float.clamp(Pervasives.infinity, Pervasives.infinity, 4.2), eq, Pervasives.infinity); + +Test.run([ + [ + "FloatTests.res", + 31, + 13, + 50 + ], + "clamp - min -infinity, max infinity" + ], Core__Float.clamp(Pervasives.neg_infinity, Pervasives.infinity, 4.2), eq, 4.2); + +Test.run([ + [ + "FloatTests.res", + 37, + 13, + 50 + ], + "clamp - min infinity, max -infinity" + ], Core__Float.clamp(Pervasives.infinity, Pervasives.neg_infinity, 4.2), eq, Pervasives.infinity); + +Test.run([ + [ + "FloatTests.res", + 43, + 13, + 51 + ], + "clamp - min -infinity, max -infinity" + ], Core__Float.clamp(Pervasives.neg_infinity, Pervasives.neg_infinity, 4.2), eq, Pervasives.neg_infinity); + +export { + eq , +} +/* Not a pure module */ diff --git a/test/FloatTests.res b/test/FloatTests.res new file mode 100644 index 00000000..c9f933ca --- /dev/null +++ b/test/FloatTests.res @@ -0,0 +1,47 @@ +open RescriptCore + +let eq = (a, b) => a == b + +Test.run(__POS_OF__("clamp"), Float.clamp(4.2), eq, 4.2) +Test.run(__POS_OF__("clamp - < min"), Float.clamp(~min=4.3, 4.1), eq, 4.3) +Test.run(__POS_OF__("clamp - > min"), Float.clamp(~min=4.1, 4.2), eq, 4.2) +Test.run(__POS_OF__("clamp - < max"), Float.clamp(~max=4.3, 4.2), eq, 4.2) +Test.run(__POS_OF__("clamp - > max"), Float.clamp(~max=4.1, 4.2), eq, 4.1) +Test.run(__POS_OF__("clamp - < min, < max"), Float.clamp(~min=4.3, ~max=4.5, 4.2), eq, 4.3) +Test.run(__POS_OF__("clamp - < min, > max"), Float.clamp(~min=4.3, ~max=4.1, 4.2), eq, 4.3) // min wins +Test.run(__POS_OF__("clamp - > min, < max"), Float.clamp(~min=4.1, ~max=4.5, 4.2), eq, 4.2) +Test.run(__POS_OF__("clamp - > min, > max"), Float.clamp(~min=4.1, ~max=4.1, 4.2), eq, 4.1) +Test.run(__POS_OF__("clamp - nan"), Float.clamp(~min=4.1, ~max=4.3, nan)->Float.isNaN, eq, true) +Test.run(__POS_OF__("clamp - infinity"), Float.clamp(~min=4.1, ~max=4.3, infinity), eq, 4.3) +Test.run(__POS_OF__("clamp - -infinity"), Float.clamp(~min=4.1, ~max=4.3, neg_infinity), eq, 4.1) +Test.run(__POS_OF__("clamp - min nan"), Float.clamp(~min=nan, 4.2), eq, 4.2) +Test.run(__POS_OF__("clamp - max nan"), Float.clamp(~max=nan, 4.2), eq, 4.2) +Test.run(__POS_OF__("clamp - min nan, max nan"), Float.clamp(~min=nan, ~max=nan, 4.2), eq, 4.2) +Test.run(__POS_OF__("clamp - min infinity"), Float.clamp(~min=infinity, 4.2), eq, infinity) +Test.run(__POS_OF__("clamp - max infinity"), Float.clamp(~max=infinity, 4.2), eq, 4.2) +Test.run(__POS_OF__("clamp - min -infinity"), Float.clamp(~min=neg_infinity, 4.2), eq, 4.2) +Test.run(__POS_OF__("clamp - max -infinity"), Float.clamp(~max=neg_infinity, 4.2), eq, neg_infinity) +Test.run( + __POS_OF__("clamp - min infinity, max infinity"), + Float.clamp(~min=infinity, ~max=infinity, 4.2), + eq, + infinity, +) +Test.run( + __POS_OF__("clamp - min -infinity, max infinity"), + Float.clamp(~min=neg_infinity, ~max=infinity, 4.2), + eq, + 4.2, +) +Test.run( + __POS_OF__("clamp - min infinity, max -infinity"), + Float.clamp(~min=infinity, ~max=neg_infinity, 4.2), + eq, + infinity, // min wins +) +Test.run( + __POS_OF__("clamp - min -infinity, max -infinity"), + Float.clamp(~min=neg_infinity, ~max=neg_infinity, 4.2), + eq, + neg_infinity, +) diff --git a/test/IntTests.mjs b/test/IntTests.mjs index 30daefe0..794b8ca3 100644 --- a/test/IntTests.mjs +++ b/test/IntTests.mjs @@ -459,6 +459,96 @@ Test.run([ -5 ]); +Test.run([ + [ + "IntTests.res", + 165, + 20, + 27 + ], + "clamp" + ], Core__Int.clamp(undefined, undefined, 42), eq, 42); + +Test.run([ + [ + "IntTests.res", + 166, + 20, + 35 + ], + "clamp - < min" + ], Core__Int.clamp(50, undefined, 42), eq, 50); + +Test.run([ + [ + "IntTests.res", + 167, + 20, + 35 + ], + "clamp - > min" + ], Core__Int.clamp(40, undefined, 42), eq, 42); + +Test.run([ + [ + "IntTests.res", + 168, + 20, + 35 + ], + "clamp - < max" + ], Core__Int.clamp(undefined, 50, 42), eq, 42); + +Test.run([ + [ + "IntTests.res", + 169, + 20, + 35 + ], + "clamp - > max" + ], Core__Int.clamp(undefined, 40, 42), eq, 40); + +Test.run([ + [ + "IntTests.res", + 170, + 20, + 42 + ], + "clamp - < min, < max" + ], Core__Int.clamp(50, 60, 42), eq, 50); + +Test.run([ + [ + "IntTests.res", + 171, + 20, + 42 + ], + "clamp - < min, > max" + ], Core__Int.clamp(50, 40, 42), eq, 50); + +Test.run([ + [ + "IntTests.res", + 172, + 20, + 42 + ], + "clamp - > min, < max" + ], Core__Int.clamp(40, 60, 42), eq, 42); + +Test.run([ + [ + "IntTests.res", + 173, + 20, + 42 + ], + "clamp - > min, > max" + ], Core__Int.clamp(40, 40, 42), eq, 40); + export { eq , $$catch , diff --git a/test/IntTests.res b/test/IntTests.res index 373cf8c9..9467ad32 100644 --- a/test/IntTests.res +++ b/test/IntTests.res @@ -161,3 +161,13 @@ Test.run( eq, [-3, -5], ) + +Test.run(__POS_OF__("clamp"), Int.clamp(42), eq, 42) +Test.run(__POS_OF__("clamp - < min"), Int.clamp(~min=50, 42), eq, 50) +Test.run(__POS_OF__("clamp - > min"), Int.clamp(~min=40, 42), eq, 42) +Test.run(__POS_OF__("clamp - < max"), Int.clamp(~max=50, 42), eq, 42) +Test.run(__POS_OF__("clamp - > max"), Int.clamp(~max=40, 42), eq, 40) +Test.run(__POS_OF__("clamp - < min, < max"), Int.clamp(~min=50, ~max=60, 42), eq, 50) +Test.run(__POS_OF__("clamp - < min, > max"), Int.clamp(~min=50, ~max=40, 42), eq, 50) // min wins +Test.run(__POS_OF__("clamp - > min, < max"), Int.clamp(~min=40, ~max=60, 42), eq, 42) +Test.run(__POS_OF__("clamp - > min, > max"), Int.clamp(~min=40, ~max=40, 42), eq, 40) diff --git a/test/TestSuite.mjs b/test/TestSuite.mjs index 3003697c..f8005b37 100644 --- a/test/TestSuite.mjs +++ b/test/TestSuite.mjs @@ -4,6 +4,7 @@ import * as IntTests from "./IntTests.mjs"; import * as TestTests from "./TestTests.mjs"; import * as ArrayTests from "./ArrayTests.mjs"; import * as ErrorTests from "./ErrorTests.mjs"; +import * as FloatTests from "./FloatTests.mjs"; import * as ObjectTests from "./ObjectTests.mjs"; import * as PromiseTest from "./PromiseTest.mjs"; import * as ResultTests from "./ResultTests.mjs"; @@ -49,8 +50,6 @@ var forEachIfOkCallFunction = ResultTests.forEachIfOkCallFunction; var forEachIfErrorDoNotCallFunction = ResultTests.forEachIfErrorDoNotCallFunction; -var eq = TypedArrayTests.eq; - var num1 = TypedArrayTests.num1; var num2 = TypedArrayTests.num2; @@ -65,6 +64,8 @@ var areSame = TypedArrayTests.areSame; var o = TypedArrayTests.o; +var eq = FloatTests.eq; + export { bign , TestError , @@ -86,7 +87,6 @@ export { getSymbolTestWhenExists , forEachIfOkCallFunction , forEachIfErrorDoNotCallFunction , - eq , num1 , num2 , num3 , @@ -94,5 +94,6 @@ export { assertWillThrow , areSame , o , + eq , } /* IntTests Not a pure module */ diff --git a/test/TestSuite.res b/test/TestSuite.res index 0c963bc9..62e51b02 100644 --- a/test/TestSuite.res +++ b/test/TestSuite.res @@ -6,3 +6,4 @@ include IntTests include ObjectTests include ResultTests include TypedArrayTests +include FloatTests