{-# LANGUAGE CPP #-}
{-# LANGUAGE LambdaCase #-}
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'
{-# INLINE stepBack #-}
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)