!      !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~Safe,8=>?@DHVb"  numhask-spaceRPos suggests where points should be placed in forming a grid across a field space. numhask-space"a space that can be divided neatly numhask-space-create equally-spaced elements across a space  numhask-space)create equally-spaced spaces from a space numhask-space#the underlying element in the space numhask-spacelower boundary numhask-spaceupper boundary numhask-space!space containing a single element numhask-spacethe intersection of two spaces numhask-spacethe union of two spaces numhask-spaceYNormalise a space so that > lower a / upper a == lower a > lower a / upper a == upper a numhask-space+create a normalised space from two elements numhask-space4create a space from two elements without normalising numhask-spaceis an element in the space numhask-space'is one space completely above the other numhask-space'is one space completely below the other numhask-space$is a space contained within another? numhask-spaceare two spaces disjoint?  numhask-spacedistance between boundaries! numhask-space,create a space centered on a plus or minus b" numhask-spacemid-point of the space# numhask-spaceLproject a data point from one space to another, preserving relative position project o n (lower o) = lower n project o n (upper o) = upper n project a a x = x project mempty one zero = NaN project one mempty zero = Infinity project one mempty one = NaN$ numhask-space/the containing space of a non-empty Traversable% numhask-spaceFlift a monotone function (increasing or decreasing) over a given space& numhask-space a small space' numhask-space widen a space( numhask-spacewiden by a small amount) numhask-space scale a Space* numhask-space move a Space+  !"#$%&'()*+   "#$ !%&'()*33777!6None +,/7=>?@ADHIVXbg8/ numhask-spaceA continuous range over type alet a = Range (-1) 1a Range -1 1fmap (+1) (Range 1 2) Range 2 3one :: Range DoubleRange -0.5 0.5zero :: Range DoubleRange Infinity -Infinityas a Field instanceRange 0 1 + zero Range 0.0 1.0Range 0 1 + Range 2 3 Range 0.0 3.0Range 1 1 - one Range 0.5 1.0Range 0 1 * one Range 0.0 1.0Range 0 1 / one Range 0.0 1.0abs (Range 1 0) Range 0.0 1.0sign (Range 1 0) == negate oneTrue IdempotentRange 0 2 + Range 0 2 Range 0.0 2.0as a space instance1NumHask.Space.project (Range 0 1) (Range 1 4) 0.52.58NumHask.Space.grid NumHask.Space.OuterPos (Range 0 10) 5[0.0,2.0,4.0,6.0,8.0,10.0]%NumHask.Space.gridSpace (Range 0 1) 4=[Range 0.0 0.25,Range 0.25 0.5,Range 0.5 0.75,Range 0.75 1.0]:gridSensible NumHask.Space.OuterPos (Range (-12.0) 23.0) 6#[-10.0,-5.0,0.0,5.0,10.0,15.0,20.0]1 numhask-spaceA tuple is the preferred concrete implementation of a Range, due to many libraries having substantial optimizations for tuples already (eg Vector[). 'Pattern Synonyms' allow us to recover a constructor without the need for tuple syntax.3 numhask-space.Numeric algebra based on Interval arithmetic 1https://en.wikipedia.org/wiki/Interval_arithmetic4 numhask-space!Monoid based on convex hull union/012/012None7=>?@ADHVbgDF numhask-spaceOA 2-dim point of a's, implemented as a tuple, but api represented as Point a a.fmap (+1) (Point 1 2) Point 2 3pure one :: Point Int Point 1 1(*) <$> Point 1 2 <*> pure 2 Point 2 4foldr (++) [] (Point [1,2] [3])[1,2,3]HPoint "a" "pair" `mappend` pure " " `mappend` Point "string" "mappended"!Point "a string" "pair mappended"As a Ring and Field classPoint 0 1 + zero Point 0 1Point 0 1 + Point 2 3 Point 2 4Point 1 1 - one Point 0 0Point 0 1 * one Point 0 1Point 0.0 1.0 / one Point 0.0 1.0Point 11 12 `mod` (pure 6) Point 5 0H numhask-spacethe preferred patternI numhask-space2rotate a point by x degrees relative to the originJ numhask-space7Create Points for a formulae y = f(x) across an x rangeFGHIJFGHIJNone,/4567=>?@ADHMVbga^ ] numhask-spacea F of _C that form a rectangle in what is often thought of as the XY plane.let a = Rect (-1) 1 (-2) 4aRect -1 1 -2 4let (Ranges x y) = ax Range -1 1y Range -2 4fmap (+1) (Rect 1 2 3 4) Rect 2 3 4 5one :: Rect DoubleRect -0.5 0.5 -0.5 0.5zero :: Rect Double*Rect Infinity -Infinity Infinity -Infinityas a Field instanceRect 0 1 2 3 + zeroRect 0.0 1.0 2.0 3.0$Rect 0 1 (-2) (-1) + Rect 2 3 (-5) 3Rect 0.0 3.0 -5.0 3.0Rect 1 1 1 1 - oneRect 0.5 1.0 0.5 1.0Rect 0 1 0 1 * oneRect 0.0 1.0 0.0 1.0Rect 0 1 0 1 / oneRect 0.0 1.0 0.0 1.0(singleton (Point 1.0 2.0) :: Rect DoubleRect 1.0 1.0 2.0 2.0abs (Rect 1 0 1 0)Rect 0.0 1.0 0.0 1.0!sign (Rect 1 0 1 0) == negate oneTrueas a Space instance7project (Rect 0 1 (-1) 0) (Rect 1 4 10 0) (Point 0.5 1)Point 2.5 -10.0%gridSpace (Rect 0 10 0 1) (Point 2 2)W[Rect 0.0 5.0 0.0 0.5,Rect 0.0 5.0 0.5 1.0,Rect 5.0 10.0 0.0 0.5,Rect 5.0 10.0 0.5 1.0]'grid MidPos (Rect 0 10 0 1) (Point 2 2)=[Point 2.5 0.25,Point 2.5 0.75,Point 7.5 0.25,Point 7.5 0.75]_ numhask-spacepattern of Ranges xrange yrange` numhask-space+pattern of Rect lowerx upperx lowery upperya numhask-space[create a list of points representing the lower left and upper right corners of a rectangle.b numhask-space the 4 cornersc numhask-space+project a Rect from an old range to a new 1d numhask-spacesRect projection maths: some sort of affine projection lurking under the hood? > width one = one > mid zero = zeroi numhask-spaceXrotate the corners of a Rect by x degrees relative to the origin, and fold to a new Rcetj numhask-space6Create Rects for a formulae y = f(x) across an x rangek numhask-space8Create values c for Rects data for a formulae c = f(x,y)l numhask-spaceGconvert a ratio of x-plane : y-plane to a ViewBox with a height of one.m numhask-spaceconvert a Rect to a ratio]^_`abcdefghijklm]^`_abcdefghijklmNoneDbb4E  !"#$%&'()*/012FGHIJ]^_`abcdefghijklmNone "7>DXbded z numhask-space(whether to include lower and upper times} numhask-spacea step in time numhask-spaceparse text as per iso8601:set -XOverloadedStrings,let t0 = parseUTCTime ("2017-12-05" :: Text)t0Just 2017-12-05 00:00:00 UTC numhask-spaceadd a TimeGrain to a UTCTime:addGrain (Years 1) 5 (UTCTime (fromGregorian 2015 2 28) 0)2020-02-29 00:00:00 UTC;addGrain (Months 1) 1 (UTCTime (fromGregorian 2015 2 28) 0)2015-03-31 00:00:00 UTC:addGrain (Hours 6) 5 (UTCTime (fromGregorian 2015 2 28) 0)2015-03-01 06:00:00 UTCJaddGrain (Seconds 0.001) (60*1000+1) (UTCTime (fromGregorian 2015 2 28) 0)2015-02-28 00:01:00.001 UTC numhask-space0compute the floor UTCTime based on the timegrain9floorGrain (Years 5) (UTCTime (fromGregorian 1999 1 1) 0)1995-12-31 00:00:00 UTC<floorGrain (Months 3) (UTCTime (fromGregorian 2016 12 30) 0)2016-09-30 00:00:00 UTC:floorGrain (Days 5) (UTCTime (fromGregorian 2016 12 30) 1)2016-12-30 00:00:00 UTCTfloorGrain (Minutes 15) (UTCTime (fromGregorian 2016 12 30) (fromDouble' $ 15*60+1))2016-12-30 00:15:00 UTCBfloorGrain (Seconds 0.1) (UTCTime (fromGregorian 2016 12 30) 0.12)2016-12-30 00:00:00.1 UTC numhask-space2compute the ceiling UTCTime based on the timegrain;ceilingGrain (Years 5) (UTCTime (fromGregorian 1999 1 1) 0)2000-12-31 00:00:00 UTC>ceilingGrain (Months 3) (UTCTime (fromGregorian 2016 12 30) 0)2016-12-31 00:00:00 UTC<ceilingGrain (Days 5) (UTCTime (fromGregorian 2016 12 30) 1)2016-12-31 00:00:00 UTCVceilingGrain (Minutes 15) (UTCTime (fromGregorian 2016 12 30) (fromDouble' $ 15*60+1))2016-12-30 00:30:00 UTCDceilingGrain (Seconds 0.1) (UTCTime (fromGregorian 2016 12 30) 0.12)2016-12-30 00:00:00.2 UTC numhask-spacepdates attached to charts are often discontinuous, but we want to smooth that reality over and show a continuous range on the axis The assumption with getSensibleTimeGrid is that there is a list of discountinuous UTCTimes rather than a continuous range. Output is a list of index points for the original [UTCTime] and label tuples, and a list of unused list elements.placedTimeLabelDiscontinuous PosIncludeBoundaries (Just "%d %b") 2 [UTCTime (fromGregorian 2017 12 6) 0, UTCTime (fromGregorian 2017 12 29) 0, UTCTime (fromGregorian 2018 1 31) 0, UTCTime (fromGregorian 2018 3 3) 0]:([(0,"06 Dec"),(1,"31 Dec"),(2,"28 Feb"),(3,"03 Mar")],[]) numhask-space1compute a sensible TimeGrain and list of UTCTimeshsensibleTimeGrid InnerPos 2 (UTCTime (fromGregorian 2016 12 31) 0, UTCTime (fromGregorian 2017 12 31) 0)T(Months 6,[2016-12-31 00:00:00 UTC,2017-06-30 00:00:00 UTC,2017-12-31 00:00:00 UTC])fsensibleTimeGrid InnerPos 2 (UTCTime (fromGregorian 2017 1 1) 0, UTCTime (fromGregorian 2017 12 30) 0)$(Months 6,[2017-06-30 00:00:00 UTC])fsensibleTimeGrid UpperPos 2 (UTCTime (fromGregorian 2017 1 1) 0, UTCTime (fromGregorian 2017 12 30) 0)<(Months 6,[2017-06-30 00:00:00 UTC,2017-12-31 00:00:00 UTC])fsensibleTimeGrid LowerPos 2 (UTCTime (fromGregorian 2017 1 1) 0, UTCTime (fromGregorian 2017 12 30) 0)<(Months 6,[2016-12-31 00:00:00 UTC,2017-06-30 00:00:00 UTC]) numhask-space8come up with a sensible TimeGrain over a NominalDiffTimez{|}~}~z{|      !"#$%&'()*+,-./01234546789:;<=>?@ABCDEFGHIJKJLMNOPQRSTUVWXYZ[\]^_`ab`cdefghijklmnopqrstuvwxyz{|}~*numhask-space-0.2.0-Lufpc8Rnj2uGfgI0GjZHwyNumHask.Space.Types NumHask.Range NumHask.Point NumHask.RectNumHask.Space.Time NumHask.SpacePosOuterPosInnerPosLowerPosUpperPosMidPos FieldSpaceGridgrid gridSpace IntersectiongetIntersectionUniongetUnionSpaceElementlowerupper singleton intersectionunionnorm...>.<|.||>||<|containsdisjointmemberOfwidth+/-midprojectspace1monotoneepswidenwidenEpsscalemove$fSemigroupUnion$fSemigroupIntersection $fShowPos$fEqPosRangeRange' gridSensible $fNumRange$fSemigroupRange$fFieldSpaceRange $fSpaceRange$fLatticeRange$fRepresentableRange$fDistributiveRange$fTraversable1Range$fTraversableRange$fFoldable1Range$fFoldableRange$fApplicativeRange $fApplyRange$fFunctorRange $fShow1Range $fEq1Range $fShowRange $fEqRange$fGenericRangePointPoint'rotategridP$fLatticePoint$fRepresentablePoint$fDistributivePoint$fFractionalPoint $fNumPoint$fBoundedPoint $fMonoidPoint$fSemigroupPoint$fTraversablePoint$fFoldablePoint $fMonadPoint$fApplicativePoint $fShow1Point $fEq1Point$fFunctorPoint $fShowPoint $fEqPoint$fGenericPointRectRect'Rangescornerscorners4 projectRectaddRectmultRectunitRectfoldRectaddPoint rotateRectgridRgridFaspectratio$fFieldSpaceRect $fSpaceRect$fSemigroupRect$fRepresentableRect$fDistributiveRect $fShowRect$fEqRect $fFunctorRect$fApplicativeRect$fFoldableRect$fTraversableRect $fGenericRectPosDiscontinuous PosInnerOnlyPosIncludeBoundaries TimeGrainYearsMonthsDaysHoursMinutesSeconds parseUTCTime floorGrain ceilingGrainplacedTimeLabelDiscontinuoussensibleTimeGrid$fShowTimeGrain $fEqTimeGrain$fGenericTimeGrainaddGrainstepSensibleTime