packages feed

elliptic-curve-0.1.0: src/Curve/Weierstrass.hs

module Curve.Weierstrass
  ( Point(..)
  , WCurve(..)
  , WPoint
  ) where

import Protolude

import Control.Monad.Random (Random(..), RandomGen, getRandom)
import ExtensionField (ExtensionField, IrreducibleMonic)
import GaloisField (GaloisField(..))
import PrimeField (PrimeField)
import Test.Tasty.QuickCheck (Arbitrary(..), Gen, suchThatMap)
import Text.PrettyPrint.Leijen.Text (Pretty(..))

import Curve (Curve(..))

-------------------------------------------------------------------------------
-- Types
-------------------------------------------------------------------------------

-- | Weierstrass curve representation.
data W

-- | Weierstrass curve points.
type WPoint = Point W

-- | Weierstrass curves @y^2 = x^3 + Ax + B@.
class Curve W c k => WCurve c k where
  a_ :: c -> k     -- ^ Coefficient @A@.
  b_ :: c -> k     -- ^ Coefficient @B@.
  g_ :: WPoint c k -- ^ Curve generator.

-- Weierstrass points are arbitrary.
instance (GaloisField k, IrreducibleMonic k im, WCurve c (ExtensionField k im))
  => Arbitrary (Point W c (ExtensionField k im)) where
  arbitrary = flip mul g_ <$> (arbitrary :: Gen Int) -- TODO
instance (KnownNat p, WCurve c (PrimeField p))
  => Arbitrary (Point W c (PrimeField p)) where
  arbitrary = suchThatMap arbitrary point

-- Weierstrass points are pretty.
instance (GaloisField k, WCurve c k) => Pretty (Point W c k) where
  pretty (A x y) = pretty (x, y)
  pretty O       = "O"

-- Weierstrass points are random.
instance (GaloisField k, WCurve c k) => Random (Point W c k) where
  random = first (flip mul g_) . (random :: RandomGen g => g -> (Int, g)) -- TODO
  -- random g = case point x of
  --   Just p -> (p, g')
  --   _      -> random g'
  --   where
  --     (x, g') = random g
  {-# INLINE random #-}
  randomR  = panic "not implemented."

-------------------------------------------------------------------------------
-- Operations
-------------------------------------------------------------------------------

-- Weierstrass curves are elliptic curves.
instance (GaloisField k, WCurve c k) => Curve W c k where

  data instance Point W c k = A k k -- ^ Affine point.
                            | O     -- ^ Infinite point.
    deriving (Eq, Generic, NFData, Read, Show)

  id = O
  {-# INLINE id #-}

  inv O       = O
  inv (A x y) = A x (-y)
  {-# INLINE inv #-}

  add p O          = p
  add O q          = q
  add p@(A x1 y1) (A x2 y2)
    | x1 /= x2     = A x3 y3
    | y1 + y2 == 0 = O
    | otherwise    = double p
    where
      l  = (y1 - y2) / (x1 - x2)
      x3 = l * l - x1 - x2
      y3 = l * (x1 - x3) - y1
  {-# INLINE add #-}

  double O       = O
  double (A _ 0) = O
  double (A x y) = A x' y'
    where
      l  = (3 * x * x + a_ (witness :: c)) / (2 * y)
      x' = l * l - 2 * x
      y' = l * (x - x') - y
  {-# INLINE double #-}

  def O       = True
  def (A x y) = y * y == (x * x + a) * x + b
    where
      a = a_ (witness :: c)
      b = b_ (witness :: c)
  {-# INLINE def #-}

  disc _ = 4 * a * a * a + 27 * b * b
    where
      a = a_ (witness :: c)
      b = b_ (witness :: c)
  {-# INLINE disc #-}

  point x = A x <$> sr (((x * x + a) * x) + b)
    where
      a = a_ (witness :: c)
      b = b_ (witness :: c)
  {-# INLINE point #-}

  rnd = getRandom
  {-# INLINE rnd #-}