music-pitch-1.7.2: src/Music/Pitch/Common/Interval.hs
{-# LANGUAGE GeneralizedNewtypeDeriving, StandaloneDeriving, TypeFamilies,
NoMonomorphismRestriction, DeriveDataTypeable #-}
------------------------------------------------------------------------------------
-- |
-- Copyright : (c) Hans Hoglund, Edward Lilley 2012–2014
--
-- License : BSD-style
--
-- Maintainer : hans@hanshoglund.se
-- Stability : experimental
-- Portability : non-portable (TF,GNTD)
--
-- Provides standard intervals.
--
-------------------------------------------------------------------------------------
module Music.Pitch.Common.Interval (
-- * Quality
Quality(..),
HasQuality(..),
invertQuality,
isPerfect,
isMajor,
isMinor,
isAugmented,
isDiminished,
-- ** Number
Number,
HasNumber(..),
unison,
prime,
second,
third,
fourth,
fifth,
sixth,
seventh,
octave,
ninth,
tenth,
twelfth,
duodecim,
thirteenth,
fourteenth,
fifteenth,
-- ** Intervals
Interval(..),
-- *** Creating intervals
mkInterval,
perfect,
major,
minor,
augmented,
diminished,
doublyAugmented,
doublyDiminished,
-- *** Inspecting intervals
isNegative,
isPositive,
isNonNegative,
isPerfectUnison,
isStep,
isLeap,
-- *** Simple and compound intervals
isSimple,
isCompound,
separate,
simple,
octaves,
-- *** Inversion
invert,
-- * Utility
asInterval,
-- * Basis values
IntervalBasis(..),
-- ** Converting basis
convertBasis,
convertBasisFloat,
intervalDiv,
-- ** Basis values (TODO cleanup)
basis_P1,
basis_A1,
basis_d2,
basis_P8,
basis_P5,
-- ** Utility
intervalDiff,
mkInterval',
) where
import Data.Maybe
import Data.Either
import Data.Semigroup
import Data.VectorSpace
-- import Data.AffineSpace
import Data.Basis
import Data.Typeable
import Control.Monad
import Control.Applicative
import qualified Data.List as List
import Music.Pitch.Absolute
import Music.Pitch.Augmentable
import Music.Pitch.Literal
import Music.Pitch.Common.Semitones
-- |
-- Interval quality is either perfect, major, minor, augmented, and
-- diminished. This representation allows for an arbitrary number of
-- augmentation or diminishions, so /augmented/ is represented by @Augmented
-- 1@, /doubly augmented/ by @Augmented 2@ and so on.
--
-- The quality of a compound interval is the quality of the simple interval on
-- which it is based.
--
-- Note that (Augmented 0) and (Diminished 0) are superfluous identity
-- values, use Perfect instead. Augmented and Diminished must also
-- take only positive arguments.
data Quality
= Major
| Minor
| Perfect
| Augmented Int
| Diminished Int
deriving (Eq, Ord, Show)
instance HasQuality Quality where
quality = id
-- | Augmentable Quality instance
--
-- This Augmentable instance exists solely for use of the getQuality
-- function, which ensures that there is never any ambiguity around
-- diminished/augmented intervals turning into major/minor/perfect
-- intervals.
instance Augmentable Quality where
augment Major = Augmented 1
augment Minor = Major
augment Perfect = Augmented 1
augment (Augmented n) = Augmented (n + 1)
augment (Diminished n) = Diminished (n - 1)
diminish Major = Minor
diminish Minor = Diminished 1
diminish Perfect = Diminished 1
diminish (Augmented n) = Augmented (n - 1)
diminish (Diminished n) = Diminished (n + 1)
class HasQuality a where
quality :: a -> Quality
-- |
-- Invert a quality.
--
-- Perfect is unaffected, major becomes minor and vice versa, augmented
-- becomes diminished and vice versa.
--
invertQuality :: Quality -> Quality
invertQuality = go
where
go Major = Minor
go Minor = Major
go Perfect = Perfect
go (Augmented n) = Diminished n
go (Diminished n) = Augmented n
-- |
-- Returns whether the given quality is perfect.
--
isPerfect :: HasQuality a => a -> Bool
isPerfect a = case quality a of { Perfect -> True ; _ -> False }
-- |
-- Returns whether the given quality is major.
--
isMajor :: HasQuality a => a -> Bool
isMajor a = case quality a of { Major -> True ; _ -> False }
-- |
-- Returns whether the given quality is minor.
--
isMinor :: HasQuality a => a -> Bool
isMinor a = case quality a of { Minor -> True ; _ -> False }
-- |
-- Returns whether the given quality is /augmented/ (including double augmented etc).
--
isAugmented :: HasQuality a => a -> Bool
isAugmented a = case quality a of { Augmented _ -> True ; _ -> False }
-- |
-- Returns whether the given quality is /diminished/ (including double diminished etc).
--
isDiminished :: HasQuality a => a -> Bool
isDiminished a = case quality a of { Diminished _ -> True ; _ -> False }
-- |
-- The number portion of an interval (i.e. second, third, etc).
--
-- Note that the interval number is always one step larger than number of steps spanned by
-- the interval (i.e. a third spans two diatonic steps). Thus 'number' does not distribute
-- over addition:
--
-- > number (a + b) = number a + number b - 1
--
newtype Number = Number { getNumber :: Int }
deriving (Eq, Ord, Num, Enum, Real, Integral)
instance Show Number where { show = show . getNumber }
instance HasNumber Number where number = id
-- | A synonym for @1@.
unison :: Number
unison = 1
-- | A synonym for @1@.
prime :: Number
prime = 1
-- | A synonym for @2@.
second :: Number
second = 2
-- | A synonym for @3@.
third :: Number
third = 3
-- | A synonym for @4@.
fourth :: Number
fourth = 4
-- | A synonym for @5@.
fifth :: Number
fifth = 5
-- | A synonym for @6@.
sixth :: Number
sixth = 6
-- | A synonym for @7@.
seventh :: Number
seventh = 7
-- | A synonym for @8@.
octave :: Number
octave = 8
-- | A synonym for @9@.
ninth :: Number
ninth = 9
-- | A synonym for @10@.
tenth :: Number
tenth = 10
-- | A synonym for @11@.
eleventh :: Number
eleventh = 11
-- | A synonym for @12@.
twelfth :: Number
twelfth = 12
-- | A synonym for @12@.
duodecim :: Number
duodecim = 12
-- | A synonym for @13@.
thirteenth :: Number
thirteenth = 13
-- | A synonym for @14@.
fourteenth :: Number
fourteenth = 14
-- | A synonym for @15@.
fifteenth :: Number
fifteenth = 15
class HasNumber a where
-- |
-- Returns the number portion of an interval.
--
-- The interval number is negative if and only if the interval is negative.
--
-- See also 'quality', 'octaves' and 'semitones'.
--
number :: a -> Number
-- |
-- An interval is the difference between two pitches, incuding negative
-- intervals.
--
-- Intervals and pitches can be added using '.+^'. To get the interval between
-- two pitches, use '.-.'.
--
-- > c .+^ minor third == eb
-- > f .-. c == perfect fourth
--
-- Adding intervals preserves spelling. For example:
--
-- > m3 ^+^ _M3 = _P5
-- > d5 ^+^ _M6 = m10
--
-- The scalar type of 'Interval' is 'Int', using '^*' to stack intervals of a certain
-- type on top of each other. For example @_P5 ^* 2@ is a stack of 2 perfect fifths, or a
-- major ninth. The 'Num' instance works as expected for '+', 'negate' and 'abs', and
-- (arbitrarily) uses minor seconds for multiplication. If you find yourself '*', or
-- 'signum' on intervals, consider switching to '*^' or 'normalized'.
--
-- Intervals are generally described in terms of 'Quality' and 'Number'. To
-- construct an interval, use the 'interval' constructor, the utility
-- constructors 'major', 'minor', 'augmented' and 'diminished', or the
-- interval literals:
--
-- > m5 == minor fifth == interval Minor 5
-- > _P4 == perfect fourth == interval Perfect 5
-- > d5 == diminished fifth == diminish (perfect fifth)
--
newtype Interval = Interval { getInterval :: (
Int, -- number of A1, i.e. chromatic steps
Int -- number of d2, i.e. diatonic steps
) }
deriving (Eq, Ord, Typeable)
-- | Avoid using '(*)', or 'signum' on intervals.
instance Num Interval where
(+) = addInterval
negate = negateInterval
abs a = if isNegative a then negate a else a
(*) = error "Music.Pitch.Common.Interval: no overloading for (*)"
signum = error "Music.Pitch.Common.Interval: no overloading for signum"
fromInteger = error "Music.Pitch.Common.Interval: no overloading for fromInteger"
instance Show Interval where
show a
| isNegative a = "-" ++ showQuality (extractQuality a) ++ show (abs $ extractNumber a)
| otherwise = showQuality (extractQuality a) ++ show (abs $ extractNumber a)
where
showQuality Major = "_M"
showQuality Minor = "m"
showQuality Perfect = "_P"
showQuality (Augmented n) = "_" ++ replicate' n 'A'
showQuality (Diminished n) = replicate' n 'd'
instance Semigroup Interval where
(<>) = addInterval
instance Monoid Interval where
mempty = perfect unison
mappend = addInterval
instance AdditiveGroup Interval where
zeroV = perfect unison
(^+^) = addInterval
negateV = negateInterval
instance VectorSpace Interval where
type Scalar Interval = Integer
(*^) = stackInterval
-- TODO move
data IntervalBasis = Chromatic | Diatonic
deriving (Eq, Ord, Show, Enum)
instance HasBasis Interval where
type Basis Interval = IntervalBasis
basisValue Chromatic = basis_A1
basisValue Diatonic = basis_d2
decompose (Interval (c,d)) = [(Chromatic, fromIntegral c), (Diatonic, fromIntegral d)]
decompose' (Interval (c,d)) Chromatic = fromIntegral c
decompose' (Interval (c,d)) Diatonic = fromIntegral d
instance HasQuality Interval where
quality i = extractQuality i
instance HasNumber Interval where
number i = extractNumber i
instance Augmentable Interval where
augment i = i ^+^ basis_A1
diminish i = i ^-^ basis_A1
instance HasSemitones Interval where
semitones (Interval (a, d)) = fromIntegral a -- assuming "semitone" == A1
instance IsInterval Interval where
fromInterval (IntervalL (o,d,c)) = (basis_P8^*o) ^+^ (basis_A1^*c) ^+^ (basis_d2^*d)
negateInterval :: Interval -> Interval
negateInterval (Interval (a, d)) = Interval (-a, -d)
addInterval :: Interval -> Interval -> Interval
addInterval (Interval (a1, d1)) (Interval (a2, d2)) = Interval (a1 + a2, d1 + d2)
stackInterval :: Integer -> Interval -> Interval
stackInterval n a | n >= 0 = mconcat $ replicate (fromIntegral n) a
| otherwise = negate $ stackInterval (negate n) a
intervalDiff :: Interval -> Int
intervalDiff (Interval (c, d)) = c - diatonicToChromatic d
-- |
-- Creates an interval from a quality and number.
--
-- Given 'Perfect' with an number not indicating a perfect consonant, 'interval' returns a
-- major interval instead. Given 'Major' or 'Minor' with a number indicating a perfect
-- consonance, 'interval' returns a perfect or diminished interval respectively.
--
mkInterval'
:: Int -- ^ Difference in chromatic steps (?).
-> Int -- ^ Number of diatonic steps (NOT interval number).
-> Interval
mkInterval' diff diatonic = Interval (diatonicToChromatic diatonic + diff, diatonic)
basis_P1 = Interval (0, 0)
basis_A1 = Interval (1, 0)
basis_d2 = Interval (0, 1)
basis_P5 = Interval (7, 4)
basis_P8 = Interval (12, 7)
mkInterval :: Quality -> Number -> Interval
-- our identity:
mkInterval Perfect 1 = basis_P1
-- and our two basis vectors:
mkInterval (Augmented 1) 1 = basis_A1
mkInterval (Diminished 1) 2 = basis_d2
mkInterval Minor 2 = basis_d2 ^+^ basis_A1
mkInterval Major 2 = (mkInterval Minor 2) ^+^ basis_A1
mkInterval (Augmented 1) 2 = (mkInterval Major 2) ^+^ basis_A1
mkInterval (Diminished 1) 3 = (mkInterval Minor 3) ^-^ basis_A1
mkInterval Minor 3 = (mkInterval Major 2) ^+^ (mkInterval Minor 2)
mkInterval Major 3 = (mkInterval Major 2) ^+^ (mkInterval Major 2)
mkInterval (Augmented 1) 3 = (mkInterval Major 3) ^+^ basis_A1
mkInterval (Diminished 1) 4 = (mkInterval Perfect 4) ^-^ basis_A1
mkInterval Perfect 4 = (mkInterval Major 3) ^+^ (mkInterval Minor 2)
mkInterval (Augmented 1) 4 = (mkInterval Perfect 4) ^+^ basis_A1
mkInterval (Diminished 1) 5 = (mkInterval Perfect 5) ^-^ basis_A1
mkInterval Perfect 5 = (mkInterval Perfect 4) ^+^ (mkInterval Major 2)
mkInterval (Augmented 1) 5 = (mkInterval Perfect 5) ^+^ basis_A1
mkInterval (Diminished 1) 6 = (mkInterval Minor 6) ^-^ basis_A1
mkInterval Minor 6 = (mkInterval Perfect 5) ^+^ (mkInterval Minor 2)
mkInterval Major 6 = (mkInterval Perfect 5) ^+^ (mkInterval Major 2)
mkInterval (Augmented 1) 6 = (mkInterval Major 6) ^+^ basis_A1
mkInterval (Diminished 1) 7 = (mkInterval Minor 7) ^-^ basis_A1
mkInterval Minor 7 = (mkInterval Major 6) ^+^ (mkInterval Minor 2)
mkInterval Major 7 = (mkInterval Major 6) ^+^ (mkInterval Major 2)
mkInterval (Augmented 1) 7 = (mkInterval Major 7) ^+^ basis_A1
mkInterval Minor 1 = error "invalid interval"
mkInterval Major 1 = error "invalid interval"
mkInterval Perfect 2 = error "invalid interval"
mkInterval Perfect 3 = error "invalid interval"
mkInterval Minor 4 = error "invalid interval"
mkInterval Major 4 = error "invalid interval"
mkInterval Minor 5 = error "invalid interval"
mkInterval Major 5 = error "invalid interval"
mkInterval Perfect 6 = error "invalid interval"
mkInterval Perfect 7 = error "invalid interval"
mkInterval (Diminished 0) n = error "(Diminished 0) is not a valid Quality"
mkInterval (Augmented 0) n = error "(Augmented 0) is not a valid Quality"
mkInterval (Diminished q) n = (mkInterval (Diminished (q - 1)) n) ^-^ basis_A1
mkInterval (Augmented q) n = (mkInterval (Diminished (q - 1)) n) ^+^ basis_A1
mkInterval q (Number n) = if n > 0
then (mkInterval q (Number (n - 7))) ^+^ basis_P8
else (mkInterval q (Number (n + 7))) ^-^ basis_P8
-- |
-- Extracting the 'number' from an interval vector.
--
-- Note that (a, d) is a representation of the interval (a * A1) + (d
-- * d2), so the 'number' part of the interval must be stored entirely
-- in the d * d2 part (adding a unison, perfect or otherwise, can
-- never increase the number of the interval)
--
extractNumber :: Interval -> Number
extractNumber (Interval (a, d))
| d >= 0 = Number (d + 1)
| otherwise = Number (d - 1)
-- |
-- Extracting the 'quality' from an interval vector.
--
-- This is much more finicky, as the A1 and d2 intervals interact in a
-- complex way to produce the perfect/major/minor/etc. intervals that
-- we are used to reading.
extractQuality :: Interval -> Quality
extractQuality (Interval (a, d))
| (a < 0) && (d == 0) = diminish (extractQuality (Interval ((a + 1), d)))
| (a, d) == (0, 0) = Perfect
| (a > 0) && (d == 0) = augment (extractQuality (Interval ((a - 1), d)))
| (a < 1) && (d == 1) = diminish (extractQuality (Interval ((a + 1), d)))
| (a, d) == (1, 1) = Minor
| (a, d) == (2, 1) = Major
| (a > 2) && (d == 1) = augment (extractQuality (Interval ((a - 1), d)))
| (a < 3) && (d == 2) = diminish (extractQuality (Interval ((a + 1), d)))
| (a, d) == (3, 2) = Minor
| (a, d) == (4, 2) = Major
| (a > 4) && (d == 2) = augment (extractQuality (Interval ((a - 1), d)))
| (a < 5) && (d == 3) = diminish (extractQuality (Interval ((a + 1), d)))
| (a, d) == (5, 3) = Perfect
| (a > 5) && (d == 3) = augment (extractQuality (Interval ((a - 1), d)))
| (a < 7) && (d == 4) = diminish (extractQuality (Interval ((a + 1), d)))
| (a, d) == (7, 4) = Perfect
| (a > 7) && (d == 4) = augment (extractQuality (Interval ((a - 1), d)))
| (a < 8) && (d == 5) = diminish (extractQuality (Interval ((a + 1), d)))
| (a, d) == (8, 5) = Minor
| (a, d) == (9, 5) = Major
| (a > 9) && (d == 5) = augment (extractQuality (Interval ((a - 1), d)))
| (a < 10) && (d == 6) = diminish (extractQuality (Interval ((a + 1), d)))
| (a, d) == (10, 6) = Minor
| (a, d) == (11, 6) = Major
| (a > 11) && (d == 6) = augment (extractQuality (Interval ((a - 1), d)))
| (a < 12) && (d == 7) = diminish (extractQuality (Interval ((a + 1), d)))
| (a, d) == (12, 7) = Perfect
| (a > 12) && (d == 7) = augment (extractQuality (Interval ((a - 1), d)))
-- note: these last two cases *have* to be this way round, otherwise
-- infinite loop occurs.
| (a > 12) || (d > 7) = extractQuality (Interval ((a - 12), (d - 7)))
| (a < 0) || (d < 0) = extractQuality (Interval ((-a), (-d)))
-- | Creates a perfect interval.
-- If given an inperfect number, constructs a major interval.
perfect :: Number -> Interval
perfect = mkInterval Perfect
-- | Creates a major interval.
-- If given a perfect number, constructs a perfect interval.
major :: Number -> Interval
major = mkInterval Major
-- | Creates a minor interval.
-- If given a perfect number, constructs a diminished interval.
minor :: Number -> Interval
minor = mkInterval Minor
-- | Creates an augmented interval.
augmented :: Number -> Interval
augmented = mkInterval (Augmented 1)
-- | Creates a diminished interval.
diminished :: Number -> Interval
diminished = mkInterval (Diminished 1)
-- | Creates a doubly augmented interval.
doublyAugmented :: Number -> Interval
doublyAugmented = mkInterval (Augmented 2)
-- | Creates a doubly diminished interval.
doublyDiminished :: Number -> Interval
doublyDiminished = mkInterval (Diminished 2)
{-
Prelude Music.Prelude> separate (2*^_P8+m3)
(2,m3)
Prelude Music.Prelude>
Prelude Music.Prelude> separate (3*^_P8+m3)
(3,m3)
Prelude Music.Prelude>
Prelude Music.Prelude> separate (0*^_P8+m3)
(0,m3)
Prelude Music.Prelude> separate ((-1)*^_P8+m3)
-}
-- |
-- Separate a compound interval into octaves and a simple interval.
--
-- > (perfect octave)^*x + y = z iff (x, y) = separate z
--
separate :: Interval -> (Octaves, Interval)
separate i = (fromIntegral o, i ^-^ (fromIntegral o *^ basis_P8))
where
o = octaves i
-- |
-- Returns the non-simple part of an interval.
--
-- > _P8^*octaves x ^+^ simple x = x
--
octaves :: Interval -> Octaves
octaves i
| isNegative i && not (isOctaveMultiple i) = negate (octaves' i) - 1
| isNegative i && isOctaveMultiple i = negate (octaves' i)
| otherwise = octaves' i
isOctaveMultiple (Interval (_,d)) = d `mod` 7 == 0
octaves' i = fromIntegral $ intervalDiv i basis_P8
-- |
-- Returns the simple part of an interval.
--
-- > (perfect octave)^*x + y = z iff y = simple z
--
simple :: Interval -> Interval
simple = snd . separate
-- |
-- Returns whether the given interval is simple.
--
-- A simple interval is a non-negative interval spanning less than one octave.
--
isSimple :: Interval -> Bool
isSimple x = octaves x == 0
-- |
-- Returns whether the given interval is compound.
--
-- A compound interval is either a negative interval, or a positive interval spanning
-- one octave or more.
--
isCompound :: Interval -> Bool
isCompound x = octaves x /= 0
-- |
-- Returns whether the given interval is negative.
--
isNegative :: Interval -> Bool
isNegative (Interval (a, d)) = d < 0
-- |
-- Returns whether the given interval is positive.
--
isPositive :: Interval -> Bool
isPositive x@(Interval (a, d)) = d >= 0 && not (isPerfectUnison x)
-- |
-- Returns whether the given interval is non-negative. This implies that it is either positive or a perfect unison.
--
isNonNegative :: Interval -> Bool
isNonNegative (Interval (a, d)) = d >= 0
-- |
-- Returns whether the given interval a perfect unison.
--
isPerfectUnison :: Interval -> Bool
isPerfectUnison = (== perfect unison)
-- |
-- Returns whether the given interval is a step (a second or smaller).
--
-- Only diatonic 'number' is taken into account, so @_A2@ is considered
-- a step and @m3@ a leap, even though they have the same number of
-- semitones.
--
isStep :: Interval -> Bool
isStep (Interval (a, d)) = (abs d) <= 2
-- |
-- Returns whether the given interval is a leap (larger than a second).
--
-- Only the diatonic 'number' is taken into account, so @_A2@ is considered
-- a step and @m3@ a leap, even though they have the same number of
-- semitones.
--
isLeap :: Interval -> Bool
isLeap (Interval (a, d)) = (abs d) > 2
-- |
-- Intervallic inversion.
--
-- The inversion an interval is determined as follows:
--
-- * The number of a simple interval the difference of nine and the number of its inversion.
--
-- * The quality of a simple interval is the inversion of the quality of its inversion.
--
-- * The inversion of a compound interval is the inversion of its simple component.
--
invert :: Interval -> Interval
invert = simple . negate
-- |
-- This is just the identity function, but is useful to fix the type of 'Interval'.
--
asInterval :: Interval -> Interval
asInterval = id
{-
isPerfectNumber :: Int -> Bool
isPerfectNumber 0 = True
isPerfectNumber 1 = False
isPerfectNumber 2 = False
isPerfectNumber 3 = True
isPerfectNumber 4 = True
isPerfectNumber 5 = False
isPerfectNumber 6 = False
-}
-- TODO more generic pattern here
diatonicToChromatic :: Int -> Int
diatonicToChromatic d = (octaves*12) + go restDia
where
-- restDia is always in [0..6]
(octaves, restDia) = d `divMod` 7
go = ([0,2,4,5,7,9,11] !!)
-- {-# DEPRECATED intervalDiff "This should be hidden" #-}
-- {-# DEPRECATED mkInterval' "This should be hidden "#-}
replicate' n = replicate (fromIntegral n)
-- | Integer div of intervals: i / di = x, where x is an integer
intervalDiv :: Interval -> Interval -> Int
intervalDiv (Interval (a, d)) (Interval (1, 0)) = a
intervalDiv (Interval (a, d)) (Interval (0, 1)) = d
intervalDiv i di
| (i > basis_P1) = intervalDivPos i di
| (i < basis_P1) = intervalDivNeg i di
| otherwise = 0 :: Int
where
intervalDivPos i di
| (i < basis_P1) = undefined
| (i ^-^ di) < basis_P1 = 0
| otherwise = 1 + (intervalDiv (i ^-^ di) di)
intervalDivNeg i di
| (i > basis_P1) = undefined
| (i ^+^ di) > basis_P1 = 0
| otherwise = 1 + (intervalDiv (i ^+^ di) di)
-- | Represent an interval i in a new basis (j, k).
--
-- We want x,y where i = x*j + y*k
--
-- e.g., convertBasis basis_d2 _P5 basis_P8 == Just (-12,7), as expected.
convertBasis
:: Interval
-> Interval
-> Interval
-> Maybe (Int, Int)
convertBasis i j k
| (p == 0) = Nothing
| not $ p `divides` r = Nothing
| not $ p `divides` q = Nothing
| otherwise = Just (r `div` p, q `div` p)
where Interval (m, n) = i
Interval (a, b) = j
Interval (c, d) = k
p = (a*d - b*c)
q = (a*n - b*m)
r = (d*m - c*n)
-- | Same as above, but don't worry if new interval has non-integer
-- coefficients -- useful when getting a value to use as a frequency
-- ratio in a tuning system.
convertBasisFloat :: (Fractional t, Eq t)
=> Interval
-> Interval
-> Interval
-> Maybe (t, t)
convertBasisFloat i j k
| (p == 0) = Nothing
| otherwise = Just (r / p, q / p)
where Interval (m, n) = i
Interval (a, b) = j
Interval (c, d) = k
p = fromIntegral $ (a*d - b*c)
q = fromIntegral $ (a*n - b*m)
r = fromIntegral $ (d*m - c*n)
divides :: Integral a => a -> a -> Bool
x `divides` y = (y `rem` x) == 0