Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added NotNaN predicate #596

Merged
merged 3 commits into from
Dec 13, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -369,6 +369,7 @@ The library comes with these predefined predicates:
* `NonDivisible[N]`: checks if an integral value is not evenly divisible by `N`
* `Even`: checks if an integral value is evenly divisible by 2
* `Odd`: checks if an integral value is not evenly divisible by 2
* `NonNaN`: checks if a floating-point number is not NaN

[`string`](https://github.com/fthomas/refined/blob/master/modules/core/shared/src/main/scala/eu/timepit/refined/string.scala)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,9 @@ object numeric extends NumericInference {
/** Predicate that checks if an integral value modulo `N` is `O`. */
final case class Modulo[N, O](n: N, o: O)

/** Predicate that checks if a floating-point number value is not NaN. */
final case class NonNaN()

/** Predicate that checks if a numeric value is less than or equal to `N`. */
type LessEqual[N] = Not[Greater[N]]

Expand Down Expand Up @@ -117,6 +120,14 @@ object numeric extends NumericInference {
Modulo(wn.fst, wo.fst)
)
}

object NonNaN {
implicit def floatNonNaNValidate: Validate.Plain[Float, NonNaN] = fromIsNaN(_.isNaN)
implicit def doubleNonNaNValidate: Validate.Plain[Double, NonNaN] = fromIsNaN(_.isNaN)

def fromIsNaN[A](isNaN: A => Boolean): Validate.Plain[A, NonNaN] =
Validate.fromPredicate(x => !isNaN(x), x => s"($x != NaN)", NonNaN())
}
}

private[refined] trait NumericInference {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
package eu.timepit.refined.predicates

object all extends AllPredicates with AllPredicatesBinCompat1
object all extends AllPredicates with AllPredicatesBinCompat1 with AllPredicatesBinCompat2

trait AllPredicates
extends BooleanPredicates
Expand All @@ -11,3 +11,5 @@ trait AllPredicates
with StringPredicates

trait AllPredicatesBinCompat1 extends StringPredicatesBinCompat1

trait AllPredicatesBinCompat2 extends NumericPredicatesBinCompat1
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ package eu.timepit.refined.predicates

import eu.timepit.refined

object numeric extends NumericPredicates
object numeric extends NumericPredicates with NumericPredicatesBinCompat1

trait NumericPredicates {
final type Less[N] = refined.numeric.Less[N]
Expand Down Expand Up @@ -36,3 +36,8 @@ trait NumericPredicates {

final val Interval = refined.numeric.Interval
}

trait NumericPredicatesBinCompat1 {
final type NonNaN = refined.numeric.NonNaN
final val NonNaN = refined.numeric.NonNaN
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package eu.timepit.refined.types

/** Module for all predefined refined types. */
object all extends AllTypes with AllTypesBinCompat1
object all extends AllTypes with AllTypesBinCompat1 with AllTypesBinCompat2

trait AllTypes
extends CharTypes
Expand All @@ -12,3 +12,5 @@ trait AllTypes
with TimeTypes

trait AllTypesBinCompat1 extends NumericTypesBinCompat1

trait AllTypesBinCompat2 extends NumericTypesBinCompat2
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package eu.timepit.refined.types

import eu.timepit.refined.api.{Refined, RefinedTypeOps}
import eu.timepit.refined.numeric.{Negative, NonNegative, NonPositive, Positive}
import eu.timepit.refined.numeric.{Negative, NonNaN, NonNegative, NonPositive, Positive}

/** Module for numeric refined types. */
object numeric {
Expand Down Expand Up @@ -165,6 +165,16 @@ object numeric {
type NonPosBigDecimal = BigDecimal Refined NonPositive

object NonPosBigDecimal extends RefinedTypeOps[NonPosBigDecimal, BigDecimal]

/** A `Float` that is not NaN. */
type NonNaNFloat = Float Refined NonNaN

object NonNaNFloat extends RefinedTypeOps[NonNaNFloat, Float]

/** A `Double` that is not NaN. */
type NonNaNDouble = Double Refined NonNaN

object NonNaNDouble extends RefinedTypeOps[NonNaNDouble, Double]
}

trait NumericTypes {
Expand Down Expand Up @@ -266,3 +276,11 @@ trait NumericTypesBinCompat1 {
final type NonPosBigDecimal = numeric.NonPosBigDecimal
final val NonPosBigDecimal = numeric.NonPosBigDecimal
}

trait NumericTypesBinCompat2 {
final type NonNaNFloat = numeric.NonNaNFloat
final val NonNaNFloat = numeric.NonNaNFloat

final type NonNaNDouble = numeric.NonNaNDouble
final val NonNaNDouble = numeric.NonNaNDouble
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@ package eu.timepit.refined

import eu.timepit.refined.TestUtils._
import eu.timepit.refined.numeric._
import org.scalacheck.{Arbitrary, Gen, Properties}
import org.scalacheck.Prop._
import org.scalacheck.Properties
import shapeless.nat._

class NumericValidateSpec extends Properties("NumericValidate") {
Expand Down Expand Up @@ -179,4 +179,24 @@ class NumericValidateSpec extends Properties("NumericValidate") {
val s = showExpr[Interval.Closed[_0, _1]](0.5)
(s ?= "(!(0.5 < 0) && !(0.5 > 1))") || (s ?= "(!(0.5 < 0.0) && !(0.5 > 1.0))")
}

val floatWithNaN: Gen[Float] = Gen.frequency(8 -> Arbitrary.arbitrary[Float], 2 -> Float.NaN)
val doubleWithNaN: Gen[Double] = Gen.frequency(8 -> Arbitrary.arbitrary[Double], 2 -> Double.NaN)

property("NonNaN.Float.isValid") = forAll(floatWithNaN) { (d: Float) =>
isValid[NonNaN](d) ?= !d.isNaN
}

property("NonNaN.Float.showExpr") = secure {
showExpr[NonNaN](Float.NaN) ?= "(NaN != NaN)"
}

property("NonNaN.Double.isValid") = forAll(doubleWithNaN) { (d: Double) =>
isValid[NonNaN](d) ?= !d.isNaN
}

property("NonNaN.Double.showExpr") = secure {
showExpr[NonNaN](Double.NaN) ?= "(NaN != NaN)"
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ object all
with CharInstances
with GenericInstances
with NumericInstances
with NumericInstancesBinCompat1
with RefTypeInstances
with StringInstances
with StringInstancesBinCompat1
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import org.scalacheck.Gen.Choose
* Module that provides `Arbitrary` instances and generators for
* numeric predicates.
*/
object numeric extends NumericInstances
object numeric extends NumericInstances with NumericInstancesBinCompat1

trait NumericInstances {

Expand Down Expand Up @@ -111,3 +111,15 @@ trait NumericInstances {
): Arbitrary[F[T, P]] =
arbitraryRefType(Gen.chooseNum(min, max))
}

trait NumericInstancesBinCompat1 {
implicit def floatNonNaNArbitrary[F[_, _]: RefType](
implicit arb: Arbitrary[Float]
): Arbitrary[F[Float, NonNaN]] =
arbitraryRefType(arb.arbitrary.map(x => if (x.isNaN) 0.0f else x))

implicit def doubleNonNaNArbitrary[F[_, _]: RefType](
implicit arb: Arbitrary[Double]
): Arbitrary[F[Double, NonNaN]] =
arbitraryRefType(arb.arbitrary.map(x => if (x.isNaN) 0.0d else x))
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import eu.timepit.refined.W
import eu.timepit.refined.api.Refined
import eu.timepit.refined.numeric._
import eu.timepit.refined.scalacheck.numeric._
import eu.timepit.refined.types.numeric.{NegDouble, NonNegInt, NonNegLong, PosFloat}
import eu.timepit.refined.types.numeric._
import eu.timepit.refined.types.time.Minute
import org.scalacheck.Prop._
import org.scalacheck.Properties
Expand Down Expand Up @@ -36,6 +36,10 @@ class NumericArbitrarySpec extends Properties("NumericArbitrary") {

property("GreaterEqual[_10]") = checkArbitraryRefinedType[Int Refined GreaterEqual[_10]]

property("NonNaNFloat") = checkArbitraryRefinedType[NonNaNFloat]

property("NonNaNDouble") = checkArbitraryRefinedType[NonNaNDouble]

property("PosFloat") = checkArbitraryRefinedType[PosFloat]

property("NonPositive") = checkArbitraryRefinedType[Short Refined NonPositive]
Expand Down