File: Basic.hs

package info (click to toggle)
haskell-cryptocipher 0.3.5-1
  • links: PTS, VCS
  • area: main
  • in suites: wheezy
  • size: 256 kB
  • sloc: haskell: 2,916; ansic: 142; makefile: 3
file content (88 lines) | stat: -rw-r--r-- 2,933 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
{-# LANGUAGE BangPatterns #-}
-- |
-- Module      : Number.Basic
-- License     : BSD-style
-- Maintainer  : Vincent Hanquez <vincent@snarc.org>
-- Stability   : experimental
-- Portability : Good

module Number.Basic
	( sqrti
	, gcde
	, gcde_binary
	, areEven
	) where

import Data.Bits

-- | sqrti returns two integer (l,b) so that l <= sqrt i <= b
-- the implementation is quite naive, use an approximation for the first number
-- and use a dichotomy algorithm to compute the bound relatively efficiently.
sqrti :: Integer -> (Integer, Integer)
sqrti i
	| i < 0     = error "cannot compute negative square root"
	| i == 0    = (0,0)
	| i == 1    = (1,1)
	| i == 2    = (1,2)
	| otherwise = loop x0
		where
			nbdigits = length $ show i
			x0n = (if even nbdigits then nbdigits - 2 else nbdigits - 1) `div` 2
			x0  = if even nbdigits then 2 * 10 ^ x0n else 6 * 10 ^ x0n
			loop x = case compare (sq x) i of
				LT -> iterUp x
				EQ -> (x, x)
				GT -> iterDown x
			iterUp lb = if sq ub >= i then iter lb ub else iterUp ub
				where ub = lb * 2
			iterDown ub = if sq lb >= i then iterDown lb else iter lb ub
				where lb = ub `div` 2
			iter lb ub
				| lb == ub   = (lb, ub)
				| lb+1 == ub = (lb, ub)
				| otherwise  =
					let d = (ub - lb) `div` 2 in
					if sq (lb + d) >= i
						then iter lb (ub-d)
						else iter (lb+d) ub
			sq a = a * a

-- | get the extended GCD of two integer using integer divMod
gcde :: Integer -> Integer -> (Integer, Integer, Integer)
gcde a b = if d < 0 then (-x,-y,-d) else (x,y,d) where
	(d, x, y)                     = f (a,1,0) (b,0,1)
	f t              (0, _, _)    = t
	f (a', sa, ta) t@(b', sb, tb) =
		let (q, r) = a' `divMod` b' in
		f t (r, sa - (q * sb), ta - (q * tb))

-- | get the extended GCD of two integer using the extended binary algorithm (HAC 14.61)
-- get (x,y,d) where d = gcd(a,b) and x,y satisfying ax + by = d
gcde_binary :: Integer -> Integer -> (Integer, Integer, Integer)
gcde_binary a' b'
	| b' == 0   = (1,0,a')
	| a' >= b'  = compute a' b'
	| otherwise = (\(x,y,d) -> (y,x,d)) $ compute b' a'
	where
		getEvenMultiplier !g !x !y
			| areEven [x,y] = getEvenMultiplier (g `shiftL` 1) (x `shiftR` 1) (y `shiftR` 1)
			| otherwise     = (x,y,g)
		halfLoop !x !y !u !i !j
			| areEven [u,i,j] = halfLoop x y (u `shiftR` 1) (i `shiftR` 1) (j `shiftR` 1)
			| even u          = halfLoop x y (u `shiftR` 1) ((i + y) `shiftR` 1) ((j - x) `shiftR` 1)
			| otherwise       = (u, i, j)
		compute a b =
			let (x,y,g) = getEvenMultiplier 1 a b in
			loop g x y x y 1 0 0 1

		loop g _ _ 0  !v _  _  !c !d = (c, d, g * v)
		loop g x y !u !v !a !b !c !d =
			let (u2,a2,b2) = halfLoop x y u a b in
			let (v2,c2,d2) = halfLoop x y v c d in
			if u2 >= v2
				then loop g x y (u2 - v2) v2 (a2 - c2) (b2 - d2) c2 d2
				else loop g x y u2 (v2 - u2) a2 b2 (c2 - a2) (d2 - b2)

-- | check if a list of integer are all even
areEven :: [Integer] -> Bool
areEven = and . map even