module Math.NumberTheory.Utils.Hyperbola
( pointsUnderHyperbola
) where
import Data.Bits
import Math.NumberTheory.Powers.Cubes
pointsUnderHyperbola0 :: Int -> Int -> Int -> [Int]
pointsUnderHyperbola0 n lo hi
| n < 0 = error "pointsUnderHyperbola0: first argument must be non-negative"
| lo <= 0 = error "pointsUnderHyperbola0: second argument must be positive"
| otherwise = go hi
where
go x
| x < lo = []
| otherwise = n `quot` x : go (x 1)
data Bresenham = Bresenham
{ bresX :: !Int
, bresBeta :: !Int
, _bresGamma :: !Int
, _bresDelta1 :: !Int
, _bresEpsilon :: !Int
}
initBresenham :: Int -> Int -> Bresenham
initBresenham n x = Bresenham x beta gamma delta1 epsilon
where
beta = n `quot` x
epsilon = n `rem` x
delta1 = n `quot` (x 1) beta
gamma = beta (x 1) * delta1
stepBack :: Bresenham -> Bresenham
stepBack (Bresenham x' beta' gamma' delta1' epsilon') =
if eps >= x
then (if eps >= x `shiftL` 1
then
let delta1 = delta1' + 2 in (Bresenham x (beta' + delta1) (gamma' + delta1 `shiftL` 1 x `shiftL` 1) delta1 (eps x `shiftL` 1))
else
let delta1 = delta1' + 1 in (Bresenham x (beta' + delta1) (gamma' + delta1 `shiftL` 1 x) delta1 (eps x))
)
else (if eps >= 0
then
(Bresenham x (beta' + delta1') (gamma' + delta1' `shiftL` 1) delta1' eps)
else
let delta1 = delta1' 1 in (Bresenham x (beta' + delta1) (gamma' + delta1 `shiftL` 1 + x) delta1 (eps + x)))
where
x = x' 1
eps = epsilon' + gamma'
pointsUnderHyperbola :: Int -> Int -> Int -> [Int]
pointsUnderHyperbola n lo hi
| n < 0 = error "pointsUnderHyperbola: first argument must be non-negative"
| lo <= 0 = error "pointsUnderHyperbola: second argument must be positive"
| hi < lo = []
| hi == lo = [n `quot` lo]
| otherwise = go (initBresenham n hi)
where
mid = (integerCubeRoot (2 * n) + 1) `max` lo
go h
| bresX h < mid = pointsUnderHyperbola0 n lo ((mid 1) `min` hi)
| otherwise = bresBeta h : go (stepBack h)