packages feed

numhask-range 0.0.2 → 0.0.3

raw patch · 8 files changed

+745/−777 lines, 8 filessetup-changedPVP: major bump suggested

API removals or changes: PVP suggests a major version bump

API changes (from Hackage documentation)

- NumHask.Rect: rangeRects :: (Ord a, BoundedField a, Traversable f) => f (Rect a) -> Rect a
- NumHask.Rect: scaleR2s :: (R2 r, BoundedField a, Traversable f, Traversable g, Ord a) => Rect a -> g (f (r a)) -> g (f (r a))
- NumHask.Rect: scaleRects :: (BoundedField a, Traversable f, Ord a) => Rect a -> f (Rect a) -> f (Rect a)
- NumHask.Rect: scaleRectss :: (BoundedField a, Traversable f, Traversable g, Ord a) => Rect a -> g (f (Rect a)) -> g (f (Rect a))
+ NumHask.Rect: projectR2 :: (R2 r, Field a, Functor f) => Rect a -> Rect a -> f (r a) -> f (r a)
+ NumHask.Rect: rangeR2 :: (Traversable f, Ord a, BoundedField a, R2 r) => f (r a) -> Rect a

Files

LICENSE view
@@ -1,30 +1,30 @@-Copyright Tony Day (c) 2017--All rights reserved.--Redistribution and use in source and binary forms, with or without-modification, are permitted provided that the following conditions are met:--    * Redistributions of source code must retain the above copyright-      notice, this list of conditions and the following disclaimer.--    * Redistributions in binary form must reproduce the above-      copyright notice, this list of conditions and the following-      disclaimer in the documentation and/or other materials provided-      with the distribution.--    * Neither the name of Tony Day nor the names of other-      contributors may be used to endorse or promote products derived-      from this software without specific prior written permission.--THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS-"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT-LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR-A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT-OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,-SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT-LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,-DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY-THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT-(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE-OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.+Copyright Tony Day (c) 2017
+
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+    * Redistributions of source code must retain the above copyright
+      notice, this list of conditions and the following disclaimer.
+
+    * Redistributions in binary form must reproduce the above
+      copyright notice, this list of conditions and the following
+      disclaimer in the documentation and/or other materials provided
+      with the distribution.
+
+    * Neither the name of Tony Day nor the names of other
+      contributors may be used to endorse or promote products derived
+      from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
Setup.hs view
@@ -1,2 +1,2 @@-import Distribution.Simple-main = defaultMain+import Distribution.Simple
+main = defaultMain
numhask-range.cabal view
@@ -1,143 +1,143 @@-name:-  numhask-range-version:-  0.0.2-synopsis:-  see readme.md-description:-  see readme.md for description.-category:-  project-homepage:-  https://github.com/tonyday567/numhask-range-license:-  BSD3-license-file:-  LICENSE-author:-  Tony Day-maintainer:-  tonyday567@gmail.com-copyright:-  Tony Day-build-type:-  Simple-cabal-version:-  >=1.14-extra-source-files:-  stack.yaml-library-  default-language:-    Haskell2010-  ghc-options:-  hs-source-dirs:-    src-  exposed-modules:-    NumHask.Range,-    NumHask.Histogram,-    NumHask.Rect-  build-depends:-    base >= 4.7 && < 5,-    numhask >= 0.0.4,-    protolude,-    lens,-    foldl,-    containers,-    QuickCheck,-    linear,-    formatting-  default-extensions:-    NoImplicitPrelude,-    UnicodeSyntax,-    BangPatterns,-    BinaryLiterals,-    DeriveFoldable,-    DeriveFunctor,-    DeriveGeneric,-    DeriveTraversable,-    DisambiguateRecordFields,-    EmptyCase,-    FlexibleContexts,-    FlexibleInstances,-    FunctionalDependencies,-    GADTSyntax,-    InstanceSigs,-    KindSignatures,-    LambdaCase,-    MonadComprehensions,-    MultiParamTypeClasses,-    MultiWayIf,-    NegativeLiterals,-    OverloadedStrings,-    ParallelListComp,-    PartialTypeSignatures,-    PatternSynonyms,-    RankNTypes,-    RecordWildCards,-    RecursiveDo,-    ScopedTypeVariables,-    TupleSections,-    TypeFamilies,-    TypeOperators--test-suite test-  default-language:-    Haskell2010-  type:-    exitcode-stdio-1.0-  hs-source-dirs:-    test-  main-is:-    test.hs-  build-depends:-    base >= 4.7 && < 5,-    HUnit,-    QuickCheck,-    numhask-range,-    protolude,-    smallcheck,-    tasty,-    tasty-hunit,-    tasty-hspec,-    tasty-quickcheck,-    tasty-smallcheck,-    numhask >= 0.0.4-  default-extensions:-    NoImplicitPrelude,-    UnicodeSyntax,-    BangPatterns,-    BinaryLiterals,-    DeriveFoldable,-    DeriveFunctor,-    DeriveGeneric,-    DeriveTraversable,-    DisambiguateRecordFields,-    EmptyCase,-    FlexibleContexts,-    FlexibleInstances,-    FunctionalDependencies,-    GADTSyntax,-    InstanceSigs,-    KindSignatures,-    LambdaCase,-    MonadComprehensions,-    MultiParamTypeClasses,-    MultiWayIf,-    NegativeLiterals,-    OverloadedStrings,-    ParallelListComp,-    PartialTypeSignatures,-    PatternSynonyms,-    RankNTypes,-    RecordWildCards,-    RecursiveDo,-    ScopedTypeVariables,-    TupleSections,-    TypeFamilies,-    TypeOperators--source-repository head-  type:-    git-  location:-    https://github.com/tonyday567/numhask-range+name:
+  numhask-range
+version:
+  0.0.3
+synopsis:
+  Numbers that are range representations
+description:
+  Numbers that represent ranges of all sorts.
+category:
+  project
+homepage:
+  https://github.com/tonyday567/numhask-range
+license:
+  BSD3
+license-file:
+  LICENSE
+author:
+  Tony Day
+maintainer:
+  tonyday567@gmail.com
+copyright:
+  Tony Day
+build-type:
+  Simple
+cabal-version:
+  >=1.14
+extra-source-files:
+  stack.yaml
+library
+  default-language:
+    Haskell2010
+  ghc-options:
+  hs-source-dirs:
+    src
+  exposed-modules:
+    NumHask.Range,
+    NumHask.Histogram,
+    NumHask.Rect
+  build-depends:
+    base >= 4.7 && < 5,
+    numhask >= 0.0.4,
+    protolude,
+    lens,
+    foldl,
+    containers,
+    QuickCheck,
+    linear,
+    formatting
+  default-extensions:
+    NoImplicitPrelude,
+    UnicodeSyntax,
+    BangPatterns,
+    BinaryLiterals,
+    DeriveFoldable,
+    DeriveFunctor,
+    DeriveGeneric,
+    DeriveTraversable,
+    DisambiguateRecordFields,
+    EmptyCase,
+    FlexibleContexts,
+    FlexibleInstances,
+    FunctionalDependencies,
+    GADTSyntax,
+    InstanceSigs,
+    KindSignatures,
+    LambdaCase,
+    MonadComprehensions,
+    MultiParamTypeClasses,
+    MultiWayIf,
+    NegativeLiterals,
+    OverloadedStrings,
+    ParallelListComp,
+    PartialTypeSignatures,
+    PatternSynonyms,
+    RankNTypes,
+    RecordWildCards,
+    RecursiveDo,
+    ScopedTypeVariables,
+    TupleSections,
+    TypeFamilies,
+    TypeOperators
+
+test-suite test
+  default-language:
+    Haskell2010
+  type:
+    exitcode-stdio-1.0
+  hs-source-dirs:
+    test
+  main-is:
+    test.hs
+  build-depends:
+    base >= 4.7 && < 5,
+    HUnit,
+    QuickCheck,
+    numhask-range,
+    protolude,
+    smallcheck,
+    tasty,
+    tasty-hunit,
+    tasty-hspec,
+    tasty-quickcheck,
+    tasty-smallcheck,
+    numhask >= 0.0.4
+  default-extensions:
+    NoImplicitPrelude,
+    UnicodeSyntax,
+    BangPatterns,
+    BinaryLiterals,
+    DeriveFoldable,
+    DeriveFunctor,
+    DeriveGeneric,
+    DeriveTraversable,
+    DisambiguateRecordFields,
+    EmptyCase,
+    FlexibleContexts,
+    FlexibleInstances,
+    FunctionalDependencies,
+    GADTSyntax,
+    InstanceSigs,
+    KindSignatures,
+    LambdaCase,
+    MonadComprehensions,
+    MultiParamTypeClasses,
+    MultiWayIf,
+    NegativeLiterals,
+    OverloadedStrings,
+    ParallelListComp,
+    PartialTypeSignatures,
+    PatternSynonyms,
+    RankNTypes,
+    RecordWildCards,
+    RecursiveDo,
+    ScopedTypeVariables,
+    TupleSections,
+    TypeFamilies,
+    TypeOperators
+
+source-repository head
+  type:
+    git
+  location:
+    https://github.com/tonyday567/numhask-range
src/NumHask/Histogram.hs view
@@ -1,110 +1,110 @@-{-# OPTIONS_GHC -Wall #-}-{-# OPTIONS_GHC -fno-warn-type-defaults #-}-{-# OPTIONS_GHC -fno-warn-name-shadowing #-}-{-# LANGUAGE OverloadedStrings #-}--module NumHask.Histogram-  ( Histogram(..)-  , freq-  , fill-  , DealOvers(..)-  , fromHist-  , hist-  , labels-  , insert-  , insertW-  , insertWs-  ) where--import NumHask.Rect--import Protolude-import qualified Control.Foldl as L-import qualified Data.Map.Strict as Map-import Linear hiding (identity)-import Data.List-import Formatting-import Control.Lens---- a histogram-data Histogram = Histogram-   { _cuts   :: [Double] -- bucket boundaries-   , _values :: Map.Map Int Double -- bucket counts-   } deriving (Show, Eq)--freq' :: Map.Map Int Double -> Map.Map Int Double-freq' m = Map.map (* recip (Protolude.sum m)) m--freq :: Histogram -> Histogram-freq (Histogram c v) = Histogram c (freq' v)--count :: L.Fold Int (Map Int Double)-count = L.premap (\x -> (x,1.0)) countW--countW :: L.Fold (Int,Double) (Map Int Double)-countW = L.Fold (\x (a,w) -> Map.insertWith (+) a w x) Map.empty identity--countBool :: L.Fold Bool Int-countBool = L.Fold (\x a -> x + if a then 1 else 0) 0 identity--histMap :: (Functor f, Functor g, Ord a, Foldable f, Foldable g) =>-    f a -> g a -> Map Int Double-histMap cuts xs = L.fold count $ (\x -> L.fold countBool (fmap (x >) cuts)) <$> xs--histMapW :: (Functor f, Functor g, Ord a, Foldable f, Foldable g) =>-    f a -> g (a,Double) -> Map Int Double-histMapW cuts xs = L.fold countW $-    (\x -> (L.fold countBool (fmap (fst x >) cuts),snd x)) <$> xs--fill :: [Double] -> [Double] -> Histogram-fill cuts xs = Histogram cuts (histMap cuts xs)--insertW :: Histogram -> Double -> Double -> Histogram-insertW (Histogram cuts vs) value weight = Histogram cuts (Map.unionWith (+) vs s)-    where-      s = histMapW cuts [(value,weight)]--insertWs :: Histogram -> [(Double, Double)] -> Histogram-insertWs (Histogram cuts vs) vws = Histogram cuts (Map.unionWith (+) vs s)-    where-      s = histMapW cuts vws--data DealOvers = IgnoreOvers | IncludeOvers Double--fromHist :: DealOvers -> Histogram -> [Rect Double]-fromHist o (Histogram cuts counts) = view rect <$> zipWith4 V4 x y z w'-  where-      y = repeat 0-      w = zipWith (/)-          ((\x -> Map.findWithDefault 0 x counts) <$> [f..l])-          (zipWith (-) z x)-      f = case o of-        IgnoreOvers -> 1-        IncludeOvers _ -> 0-      l = case o of-        IgnoreOvers -> length cuts - 1-        IncludeOvers _ -> length cuts-      w' = (/Protolude.sum w) <$> w-      x = case o of-        IgnoreOvers -> cuts-        IncludeOvers outw -> [Data.List.head cuts - outw] <> cuts <> [Data.List.last cuts + outw]-      z = drop 1 x--labels :: DealOvers -> [Double] -> [Text]-labels o cuts =-    case o of-      IgnoreOvers -> inside-      IncludeOvers _ -> [ "< " <> sformat (prec 2) (Data.List.head cuts)] <> inside <> [ "> " <> sformat (prec 2) (Data.List.last cuts)]-  where-    inside = sformat (prec 2) <$> zipWith (\l u -> (l+u)/2) cuts (drop 1 cuts)--hist :: [Double] -> Double -> L.Fold Double Histogram-hist cuts r =-    L.Fold-    (\(Histogram cuts counts) a ->-       Histogram cuts-       (Map.unionWith (+)-        (Map.map (*r) counts)-        (Map.singleton (L.fold countBool (fmap (a>) cuts)) 1)))-    (Histogram cuts mempty)-    identity+{-# OPTIONS_GHC -Wall #-}
+{-# OPTIONS_GHC -fno-warn-type-defaults #-}
+{-# OPTIONS_GHC -fno-warn-name-shadowing #-}
+{-# LANGUAGE OverloadedStrings #-}
+
+module NumHask.Histogram
+  ( Histogram(..)
+  , freq
+  , fill
+  , DealOvers(..)
+  , fromHist
+  , hist
+  , labels
+  , insert
+  , insertW
+  , insertWs
+  ) where
+
+import NumHask.Rect
+
+import Protolude
+import qualified Control.Foldl as L
+import qualified Data.Map.Strict as Map
+import Linear hiding (identity)
+import Data.List
+import Formatting
+import Control.Lens
+
+-- a histogram
+data Histogram = Histogram
+   { _cuts   :: [Double] -- bucket boundaries
+   , _values :: Map.Map Int Double -- bucket counts
+   } deriving (Show, Eq)
+
+freq' :: Map.Map Int Double -> Map.Map Int Double
+freq' m = Map.map (* recip (Protolude.sum m)) m
+
+freq :: Histogram -> Histogram
+freq (Histogram c v) = Histogram c (freq' v)
+
+count :: L.Fold Int (Map Int Double)
+count = L.premap (\x -> (x,1.0)) countW
+
+countW :: L.Fold (Int,Double) (Map Int Double)
+countW = L.Fold (\x (a,w) -> Map.insertWith (+) a w x) Map.empty identity
+
+countBool :: L.Fold Bool Int
+countBool = L.Fold (\x a -> x + if a then 1 else 0) 0 identity
+
+histMap :: (Functor f, Functor g, Ord a, Foldable f, Foldable g) =>
+    f a -> g a -> Map Int Double
+histMap cuts xs = L.fold count $ (\x -> L.fold countBool (fmap (x >) cuts)) <$> xs
+
+histMapW :: (Functor f, Functor g, Ord a, Foldable f, Foldable g) =>
+    f a -> g (a,Double) -> Map Int Double
+histMapW cuts xs = L.fold countW $
+    (\x -> (L.fold countBool (fmap (fst x >) cuts),snd x)) <$> xs
+
+fill :: [Double] -> [Double] -> Histogram
+fill cuts xs = Histogram cuts (histMap cuts xs)
+
+insertW :: Histogram -> Double -> Double -> Histogram
+insertW (Histogram cuts vs) value weight = Histogram cuts (Map.unionWith (+) vs s)
+    where
+      s = histMapW cuts [(value,weight)]
+
+insertWs :: Histogram -> [(Double, Double)] -> Histogram
+insertWs (Histogram cuts vs) vws = Histogram cuts (Map.unionWith (+) vs s)
+    where
+      s = histMapW cuts vws
+
+data DealOvers = IgnoreOvers | IncludeOvers Double
+
+fromHist :: DealOvers -> Histogram -> [Rect Double]
+fromHist o (Histogram cuts counts) = view rect <$> zipWith4 V4 x y z w'
+  where
+      y = repeat 0
+      w = zipWith (/)
+          ((\x -> Map.findWithDefault 0 x counts) <$> [f..l])
+          (zipWith (-) z x)
+      f = case o of
+        IgnoreOvers -> 1
+        IncludeOvers _ -> 0
+      l = case o of
+        IgnoreOvers -> length cuts - 1
+        IncludeOvers _ -> length cuts
+      w' = (/Protolude.sum w) <$> w
+      x = case o of
+        IgnoreOvers -> cuts
+        IncludeOvers outw -> [Data.List.head cuts - outw] <> cuts <> [Data.List.last cuts + outw]
+      z = drop 1 x
+
+labels :: DealOvers -> [Double] -> [Text]
+labels o cuts =
+    case o of
+      IgnoreOvers -> inside
+      IncludeOvers _ -> [ "< " <> sformat (prec 2) (Data.List.head cuts)] <> inside <> [ "> " <> sformat (prec 2) (Data.List.last cuts)]
+  where
+    inside = sformat (prec 2) <$> zipWith (\l u -> (l+u)/2) cuts (drop 1 cuts)
+
+hist :: [Double] -> Double -> L.Fold Double Histogram
+hist cuts r =
+    L.Fold
+    (\(Histogram cuts counts) a ->
+       Histogram cuts
+       (Map.unionWith (+)
+        (Map.map (*r) counts)
+        (Map.singleton (L.fold countBool (fmap (a>) cuts)) 1)))
+    (Histogram cuts mempty)
+    identity
src/NumHask/Range.hs view
@@ -1,240 +1,240 @@-{-# LANGUAGE UndecidableInstances #-}-{-# LANGUAGE ExtendedDefaultRules #-}-{-# OPTIONS_GHC -Wall #-}---- | A 'Range' a is a tuple representing an interval of a number space.  A Range can be thought of as consisting of a low and high value, though low<high isn't strictly enforced, allowing a negative space so to speak.--- The library uses the 'NumHask' classes and thus most of the usual arithmetic operators can be used.--module NumHask.Range-  ( Range(..)-  , (...)-  , low-  , high-  , mid-  , width-  , element-  , singleton-  , singular-  , intersection-  , contains-  , range-  , project-  , LinearPos(..)-  , linearSpace-  , linearSpaceSensible-  , fromLinearSpace- ) where--import NumHask.Prelude-import Control.Category (id)-import Control.Lens hiding (Magma, singular, element, contains, (...))-import qualified Control.Foldl as L-import Test.QuickCheck---- | a newtype wrapped (a, a) tuple-newtype Range a = Range { range_ :: (a, a) }-  deriving (Eq, Ord, Show, Functor)---- | alternative constructor-(...) :: Ord a => a -> a -> Range a-a ... b-  | a <= b = Range (a, b)-  | otherwise = Range (b, a)---- | lens for the fst of the tuple-low :: Lens' (Range a) a-low = lens (\(Range (l,_)) -> l) (\(Range (_,u)) l -> Range (l,u))---- | lens for the snd of the tuple-high :: Lens' (Range a) a-high = lens (\(Range (_,u)) -> u) (\(Range (l,_)) u -> Range (l,u))---- | mid-value lens-mid ::-    (BoundedField a) =>-    Lens' (Range a) a-mid =-    lens-    plushom-    (\r m -> Range (m - plushom r, m + plushom r))---- | range width lens-width ::-    (BoundedField a) =>-    Lens' (Range a) a-width =-    lens-    (\(Range (l,u)) -> (u-l))-    (\r w -> Range (plushom r - w/two, plushom r + w/two))--instance (Arbitrary a) => Arbitrary (Range a) where-    arbitrary = do-        a <- arbitrary-        b <- arbitrary-        pure (Range (a,b))---- | choosing the convex hull as plus seems like a natural choice, given the cute zero definition.-instance (Ord a) => AdditiveMagma (Range a) where-    plus (Range (l0,u0)) (Range (l1,u1)) = Range (min l0 l1, max u0 u1)--instance (Ord a, BoundedField a) => AdditiveUnital (Range a) where-    zero = Range (infinity,neginfinity)--instance (Ord a) => AdditiveAssociative (Range a)-instance (Ord a) => AdditiveCommutative (Range a)-instance (Ord a, BoundedField a) => Additive (Range a)--instance (Ord a) => Semigroup (Range a) where-    (<>) = plus--instance (AdditiveUnital (Range a), Semigroup (Range a)) => Monoid (Range a) where-    mempty = zero-    mappend = (<>)--instance (Ord a) => AdditiveInvertible (Range a)-    where-        negate (Range (l,u)) = Range (u,l)--instance (BoundedField a, Ord a) => AdditiveGroup (Range a)---- | natural interpretation of a `Range a` as an `a` is the mid-point-instance (BoundedField a) =>-    AdditiveHomomorphic (Range a) a where-    plushom (Range (l,u)) = (l+u)/two---- | natural interpretation of an `a` as a `Range a` is a singular Range-instance (Ord a) =>-    AdditiveHomomorphic a (Range a) where-    plushom a = singleton a---- | times may well be some sort of affine projection lurking under the hood-instance (BoundedField a) => MultiplicativeMagma (Range a) where-    times a b = Range (m - r/two, m + r/two)-        where-          m = view mid b + (view mid a * view width b)-          r = view width a * view width b---- | The unital object derives from:------ view range one = one--- view mid zero = zero--- ie (-0.5,0.5)-instance (BoundedField a) => MultiplicativeUnital (Range a) where-    one = Range (negate half, half)--instance (BoundedField a) => MultiplicativeAssociative (Range a)--instance (Ord a, BoundedField a) => MultiplicativeInvertible (Range a) where-    recip a = case view width a == zero of-      True  -> theta-      False -> Range (m - r/two, m + r/two)-        where-          m = negate (view mid a) * recip (view width a)-          r = recip (view width a)--instance (Ord a, BoundedField a) => MultiplicativeRightCancellative (Range a)-instance (Ord a, BoundedField a) => MultiplicativeLeftCancellative (Range a)--instance (BoundedField a, Ord a) => Signed (Range a) where-    sign (Range (l,u)) = if u >= l then one else negate one-    abs (Range (l,u)) = if u >= l then Range (l,u) else Range (u,l)--instance (AdditiveGroup a) => Normed (Range a) a where-    size (Range (l, u)) = u-l--instance (Ord a, AdditiveGroup a) => Metric (Range a) a where-    distance (Range (l,u)) (Range (l',u'))-        | u < l' = l' - u-        | u' < l = l - u'-        | otherwise = zero---- | theta is a bit like 1/infinity-theta :: (AdditiveUnital a) => Range a-theta = Range (zero, zero)--two :: (MultiplicativeUnital a, Additive a) => a-two = one + one--half :: (BoundedField a) => a-half = one / (one + one)--singleton :: a -> Range a-singleton a = Range (a,a)---- | determine whether a point is within the range-element :: (Ord a) => a -> Range a -> Bool-element a (Range (l,u)) = a >= l && a <= u---- | is the range a singleton point-singular :: (Eq a) => Range a -> Bool-singular (Range (l,u)) = l==u--intersection :: (Ord a) => Range a -> Range a -> Range a-intersection a b =-    Range (max (view low a) (view low b), min (view high a) (view high b))--contains :: (Ord a) => Range a -> Range a -> Bool-contains (Range (l,u)) (Range (l',u')) = l <= l' && u >= u'---- | range of a foldable-range :: (Foldable f, Ord a, BoundedField a) => f a -> Range a-range = L.fold (L.Fold (\x a -> x + singleton a) zero id)---- | project a data point from an old range to a new range--- project o n (view low o) == view low n--- project o n (view high o) == view high n--- project a a == id-project :: (Field b) => Range b -> Range b -> b -> b-project (Range (l0,u0)) (Range (l1,u1)) p =-    ((p-l0)/(u0-l0)) * (u1-l1) + l1---- * linear--- | overns where data points go on the range-data LinearPos = OuterPos | InnerPos | LowerPos | UpperPos | MidPos deriving (Eq)---- | turn a range into a list of n equally-spaced `a`s-linearSpace :: (Field a, FromInteger a) => LinearPos -> Range a -> Int -> [a]-linearSpace o (Range (l, u)) n = (+ if o==MidPos then step/two else zero) <$> posns-  where-    posns = (l +) . (step *) . fromIntegral <$> [i0..i1]-    step = (u - l)/fromIntegral n-    (i0,i1) = case o of-                OuterPos -> (0,n)-                InnerPos -> (1,n - 1)-                LowerPos -> (0,n - 1)-                UpperPos -> (1,n)-                MidPos -> (0,n - 1)---- | turn a range into n `a`s pleasing to human sense and sensibility--- the `a`s may well lie outside the original range as a result-linearSpaceSensible :: (Fractional a, Ord a, FromInteger a, QuotientField a, ExpField a) =>-    LinearPos -> Range a -> Int -> [a]-linearSpaceSensible tp (Range (l, u)) n =-    (+ if tp==MidPos then step/two else zero) <$> posns-  where-    posns = (first' +) . (step *) . fromIntegral <$> [i0..i1]-    span = u - l-    step' = 10 ^^ floor (logBase 10 (span/fromIntegral n))-    err = fromIntegral n / span * step'-    step-      | err <= 0.15 = 10 * step'-      | err <= 0.35 = 5 * step'-      | err <= 0.75 = 2 * step'-      | otherwise = step'-    first' = step * fromIntegral (ceiling (l/step))-    last' = step * fromIntegral (floor (u/step))-    n' = round ((last' - first')/step)-    (i0,i1) = case tp of-                OuterPos -> (0,n')-                InnerPos -> (1,n' - 1)-                LowerPos -> (0,n' - 1)-                UpperPos -> (1,n')-                MidPos -> (0,n' - 1)---- | take a list of (ascending) `a`s and make some (ascending) ranges--- based on OuterPos--- fromLinearSpace . linearSpace OuterPos == id--- linearSpace OuterPos . fromLinearSpace == id-fromLinearSpace :: [a] -> [Range a]-fromLinearSpace as = zipWith (curry Range) as (drop 1 as)-+{-# LANGUAGE UndecidableInstances #-}
+{-# LANGUAGE ExtendedDefaultRules #-}
+{-# OPTIONS_GHC -Wall #-}
+
+-- | A 'Range' a is a tuple representing an interval of a number space.  A Range can be thought of as consisting of a low and high value, though low<high isn't strictly enforced, allowing a negative space so to speak.
+-- The library uses the 'NumHask' classes and thus most of the usual arithmetic operators can be used.
+
+module NumHask.Range
+  ( Range(..)
+  , (...)
+  , low
+  , high
+  , mid
+  , width
+  , element
+  , singleton
+  , singular
+  , intersection
+  , contains
+  , range
+  , project
+  , LinearPos(..)
+  , linearSpace
+  , linearSpaceSensible
+  , fromLinearSpace
+ ) where
+
+import NumHask.Prelude
+import Control.Category (id)
+import Control.Lens hiding (Magma, singular, element, contains, (...))
+import qualified Control.Foldl as L
+import Test.QuickCheck
+
+-- | a newtype wrapped (a, a) tuple
+newtype Range a = Range { range_ :: (a, a) }
+  deriving (Eq, Ord, Show, Functor)
+
+-- | alternative constructor
+(...) :: Ord a => a -> a -> Range a
+a ... b
+  | a <= b = Range (a, b)
+  | otherwise = Range (b, a)
+
+-- | lens for the fst of the tuple
+low :: Lens' (Range a) a
+low = lens (\(Range (l,_)) -> l) (\(Range (_,u)) l -> Range (l,u))
+
+-- | lens for the snd of the tuple
+high :: Lens' (Range a) a
+high = lens (\(Range (_,u)) -> u) (\(Range (l,_)) u -> Range (l,u))
+
+-- | mid-value lens
+mid ::
+    (BoundedField a) =>
+    Lens' (Range a) a
+mid =
+    lens
+    plushom
+    (\r m -> Range (m - plushom r, m + plushom r))
+
+-- | range width lens
+width ::
+    (BoundedField a) =>
+    Lens' (Range a) a
+width =
+    lens
+    (\(Range (l,u)) -> (u-l))
+    (\r w -> Range (plushom r - w/two, plushom r + w/two))
+
+instance (Arbitrary a) => Arbitrary (Range a) where
+    arbitrary = do
+        a <- arbitrary
+        b <- arbitrary
+        pure (Range (a,b))
+
+-- | choosing the convex hull as plus seems like a natural choice, given the cute zero definition.
+instance (Ord a) => AdditiveMagma (Range a) where
+    plus (Range (l0,u0)) (Range (l1,u1)) = Range (min l0 l1, max u0 u1)
+
+instance (Ord a, BoundedField a) => AdditiveUnital (Range a) where
+    zero = Range (infinity,neginfinity)
+
+instance (Ord a) => AdditiveAssociative (Range a)
+instance (Ord a) => AdditiveCommutative (Range a)
+instance (Ord a, BoundedField a) => Additive (Range a)
+
+instance (Ord a) => Semigroup (Range a) where
+    (<>) = plus
+
+instance (AdditiveUnital (Range a), Semigroup (Range a)) => Monoid (Range a) where
+    mempty = zero
+    mappend = (<>)
+
+instance (Ord a) => AdditiveInvertible (Range a)
+    where
+        negate (Range (l,u)) = Range (u,l)
+
+instance (BoundedField a, Ord a) => AdditiveGroup (Range a)
+
+-- | natural interpretation of a `Range a` as an `a` is the mid-point
+instance (BoundedField a) =>
+    AdditiveHomomorphic (Range a) a where
+    plushom (Range (l,u)) = (l+u)/two
+
+-- | natural interpretation of an `a` as a `Range a` is a singular Range
+instance (Ord a) =>
+    AdditiveHomomorphic a (Range a) where
+    plushom a = singleton a
+
+-- | times may well be some sort of affine projection lurking under the hood
+instance (BoundedField a) => MultiplicativeMagma (Range a) where
+    times a b = Range (m - r/two, m + r/two)
+        where
+          m = view mid b + (view mid a * view width b)
+          r = view width a * view width b
+
+-- | The unital object derives from:
+--
+-- view range one = one
+-- view mid zero = zero
+-- ie (-0.5,0.5)
+instance (BoundedField a) => MultiplicativeUnital (Range a) where
+    one = Range (negate half, half)
+
+instance (BoundedField a) => MultiplicativeAssociative (Range a)
+
+instance (Ord a, BoundedField a) => MultiplicativeInvertible (Range a) where
+    recip a = case view width a == zero of
+      True  -> theta
+      False -> Range (m - r/two, m + r/two)
+        where
+          m = negate (view mid a) * recip (view width a)
+          r = recip (view width a)
+
+instance (Ord a, BoundedField a) => MultiplicativeRightCancellative (Range a)
+instance (Ord a, BoundedField a) => MultiplicativeLeftCancellative (Range a)
+
+instance (BoundedField a, Ord a) => Signed (Range a) where
+    sign (Range (l,u)) = if u >= l then one else negate one
+    abs (Range (l,u)) = if u >= l then Range (l,u) else Range (u,l)
+
+instance (AdditiveGroup a) => Normed (Range a) a where
+    size (Range (l, u)) = u-l
+
+instance (Ord a, AdditiveGroup a) => Metric (Range a) a where
+    distance (Range (l,u)) (Range (l',u'))
+        | u < l' = l' - u
+        | u' < l = l - u'
+        | otherwise = zero
+
+-- | theta is a bit like 1/infinity
+theta :: (AdditiveUnital a) => Range a
+theta = Range (zero, zero)
+
+two :: (MultiplicativeUnital a, Additive a) => a
+two = one + one
+
+half :: (BoundedField a) => a
+half = one / (one + one)
+
+singleton :: a -> Range a
+singleton a = Range (a,a)
+
+-- | determine whether a point is within the range
+element :: (Ord a) => a -> Range a -> Bool
+element a (Range (l,u)) = a >= l && a <= u
+
+-- | is the range a singleton point
+singular :: (Eq a) => Range a -> Bool
+singular (Range (l,u)) = l==u
+
+intersection :: (Ord a) => Range a -> Range a -> Range a
+intersection a b =
+    Range (max (view low a) (view low b), min (view high a) (view high b))
+
+contains :: (Ord a) => Range a -> Range a -> Bool
+contains (Range (l,u)) (Range (l',u')) = l <= l' && u >= u'
+
+-- | range of a foldable
+range :: (Foldable f, Ord a, BoundedField a) => f a -> Range a
+range = L.fold (L.Fold (\x a -> x + singleton a) zero id)
+
+-- | project a data point from an old range to a new range
+-- project o n (view low o) == view low n
+-- project o n (view high o) == view high n
+-- project a a == id
+project :: (Field b) => Range b -> Range b -> b -> b
+project (Range (l0,u0)) (Range (l1,u1)) p =
+    ((p-l0)/(u0-l0)) * (u1-l1) + l1
+
+-- * linear
+-- | overns where data points go on the range
+data LinearPos = OuterPos | InnerPos | LowerPos | UpperPos | MidPos deriving (Eq)
+
+-- | turn a range into a list of n equally-spaced `a`s
+linearSpace :: (Field a, FromInteger a) => LinearPos -> Range a -> Int -> [a]
+linearSpace o (Range (l, u)) n = (+ if o==MidPos then step/two else zero) <$> posns
+  where
+    posns = (l +) . (step *) . fromIntegral <$> [i0..i1]
+    step = (u - l)/fromIntegral n
+    (i0,i1) = case o of
+                OuterPos -> (0,n)
+                InnerPos -> (1,n - 1)
+                LowerPos -> (0,n - 1)
+                UpperPos -> (1,n)
+                MidPos -> (0,n - 1)
+
+-- | turn a range into n `a`s pleasing to human sense and sensibility
+-- the `a`s may well lie outside the original range as a result
+linearSpaceSensible :: (Fractional a, Ord a, FromInteger a, QuotientField a, ExpField a) =>
+    LinearPos -> Range a -> Int -> [a]
+linearSpaceSensible tp (Range (l, u)) n =
+    (+ if tp==MidPos then step/two else zero) <$> posns
+  where
+    posns = (first' +) . (step *) . fromIntegral <$> [i0..i1]
+    span = u - l
+    step' = 10 ^^ floor (logBase 10 (span/fromIntegral n))
+    err = fromIntegral n / span * step'
+    step
+      | err <= 0.15 = 10 * step'
+      | err <= 0.35 = 5 * step'
+      | err <= 0.75 = 2 * step'
+      | otherwise = step'
+    first' = step * fromIntegral (ceiling (l/step))
+    last' = step * fromIntegral (floor (u/step))
+    n' = round ((last' - first')/step)
+    (i0,i1) = case tp of
+                OuterPos -> (0,n')
+                InnerPos -> (1,n' - 1)
+                LowerPos -> (0,n' - 1)
+                UpperPos -> (1,n')
+                MidPos -> (0,n' - 1)
+
+-- | take a list of (ascending) `a`s and make some (ascending) ranges
+-- based on OuterPos
+-- fromLinearSpace . linearSpace OuterPos == id
+-- linearSpace OuterPos . fromLinearSpace == id
+fromLinearSpace :: [a] -> [Range a]
+fromLinearSpace as = zipWith (curry Range) as (drop 1 as)
+
src/NumHask/Rect.hs view
@@ -1,185 +1,153 @@-{-# LANGUAGE UndecidableInstances #-}-{-# OPTIONS_GHC -Wall #-}--module NumHask.Rect-  ( Rect(..)-  , rect-  , corners-  , midRect-  , elementRect-  , singletonRect-  , singularRect-  , intersectionRect-  , containsRect-  , rangeR2s-  , scaleR2s-  , rangeRects-  , projectRect-  , scaleRects-  , scaleRectss-  , gridP-  , grid-  ) where--import NumHask.Range-import NumHask.Prelude-import Control.Lens hiding (Magma, singular, element, contains)-import Linear.V2-import Linear.V4---- | a two-dimensional plane, bounded by ranges.-newtype Rect a = Rect {xy :: V2 (Range a)}-    deriving (Show, Eq, Ord, Functor)---- | an alternative specification; as a 4-dim vector `V4 x y z w` where:--- - (x,y) is the lower left corner of a rectangle, and--- - (z,w) is the upper right corner of a rectangle-rect :: Iso' (V4 a) (Rect a)-rect = iso toRect toV4-  where-    toRect (V4 x y z w) = Rect $ V2 (Range (x,z)) (Range (y,w))-    toV4 (Rect (V2 (Range (x,z)) (Range (y,w)))) = V4 x y z w---- | a convex hull approach-instance (Ord a) => AdditiveMagma (Rect a) where-    plus (Rect (V2 ax ay)) (Rect (V2 bx yb)) =-        Rect (V2 (ax `plus` bx) (ay `plus` yb))--instance (Ord a, BoundedField a) => AdditiveUnital (Rect a) where-    zero = Rect $ V2 zero zero--instance (Ord a) => AdditiveAssociative (Rect a)-instance (Ord a) => AdditiveCommutative (Rect a)-instance (Ord a, BoundedField a) => Additive (Rect a)--instance (Ord a) => Semigroup (Rect a) where-    (<>) = plus--instance (AdditiveUnital (Rect a), Semigroup (Rect a)) => Monoid (Rect a) where-    mempty = zero-    mappend = (<>)--instance (Ord a) => AdditiveInvertible (Rect a) where-    negate (Rect (V2 x y)) = Rect (V2 (negate x) (negate y))--instance (BoundedField a, Ord a) => AdditiveGroup (Rect a)---- | natural interpretation of an `a` as an `Rect a`-instance (Ord a) =>-    AdditiveHomomorphic (V2 a) (Rect a) where-    plushom v = singletonRect v--instance (BoundedField a) => MultiplicativeMagma (Rect a) where-    (Rect (V2 a0 b0)) `times` (Rect (V2 a1 b1)) =-        Rect (V2 (a0 `times` a1) (b0 `times` b1))--instance (BoundedField a) => MultiplicativeUnital (Rect a) where-    one = Rect (V2 one one)-instance (BoundedField a) => MultiplicativeAssociative (Rect a)-instance (Ord a, BoundedField a) => MultiplicativeInvertible (Rect a) where-    recip (Rect (V2 a b)) = Rect (V2 (recip a) (recip b))-instance (Ord a, BoundedField a) => MultiplicativeLeftCancellative (Rect a)-instance (Ord a, BoundedField a) => MultiplicativeRightCancellative (Rect a)--instance (BoundedField a, Ord a) => Signed (Rect a) where-    sign (Rect (V2 a b)) = Rect (V2 (sign a) (sign b))-    abs (Rect (V2 a b)) = Rect (V2 (abs a) (abs b))--instance (AdditiveGroup a) => Normed (Rect a) (V2 a) where-    size (Rect (V2 x y)) = V2 (size x) (size y)--instance (Ord a, AdditiveGroup a) => Metric (Rect a) (V2 a) where-    distance (Rect (V2 x y)) (Rect (V2 x1 y1)) = V2 (distance x x1) (distance y y1)---midRect :: (BoundedField a) => Rect a -> V2 a-midRect (Rect (V2 x y)) = V2 (plushom x) (plushom y)---- | determine whether a point is within the range-elementRect :: (Ord a) => V2 a -> Rect a -> Bool-elementRect (V2 x y) (Rect (V2 rx ry)) = NumHask.Range.element x rx && NumHask.Range.element y ry---- | is the range a singleton V2 (has zero area)-singularRect :: (Eq a) => Rect a -> Bool-singularRect (Rect (V2 x y)) = NumHask.Range.singular x || NumHask.Range.singular y--singletonRect :: V2 a -> Rect a-singletonRect (V2 x y) = Rect (V2 (singleton x) (singleton y)) --intersectionRect :: (Ord a) => Rect a -> Rect a -> Rect a-intersectionRect (Rect (V2 x y)) (Rect (V2 x1 y1)) =-    Rect (V2 (NumHask.Range.intersection x x1) (NumHask.Range.intersection y y1))--containsRect :: (Ord a) => Rect a -> Rect a -> Bool-containsRect (Rect (V2 x y)) (Rect (V2 x1 y1)) =-    NumHask.Range.contains x x1 && NumHask.Range.contains y y1--corners :: Rect a -> [V2 a]-corners (Rect (V2 (Range (lx,ux)) (Range (ly,uy)))) = [V2 lx ly, V2 ux uy]---- | the range Rect of a container of R2s-rangeR2 :: (Traversable f, Ord a, BoundedField a, R2 r) => f (r a) -> Rect a-rangeR2 f = Rect (V2 (range $ view _x <$> f) (range $ view _y <$> f))---- | range specialized to double traversables-rangeR2s :: (BoundedField a, Traversable g, Traversable f, R2 r, Ord a) =>-    g (f (r a)) -> Rect a-rangeR2s f = foldMap rangeR2 f---- | project a container of r2 points from an old Rect to a new one-projectR2 :: (R2 r, Field a, Functor f) =>-    Rect a -> Rect a -> f (r a) -> f (r a)-projectR2 (Rect (V2 rx ry)) (Rect (V2 rx' ry')) qs =-    (over _x (project rx rx') . over _y (project ry ry')) <$> qs---- | project a double container of r2s from the current Rect range-scaleR2s ::-    (R2 r, BoundedField a, Traversable f, Traversable g, Ord a) =>-    Rect a -> g (f (r a)) -> g (f (r a))-scaleR2s xy qss = projectR2 (rangeR2s qss) xy <$> qss---- | project a Rect from an old Rect range to a new one-projectRect :: (Field a) =>-    Rect a -> Rect a -> Rect a -> Rect a-projectRect (Rect (V2 rx ry)) (Rect (V2 rx' ry')) (Rect (V2 rx0 ry0)) =-    Rect (V2 (project rx rx' <$> rx0) (project ry ry' <$> ry0))---- | project a container of Rects from an old Rect range to a new one-projectRects :: (Field a, Functor f) =>-    Rect a -> Rect a -> f (Rect a) -> f (Rect a)-projectRects o n f = projectRect o n <$> f---- | the range Rect of a container of Rects-rangeRects :: (Ord a, BoundedField a, Traversable f) =>-    f (Rect a) -> Rect a-rangeRects f = fold f---- | scale a double container of Rects from the current range-scaleRects ::-    (BoundedField a, Traversable f, Ord a) =>-    Rect a -> f (Rect a) -> f (Rect a)-scaleRects xy f = projectRects (fold f) xy f---- | scale a double container of Rects from the current range-scaleRectss ::-    (BoundedField a, Traversable f, Traversable g, Ord a) =>-    Rect a -> g (f (Rect a)) -> g (f (Rect a))-scaleRectss xy g = projectRects (fold $ fold <$> g) xy <$> g---- | grid points on a rectange, divided up by a V2 Int-gridP :: (Field a, FromInteger a) => LinearPos -> Rect a -> V2 Int -> [V2 a]-gridP tp (Rect (V2 rX rY)) (V2 stepX stepY) =-    [V2 x y | x <- linearSpace tp rX stepX, y <- linearSpace tp rY stepY]---- | a rectangle divided up by a V2 Int, making a list of smaller rectangles-grid :: (BoundedField a, FromInteger a) => Rect a -> V2 Int -> [Rect a]-grid (Rect (V2 rX rY)) (V2 stepX stepY) =-    [ Rect (V2 (Range (x,x+sx)) (Range (y,y+sy)))-    | x <- linearSpace LowerPos rX stepX-    , y <- linearSpace LowerPos rY stepY-    ]-  where-    sx = view width rX / fromIntegral stepX-    sy = view width rY / fromIntegral stepY--+{-# LANGUAGE UndecidableInstances #-}
+{-# OPTIONS_GHC -Wall #-}
+
+module NumHask.Rect
+  ( Rect(..)
+  , rect
+  , corners
+  , midRect
+  , elementRect
+  , singletonRect
+  , singularRect
+  , intersectionRect
+  , containsRect
+  , rangeR2
+  , rangeR2s
+  , projectR2
+  , projectRect
+  , gridP
+  , grid
+  ) where
+
+import NumHask.Range
+import NumHask.Prelude
+import Control.Lens hiding (Magma, singular, element, contains)
+import Linear.V2
+import Linear.V4
+
+-- | a two-dimensional plane, bounded by ranges.
+newtype Rect a = Rect {xy :: V2 (Range a)}
+    deriving (Show, Eq, Ord, Functor)
+
+-- | an alternative specification; as a 4-dim vector `V4 x y z w` where:
+-- - (x,y) is the lower left corner of a rectangle, and
+-- - (z,w) is the upper right corner of a rectangle
+rect :: Iso' (V4 a) (Rect a)
+rect = iso toRect toV4
+  where
+    toRect (V4 x y z w) = Rect $ V2 (Range (x,z)) (Range (y,w))
+    toV4 (Rect (V2 (Range (x,z)) (Range (y,w)))) = V4 x y z w
+
+-- | a convex hull approach
+instance (Ord a) => AdditiveMagma (Rect a) where
+    plus (Rect (V2 ax ay)) (Rect (V2 bx yb)) =
+        Rect (V2 (ax `plus` bx) (ay `plus` yb))
+
+instance (Ord a, BoundedField a) => AdditiveUnital (Rect a) where
+    zero = Rect $ V2 zero zero
+
+instance (Ord a) => AdditiveAssociative (Rect a)
+instance (Ord a) => AdditiveCommutative (Rect a)
+instance (Ord a, BoundedField a) => Additive (Rect a)
+
+instance (Ord a) => Semigroup (Rect a) where
+    (<>) = plus
+
+instance (AdditiveUnital (Rect a), Semigroup (Rect a)) => Monoid (Rect a) where
+    mempty = zero
+    mappend = (<>)
+
+instance (Ord a) => AdditiveInvertible (Rect a) where
+    negate (Rect (V2 x y)) = Rect (V2 (negate x) (negate y))
+
+instance (BoundedField a, Ord a) => AdditiveGroup (Rect a)
+
+-- | natural interpretation of an `a` as an `Rect a`
+instance (Ord a) =>
+    AdditiveHomomorphic (V2 a) (Rect a) where
+    plushom v = singletonRect v
+
+instance (BoundedField a) => MultiplicativeMagma (Rect a) where
+    (Rect (V2 a0 b0)) `times` (Rect (V2 a1 b1)) =
+        Rect (V2 (a0 `times` a1) (b0 `times` b1))
+
+instance (BoundedField a) => MultiplicativeUnital (Rect a) where
+    one = Rect (V2 one one)
+instance (BoundedField a) => MultiplicativeAssociative (Rect a)
+instance (Ord a, BoundedField a) => MultiplicativeInvertible (Rect a) where
+    recip (Rect (V2 a b)) = Rect (V2 (recip a) (recip b))
+instance (Ord a, BoundedField a) => MultiplicativeLeftCancellative (Rect a)
+instance (Ord a, BoundedField a) => MultiplicativeRightCancellative (Rect a)
+
+instance (BoundedField a, Ord a) => Signed (Rect a) where
+    sign (Rect (V2 a b)) = Rect (V2 (sign a) (sign b))
+    abs (Rect (V2 a b)) = Rect (V2 (abs a) (abs b))
+
+instance (AdditiveGroup a) => Normed (Rect a) (V2 a) where
+    size (Rect (V2 x y)) = V2 (size x) (size y)
+
+instance (Ord a, AdditiveGroup a) => Metric (Rect a) (V2 a) where
+    distance (Rect (V2 x y)) (Rect (V2 x1 y1)) = V2 (distance x x1) (distance y y1)
+
+
+midRect :: (BoundedField a) => Rect a -> V2 a
+midRect (Rect (V2 x y)) = V2 (plushom x) (plushom y)
+
+-- | determine whether a point is within the range
+elementRect :: (Ord a) => V2 a -> Rect a -> Bool
+elementRect (V2 x y) (Rect (V2 rx ry)) = NumHask.Range.element x rx && NumHask.Range.element y ry
+
+-- | is the range a singleton V2 (has zero area)
+singularRect :: (Eq a) => Rect a -> Bool
+singularRect (Rect (V2 x y)) = NumHask.Range.singular x || NumHask.Range.singular y
+
+singletonRect :: V2 a -> Rect a
+singletonRect (V2 x y) = Rect (V2 (singleton x) (singleton y)) 
+
+intersectionRect :: (Ord a) => Rect a -> Rect a -> Rect a
+intersectionRect (Rect (V2 x y)) (Rect (V2 x1 y1)) =
+    Rect (V2 (NumHask.Range.intersection x x1) (NumHask.Range.intersection y y1))
+
+containsRect :: (Ord a) => Rect a -> Rect a -> Bool
+containsRect (Rect (V2 x y)) (Rect (V2 x1 y1)) =
+    NumHask.Range.contains x x1 && NumHask.Range.contains y y1
+
+corners :: Rect a -> [V2 a]
+corners (Rect (V2 (Range (lx,ux)) (Range (ly,uy)))) = [V2 lx ly, V2 ux uy]
+
+-- | the range Rect of a container of R2s
+rangeR2 :: (Traversable f, Ord a, BoundedField a, R2 r) => f (r a) -> Rect a
+rangeR2 f = Rect (V2 (range $ view _x <$> f) (range $ view _y <$> f))
+
+-- | range specialized to double traversables
+rangeR2s :: (BoundedField a, Traversable g, Traversable f, R2 r, Ord a) =>
+    g (f (r a)) -> Rect a
+rangeR2s f = foldMap rangeR2 f
+
+-- | project a container of r2 points from an old Rect to a new one
+projectR2 :: (R2 r, Field a, Functor f) =>
+    Rect a -> Rect a -> f (r a) -> f (r a)
+projectR2 (Rect (V2 rx ry)) (Rect (V2 rx' ry')) qs =
+    (over _x (project rx rx') . over _y (project ry ry')) <$> qs
+
+-- | project a Rect from an old Rect range to a new one
+projectRect :: (Field a) =>
+    Rect a -> Rect a -> Rect a -> Rect a
+projectRect (Rect (V2 rx ry)) (Rect (V2 rx' ry')) (Rect (V2 rx0 ry0)) =
+    Rect (V2 (project rx rx' <$> rx0) (project ry ry' <$> ry0))
+
+-- | grid points on a rectange, divided up by a V2 Int
+gridP :: (Field a, FromInteger a) => LinearPos -> Rect a -> V2 Int -> [V2 a]
+gridP tp (Rect (V2 rX rY)) (V2 stepX stepY) =
+    [V2 x y | x <- linearSpace tp rX stepX, y <- linearSpace tp rY stepY]
+
+-- | a rectangle divided up by a V2 Int, making a list of smaller rectangles
+grid :: (BoundedField a, FromInteger a) => Rect a -> V2 Int -> [Rect a]
+grid (Rect (V2 rX rY)) (V2 stepX stepY) =
+    [ Rect (V2 (Range (x,x+sx)) (Range (y,y+sy)))
+    | x <- linearSpace LowerPos rX stepX
+    , y <- linearSpace LowerPos rY stepY
+    ]
+  where
+    sx = view width rX / fromIntegral stepX
+    sy = view width rY / fromIntegral stepY
stack.yaml view
@@ -1,7 +1,7 @@-resolver: lts-8.9--packages:-- '.'--extra-deps:-- numhask-0.0.4+resolver: lts-8.9
+
+packages:
+- '.'
+
+extra-deps:
+- numhask-0.0.4
test/test.hs view
@@ -1,60 +1,60 @@-{-# OPTIONS_GHC -Wall #-}-{-# LANGUAGE DataKinds #-}--module Main where--import NumHask.Prelude-import NumHask.Range--import Test.Tasty (TestName, TestTree, testGroup, defaultMain, localOption)-import Test.Tasty.QuickCheck--data LawArity a =-    Nonary Bool |-    Unary (a -> Bool) |-    Binary (a -> a -> Bool) |-    Ternary (a -> a -> a -> Bool) |-    Ornary (a -> a -> a -> a -> Bool) |-    Failiary (a -> Property)--type Law a = (TestName, LawArity a)--testLawOf  :: (Arbitrary a, Show a) => [a] -> Law a -> TestTree-testLawOf _ (name, Nonary f) = testProperty name f-testLawOf _ (name, Unary f) = testProperty name f-testLawOf _ (name, Binary f) = testProperty name f-testLawOf _ (name, Ternary f) = testProperty name f-testLawOf _ (name, Ornary f) = testProperty name f-testLawOf _ (name, Failiary f) = testProperty name f--testRange :: TestTree-testRange = testGroup "Data.Range" $ testLawOf ([]::[Range Double]) <$> rangeLaws--main :: IO ()-main =-    defaultMain $ testGroup "range" [localOption (QuickCheckTests 1000) testRange]--rangeLaws :: [Law (Range Double)]-rangeLaws =-    [ ("associative: (a + b) + c = a + (b + c)", Ternary (\a b c -> (a + b) + c == a + (b + c)))-    , ("left id: zero + a = a", Unary (\a -> zero + a == a))-    , ("right id: a + zero = a", Unary (\a -> a + zero == a))-    , ("commutative: a + b == b + a", Binary (\a b -> a + b == b + a))-    , ("associative: a `times` (b `times` c) = (a `times` b) `times` c", Failiary $ expectFailure . (\a b c -> ((a `times` b) `times` c) == (a `times` (b `times` c))))-    , ("left id: one * a = a", Unary (\a -> fuzzyeq 1e-8 (one `times` a) a))-    , ("right id: a * one = a", Unary (\a -> fuzzyeq 1e-8 (a `times` one) a))-    , ("commutative: a * b == b * a", Failiary $ expectFailure . (\a b -> a `times` b == b `times` a))-    , ("recip iso: recip . recip == id", Unary (\a -> zeroRange a || fuzzyeq 1e-4 (recip . recip $ a) a))-    , ("divide: zero range || a /~ a = one", Unary (\a -> zeroRange a || fuzzyeq 1e-8 (a /~ a) one))-    , ("recip divide right: zero range || recip a == one /~ a", Unary (\a -> zeroRange a || fuzzyeq 1e-8 (recip a) (one /~ a)))-    , ("recip left: zero range || recip a * a == one",  Unary (\a -> zeroRange a ||fuzzyeq 1e-8 (recip a `times` a) one))-    , ("recip right: zero range || a * recip a == one", Unary (\a -> zeroRange a || fuzzyeq 1e-8 (a `times` recip a) one))-    ]--fuzzyeq :: (AdditiveGroup a, Ord a) => a -> Range a -> Range a -> Bool-fuzzyeq eps0 (Range (l0,u0)) (Range (l1,u1)) =-    (l0-l1) <= eps0 && (l1-l0) <= eps0 && (u0-u1) <= eps0 && (u1-u0) <= eps0 --zeroRange :: (Eq a) => Range a -> Bool-zeroRange (Range (l,u)) = l==u-+{-# OPTIONS_GHC -Wall #-}
+{-# LANGUAGE DataKinds #-}
+
+module Main where
+
+import NumHask.Prelude
+import NumHask.Range
+
+import Test.Tasty (TestName, TestTree, testGroup, defaultMain, localOption)
+import Test.Tasty.QuickCheck
+
+data LawArity a =
+    Nonary Bool |
+    Unary (a -> Bool) |
+    Binary (a -> a -> Bool) |
+    Ternary (a -> a -> a -> Bool) |
+    Ornary (a -> a -> a -> a -> Bool) |
+    Failiary (a -> Property)
+
+type Law a = (TestName, LawArity a)
+
+testLawOf  :: (Arbitrary a, Show a) => [a] -> Law a -> TestTree
+testLawOf _ (name, Nonary f) = testProperty name f
+testLawOf _ (name, Unary f) = testProperty name f
+testLawOf _ (name, Binary f) = testProperty name f
+testLawOf _ (name, Ternary f) = testProperty name f
+testLawOf _ (name, Ornary f) = testProperty name f
+testLawOf _ (name, Failiary f) = testProperty name f
+
+testRange :: TestTree
+testRange = testGroup "Data.Range" $ testLawOf ([]::[Range Double]) <$> rangeLaws
+
+main :: IO ()
+main =
+    defaultMain $ testGroup "range" [localOption (QuickCheckTests 1000) testRange]
+
+rangeLaws :: [Law (Range Double)]
+rangeLaws =
+    [ ("associative: (a + b) + c = a + (b + c)", Ternary (\a b c -> (a + b) + c == a + (b + c)))
+    , ("left id: zero + a = a", Unary (\a -> zero + a == a))
+    , ("right id: a + zero = a", Unary (\a -> a + zero == a))
+    , ("commutative: a + b == b + a", Binary (\a b -> a + b == b + a))
+    , ("associative: a `times` (b `times` c) = (a `times` b) `times` c", Failiary $ expectFailure . (\a b c -> ((a `times` b) `times` c) == (a `times` (b `times` c))))
+    , ("left id: one * a = a", Unary (\a -> fuzzyeq 1e-8 (one `times` a) a))
+    , ("right id: a * one = a", Unary (\a -> fuzzyeq 1e-8 (a `times` one) a))
+    , ("commutative: a * b == b * a", Failiary $ expectFailure . (\a b -> a `times` b == b `times` a))
+    , ("recip iso: recip . recip == id", Unary (\a -> zeroRange a || fuzzyeq 1e-4 (recip . recip $ a) a))
+    , ("divide: zero range || a /~ a = one", Unary (\a -> zeroRange a || fuzzyeq 1e-8 (a /~ a) one))
+    , ("recip divide right: zero range || recip a == one /~ a", Unary (\a -> zeroRange a || fuzzyeq 1e-8 (recip a) (one /~ a)))
+    , ("recip left: zero range || recip a * a == one",  Unary (\a -> zeroRange a ||fuzzyeq 1e-8 (recip a `times` a) one))
+    , ("recip right: zero range || a * recip a == one", Unary (\a -> zeroRange a || fuzzyeq 1e-8 (a `times` recip a) one))
+    ]
+
+fuzzyeq :: (AdditiveGroup a, Ord a) => a -> Range a -> Range a -> Bool
+fuzzyeq eps0 (Range (l0,u0)) (Range (l1,u1)) =
+    (l0-l1) <= eps0 && (l1-l0) <= eps0 && (u0-u1) <= eps0 && (u1-u0) <= eps0 
+
+zeroRange :: (Eq a) => Range a -> Bool
+zeroRange (Range (l,u)) = l==u
+