-- |
-- Module      : Math.Diophantine
-- Copyright   : Joe Jevnik 2013
--
-- License     : GPL-2
-- Maintainer  : joejev@gmail.org
-- Stability   : stable
-- Portability : GHC
--
-- A module for solving quadratic diophantine equations.

module Math.Diophantine
    (
    -- * Data
      Equation(GeneralEquation) -- instances: Show
    , Solution(..)              -- instances: Eq, Show
    , Z
    , SolveError(..)            -- Instances: Show
    -- * Utilities
    , readEquation              -- :: String -> Equation
    , specializeEquation        -- :: Equation -> Equation
    , toMaybeList               -- :: Solution -> Maybe [(Integer,Integer)]
    , mergeSolutions            -- :: Solution -> Solution -> Solution
    -- * Equation Solving
    , solve                     -- :: Equation -> Solution
    ) where

import Math.Diophantine.Internal
import Math.Diophantine.Parser

-- -------------------------------------------------------------------------- --
-- Data types.

-- | A way to report an error in solving.
data SolveError = SolveError ReadError -- ^ Represents a read error when reading
                                       -- the equation from a string.
                | HyperbolicError      -- ^ The error when you try to solve a
                                       -- hyperbolic equation.
                  deriving Show

-- -------------------------------------------------------------------------- --
-- Exported functions.

-- | Extracts the list of solution pairs from a 'Solution'.
toMaybeList :: Solution -> Maybe [(Z,Z)]
toMaybeList (SolutionSet ns) = Just ns
toMaybeList _                = Nothing

-- | Determines what type of equation to solve for, and then calls the
-- appropriate solve function. Example:
--
-- >>> solve (GeneralEquation 1 2 3 3 5 0)
-- [(-3,0),(-2,-1),(0,0),(1,-1)]
solve :: Equation -> Either SolveError Solution
solve e = case specializeEquation e of
              e@(LinearEquation{})           -> Right $ solveLinear           e
              e@(SimpleHyperbolicEquation{}) -> Right $ solveSimpleHyperbolic e
              e@(ElipticalEquation{})        -> Right $ solveEliptical        e
              e@(ParabolicEquation{})        -> Right $ solveParabolic        e
              e@(HyperbolicEquation{})       -> Left HyperbolicError

-- | Read an 'Equation' out of a 'String', and then solve it.
solveString :: String -> Either SolveError Solution
solveString str = case readEquation str of
                      Right eq -> solve eq
                      Left e   -> Left $ SolveError e