module IEEE754.Range where

open import IEEE754.Prelude
open import IEEE754.Sign
open import IEEE754.Format
open import IEEE754.BitVec
open import IEEE754.Exact
open import IEEE754.Representation
open import IEEE754.FiniteSpec
open import IEEE754.FiniteDecode
open import IEEE754.Representable
open import IEEE754.RationalOrder

import Cubical.Data.Int as ℤ
import Cubical.Data.Nat.Order as ℕOrder
import Cubical.Data.Rationals as ℚ

leastPositiveBits : (n : ℕ) → BitVec (suc n)
leastPositiveBits zero = bit1 ∷ []
leastPositiveBits (suc n) = bit0 ∷ leastPositiveBits n

leastPositiveBits-value :
  (n : ℕ) →
  bitsToℕ (leastPositiveBits n) ≡ 1
leastPositiveBits-value zero = refl
leastPositiveBits-value (suc n) = leastPositiveBits-value n

leastPositiveBits-positive :
  (n : ℕ) →
  0 ℕOrder.< bitsToℕ (leastPositiveBits n)
leastPositiveBits-positive n =
  subst
    (0 ℕOrder.<_)
    (sym (leastPositiveBits-value n))
    (0 , refl)

leastPositiveBits-notAllOnes :
  (n : ℕ) →
  Bool→Type (not (allOnes (leastPositiveBits (suc n))))
leastPositiveBits-notAllOnes n = tt

leastPositiveBits-belowReserved :
  (n : ℕ) →
  suc (bitsToℕ (leastPositiveBits (suc n))) ℕOrder.<
  2 ^ suc (suc n)
leastPositiveBits-belowReserved n =
  notAllOnes→below-reserved
    (leastPositiveBits (suc n))
    (leastPositiveBits-notAllOnes n)

maxFiniteExponentBits : (n : ℕ) → BitVec (suc n)
maxFiniteExponentBits zero = bit0 ∷ []
maxFiniteExponentBits (suc n) = bit1 ∷ maxFiniteExponentBits n

maxFiniteExponentBits-notAllOnes :
  (n : ℕ) →
  Bool→Type (not (allOnes (maxFiniteExponentBits n)))
maxFiniteExponentBits-notAllOnes zero = tt
maxFiniteExponentBits-notAllOnes (suc n) =
  maxFiniteExponentBits-notAllOnes n

maxFiniteExponentBits-notAllZeros :
  (n : ℕ) →
  Bool→Type (not (allZeros (maxFiniteExponentBits (suc n))))
maxFiniteExponentBits-notAllZeros n = tt

maxFiniteExponentBits-positive :
  (n : ℕ) →
  0 ℕOrder.< bitsToℕ (maxFiniteExponentBits (suc n))
maxFiniteExponentBits-positive n =
  notAllZeros→positive
    (maxFiniteExponentBits (suc n))
    (maxFiniteExponentBits-notAllZeros n)

allOnesBits-notAllZeros :
  (n : ℕ) →
  Bool→Type (not (allZeros (replicate {n = suc n} bit1)))
allOnesBits-notAllZeros n = tt

allOnesBits-positive :
  (n : ℕ) →
  0 ℕOrder.< bitsToℕ (replicate {n = suc n} bit1)
allOnesBits-positive n =
  notAllZeros→positive
    (replicate {n = suc n} bit1)
    (allOnesBits-notAllZeros n)

maxFiniteExponentBits-belowReserved :
  (n : ℕ) →
  suc (bitsToℕ (maxFiniteExponentBits n)) ℕOrder.<
  2 ^ suc n
maxFiniteExponentBits-belowReserved n =
  notAllOnes→below-reserved
    (maxFiniteExponentBits n)
    (maxFiniteExponentBits-notAllOnes n)

record NondegenerateExponentRange
  (F : BinaryInterchangeFormat) : Type₀ where
  constructor nondegenerate-exponent-range
  field
    exponentTail : ℕ
    exponentBitsMinusOne≡suc :
      BinaryInterchangeFormat.exponentBitsMinusOne F ≡ suc exponentTail

binary16-nondegenerateExponentRange : NondegenerateExponentRange binary16
binary16-nondegenerateExponentRange = nondegenerate-exponent-range 3 refl

binary32-nondegenerateExponentRange : NondegenerateExponentRange binary32
binary32-nondegenerateExponentRange = nondegenerate-exponent-range 6 refl

binary64-nondegenerateExponentRange : NondegenerateExponentRange binary64
binary64-nondegenerateExponentRange = nondegenerate-exponent-range 9 refl

binary128-nondegenerateExponentRange : NondegenerateExponentRange binary128
binary128-nondegenerateExponentRange = nondegenerate-exponent-range 13 refl

record FiniteRangeFormat (F : BinaryInterchangeFormat) : Type₀ where
  constructor finite-range-format
  field
    positiveBias : PositiveExponentBias F
    nondegenerateExponentRange : NondegenerateExponentRange F

binary16-finiteRangeFormat : FiniteRangeFormat binary16
binary16-finiteRangeFormat =
  finite-range-format
    binary16-positiveExponentBias
    binary16-nondegenerateExponentRange

binary32-finiteRangeFormat : FiniteRangeFormat binary32
binary32-finiteRangeFormat =
  finite-range-format
    binary32-positiveExponentBias
    binary32-nondegenerateExponentRange

binary64-finiteRangeFormat : FiniteRangeFormat binary64
binary64-finiteRangeFormat =
  finite-range-format
    binary64-positiveExponentBias
    binary64-nondegenerateExponentRange

binary128-finiteRangeFormat : FiniteRangeFormat binary128
binary128-finiteRangeFormat =
  finite-range-format
    binary128-positiveExponentBias
    binary128-nondegenerateExponentRange

leastSubnormalTrailing :
  (F : BinaryInterchangeFormat) →
  BitVec (trailingSignificandBits F)
leastSubnormalTrailing F =
  leastPositiveBits
    (BinaryInterchangeFormat.trailingSignificandBitsMinusOne F)

zeroTrailing :
  (F : BinaryInterchangeFormat) →
  BitVec (trailingSignificandBits F)
zeroTrailing F = replicate {n = trailingSignificandBits F} bit0

maxTrailing :
  (F : BinaryInterchangeFormat) →
  BitVec (trailingSignificandBits F)
maxTrailing F = replicate {n = trailingSignificandBits F} bit1

leastNormalExponent :
  (F : BinaryInterchangeFormat) →
  BitVec (exponentWidth F)
leastNormalExponent F =
  leastPositiveBits
    (BinaryInterchangeFormat.exponentBitsMinusOne F)

maxFiniteExponent :
  (F : BinaryInterchangeFormat) →
  BitVec (exponentWidth F)
maxFiniteExponent F =
  maxFiniteExponentBits
    (BinaryInterchangeFormat.exponentBitsMinusOne F)

maxFiniteExponentField : BinaryInterchangeFormat → ℕ
maxFiniteExponentField F = bitsToℕ (maxFiniteExponent F)

emax : BinaryInterchangeFormat → ℤ.ℤ
emax F = unbiasedExponent F (maxFiniteExponent F)

minPositiveSubnormalMagnitude :
  BinaryInterchangeFormat →
  ℚ.ℚ
minPositiveSubnormalMagnitude F =
  subnormalProductMagnitude {F = F} (leastSubnormalTrailing F)

maxSubnormalMagnitude :
  BinaryInterchangeFormat →
  ℚ.ℚ
maxSubnormalMagnitude F =
  subnormalProductMagnitude {F = F} (maxTrailing F)

minPositiveNormalMagnitude :
  BinaryInterchangeFormat →
  ℚ.ℚ
minPositiveNormalMagnitude F =
  normalProductMagnitude
    {F = F}
    (leastNormalExponent F)
    (zeroTrailing F)

maxFiniteMagnitude :
  BinaryInterchangeFormat →
  ℚ.ℚ
maxFiniteMagnitude F =
  normalProductMagnitude
    {F = F}
    (maxFiniteExponent F)
    (maxTrailing F)

minNormal-unbiasedExponent≡emin :
  (F : BinaryInterchangeFormat) →
  unbiasedExponent F (leastNormalExponent F) ≡ emin F
minNormal-unbiasedExponent≡emin F =
  cong
    (λ n → ℤ._ℕ-_ n (exponentBias F))
    (leastPositiveBits-value
      (BinaryInterchangeFormat.exponentBitsMinusOne F))

minPositiveSubnormalMagnitude-textbook :
  (F : BinaryInterchangeFormat) →
  minPositiveSubnormalMagnitude F ≡
  trailingFraction {F = F} (leastSubnormalTrailing F) ℚ.·
  pow2ℚ (emin F)
minPositiveSubnormalMagnitude-textbook F = refl

maxSubnormalMagnitude-textbook :
  (F : BinaryInterchangeFormat) →
  maxSubnormalMagnitude F ≡
  trailingFraction {F = F} (maxTrailing F) ℚ.·
  pow2ℚ (emin F)
maxSubnormalMagnitude-textbook F = refl

minPositiveNormalMagnitude-textbook :
  (F : BinaryInterchangeFormat) →
  minPositiveNormalMagnitude F ≡
  normalCoefficient {F = F} (zeroTrailing F) ℚ.·
  pow2ℚ (emin F)
minPositiveNormalMagnitude-textbook F =
  cong
    (λ exponent →
      normalCoefficient {F = F} (zeroTrailing F) ℚ.·
      pow2ℚ exponent)
    (minNormal-unbiasedExponent≡emin F)

maxFiniteMagnitude-textbook :
  (F : BinaryInterchangeFormat) →
  maxFiniteMagnitude F ≡
  normalCoefficient {F = F} (maxTrailing F) ℚ.·
  pow2ℚ (emax F)
maxFiniteMagnitude-textbook F = refl

subnormalProductMagnitude-nonnegative :
  ∀ {F} →
  (bias : PositiveExponentBias F) →
  (trailing : BitVec (trailingSignificandBits F)) →
  rationalZero ≤ℚ subnormalProductMagnitude {F = F} trailing
subnormalProductMagnitude-nonnegative {F} bias trailing =
  subst
    (λ magnitude → rationalZero ≤ℚ magnitude)
    (subnormalMagnitude-textbook-product bias trailing)
    (scaleNatByPowerOfTwo-nonnegative
      (bitsToℕ trailing)
      (subnormalShift F))

normalProductMagnitude-nonnegative :
  ∀ {F} →
  (exponent : BitVec (exponentWidth F)) →
  (trailing : BitVec (trailingSignificandBits F)) →
  rationalZero ≤ℚ normalProductMagnitude {F = F} exponent trailing
normalProductMagnitude-nonnegative {F} exponent trailing =
  subst
    (λ magnitude → rationalZero ≤ℚ magnitude)
    (normalMagnitude-textbook-product exponent trailing)
    (scaleNatByPowerOfTwo-nonnegative
      (normalSignificand {F = F} trailing)
      (normalShift F exponent))

normalSignificand≤maxTrailing :
  ∀ {F} →
  (trailing : BitVec (trailingSignificandBits F)) →
  normalSignificand {F = F} trailing ℕOrder.≤
  normalSignificand {F = F} (maxTrailing F)
normalSignificand≤maxTrailing {F} trailing =
  ℕOrder.≤-k+
    {m = bitsToℕ trailing}
    {n = bitsToℕ (maxTrailing F)}
    {k = 2 ^ trailingSignificandBits F}
    (bitsToℕ≤allOnes trailing)

subnormalProductMagnitude≤maxSubnormalMagnitude :
  ∀ {F} →
  (bias : PositiveExponentBias F) →
  (trailing : BitVec (trailingSignificandBits F)) →
  subnormalProductMagnitude {F = F} trailing ≤ℚ
  maxSubnormalMagnitude F
subnormalProductMagnitude≤maxSubnormalMagnitude {F} bias trailing =
  subst2
    _≤ℚ_
    (subnormalMagnitude-textbook-product bias trailing)
    (subnormalMagnitude-textbook-product bias (maxTrailing F))
    (scaleNatByPowerOfTwo-monotone
      (bitsToℕ trailing)
      (bitsToℕ (maxTrailing F))
      (subnormalShift F)
      (bitsToℕ≤allOnes trailing))

normalProductMagnitude≤sameExponentMaxTrailing :
  ∀ {F} →
  (exponent : BitVec (exponentWidth F)) →
  (trailing : BitVec (trailingSignificandBits F)) →
  normalProductMagnitude {F = F} exponent trailing ≤ℚ
  normalProductMagnitude {F = F} exponent (maxTrailing F)
normalProductMagnitude≤sameExponentMaxTrailing {F} exponent trailing =
  subst2
    _≤ℚ_
    (normalMagnitude-textbook-product exponent trailing)
    (normalMagnitude-textbook-product exponent (maxTrailing F))
    (scaleNatByPowerOfTwo-monotone
      (normalSignificand {F = F} trailing)
      (normalSignificand {F = F} (maxTrailing F))
      (normalShift F exponent)
      (normalSignificand≤maxTrailing {F = F} trailing))

minPositiveSubnormalMagnitude≤maxSubnormalMagnitude :
  ∀ {F} →
  (bias : PositiveExponentBias F) →
  minPositiveSubnormalMagnitude F ≤ℚ
  maxSubnormalMagnitude F
minPositiveSubnormalMagnitude≤maxSubnormalMagnitude {F} bias =
  subnormalProductMagnitude≤maxSubnormalMagnitude
    bias
    (leastSubnormalTrailing F)

minPositiveSubnormalMagnitude≤subnormalProductMagnitude :
  ∀ {F} →
  (bias : PositiveExponentBias F) →
  (trailing : BitVec (trailingSignificandBits F)) →
  0 ℕOrder.< bitsToℕ trailing →
  minPositiveSubnormalMagnitude F ≤ℚ
  subnormalProductMagnitude {F = F} trailing
minPositiveSubnormalMagnitude≤subnormalProductMagnitude {F} bias trailing trailing>0 =
  subst2
    _≤ℚ_
    (subnormalMagnitude-textbook-product bias (leastSubnormalTrailing F))
    (subnormalMagnitude-textbook-product bias trailing)
    (scaleNatByPowerOfTwo-monotone
      (bitsToℕ (leastSubnormalTrailing F))
      (bitsToℕ trailing)
      (subnormalShift F)
      (subst
        (λ coefficient → coefficient ℕOrder.≤ bitsToℕ trailing)
        (sym
          (leastPositiveBits-value
            (BinaryInterchangeFormat.trailingSignificandBitsMinusOne F)))
        trailing>0))

subnormalProductMagnitude-positiveInterval :
  ∀ {F} →
  (bias : PositiveExponentBias F) →
  (trailing : BitVec (trailingSignificandBits F)) →
  0 ℕOrder.< bitsToℕ trailing →
  ℚInterval
    (minPositiveSubnormalMagnitude F)
    (maxSubnormalMagnitude F)
    (subnormalProductMagnitude {F = F} trailing)
subnormalProductMagnitude-positiveInterval bias trailing trailing>0 =
  in-ℚ-interval
    (minPositiveSubnormalMagnitude≤subnormalProductMagnitude
      bias
      trailing
      trailing>0)
    (subnormalProductMagnitude≤maxSubnormalMagnitude bias trailing)

maxTrailing≤zeroNormalSignificand :
  ∀ {F} →
  bitsToℕ (maxTrailing F) ℕOrder.≤
  normalSignificand {F = F} (zeroTrailing F)
maxTrailing≤zeroNormalSignificand {F} =
  ℕOrder.≤-trans
    (ℕOrder.<-weaken (bitsToℕ-bound (maxTrailing F)))
    (ℕOrder.≤SumLeft
      {n = 2 ^ trailingSignificandBits F}
      {k = bitsToℕ (zeroTrailing F)})

trailingFraction≤zeroNormalCoefficient :
  ∀ {F} →
  trailingFraction {F = F} (maxTrailing F) ≤ℚ
  normalCoefficient {F = F} (zeroTrailing F)
trailingFraction≤zeroNormalCoefficient {F} =
  subst2
    _≤ℚ_
    (sym (trailingFraction-canonical {F = F} (maxTrailing F)))
    (sym (normalCoefficient-canonical {F = F} (zeroTrailing F)))
    (scaleNatByPowerOfTwo-monotone
      (fractionNumerator {F = F} (maxTrailing F))
      (normalNumerator {F = F} (zeroTrailing F))
      (fractionShift F)
      (maxTrailing≤zeroNormalSignificand {F = F}))

maxSubnormalMagnitude≤minPositiveNormalMagnitude :
  (F : BinaryInterchangeFormat) →
  maxSubnormalMagnitude F ≤ℚ
  minPositiveNormalMagnitude F
maxSubnormalMagnitude≤minPositiveNormalMagnitude F =
  ≤ℚ-subst-right
    {left = maxSubnormalMagnitude F}
    {middle =
      normalCoefficient {F = F} (zeroTrailing F) ℚ.·
      pow2ℚ (emin F)}
    {right = minPositiveNormalMagnitude F}
    (sym (minPositiveNormalMagnitude-textbook F))
    (≤ℚ-mul-right-nonnegative
      (trailingFraction {F = F} (maxTrailing F))
      (normalCoefficient {F = F} (zeroTrailing F))
      (pow2ℚ (emin F))
      (pow2ℚ-nonnegative (emin F))
      (trailingFraction≤zeroNormalCoefficient {F = F}))

minPositiveSubnormalMagnitude≤minPositiveNormalMagnitude :
  ∀ {F} →
  (bias : PositiveExponentBias F) →
  minPositiveSubnormalMagnitude F ≤ℚ
  minPositiveNormalMagnitude F
minPositiveSubnormalMagnitude≤minPositiveNormalMagnitude {F} bias =
  ≤ℚ-trans
    (minPositiveSubnormalMagnitude F)
    (maxSubnormalMagnitude F)
    (minPositiveNormalMagnitude F)
    (minPositiveSubnormalMagnitude≤maxSubnormalMagnitude bias)
    (maxSubnormalMagnitude≤minPositiveNormalMagnitude F)

subnormalProductMagnitude≤minPositiveNormalMagnitude :
  ∀ {F} →
  (bias : PositiveExponentBias F) →
  (trailing : BitVec (trailingSignificandBits F)) →
  subnormalProductMagnitude {F = F} trailing ≤ℚ
  minPositiveNormalMagnitude F
subnormalProductMagnitude≤minPositiveNormalMagnitude {F} bias trailing =
  ≤ℚ-trans
    (subnormalProductMagnitude {F = F} trailing)
    (maxSubnormalMagnitude F)
    (minPositiveNormalMagnitude F)
    (subnormalProductMagnitude≤maxSubnormalMagnitude bias trailing)
    (maxSubnormalMagnitude≤minPositiveNormalMagnitude F)

finiteTextbookMagnitude-nonnegative :
  ∀ {F} →
  (bias : PositiveExponentBias F) →
  {magnitude : ℚ.ℚ} →
  FiniteTextbookMagnitude bias magnitude →
  rationalZero ≤ℚ magnitude
finiteTextbookMagnitude-nonnegative bias representable-zero =
  zeroMagnitude-nonnegative
finiteTextbookMagnitude-nonnegative {F} bias
  (representable-subnormal trailing trailing>0) =
  subnormalProductMagnitude-nonnegative bias trailing
finiteTextbookMagnitude-nonnegative {F} bias
  (representable-normal exponent trailing exponent>0 exponent<max interval) =
  normalProductMagnitude-nonnegative exponent trailing

leastNormalExponent-positive :
  ∀ {F} →
  NondegenerateExponentRange F →
  0 ℕOrder.< bitsToℕ (leastNormalExponent F)
leastNormalExponent-positive {F} range =
  subst
    (λ n → 0 ℕOrder.< bitsToℕ (leastPositiveBits n))
    (sym (NondegenerateExponentRange.exponentBitsMinusOne≡suc range))
    (leastPositiveBits-positive
      (suc (NondegenerateExponentRange.exponentTail range)))

leastNormalExponent-belowReserved :
  ∀ {F} →
  NondegenerateExponentRange F →
  suc (bitsToℕ (leastNormalExponent F)) ℕOrder.<
  2 ^ exponentWidth F
leastNormalExponent-belowReserved {F} range =
  subst
    (λ n →
      suc (bitsToℕ (leastPositiveBits n)) ℕOrder.<
      2 ^ suc n)
    (sym (NondegenerateExponentRange.exponentBitsMinusOne≡suc range))
    (leastPositiveBits-belowReserved
      (NondegenerateExponentRange.exponentTail range))

maxFiniteExponent-positive :
  ∀ {F} →
  NondegenerateExponentRange F →
  0 ℕOrder.< bitsToℕ (maxFiniteExponent F)
maxFiniteExponent-positive {F} range =
  subst
    (λ n → 0 ℕOrder.< bitsToℕ (maxFiniteExponentBits n))
    (sym (NondegenerateExponentRange.exponentBitsMinusOne≡suc range))
    (maxFiniteExponentBits-positive
      (NondegenerateExponentRange.exponentTail range))

maxFiniteExponent-belowReserved :
  (F : BinaryInterchangeFormat) →
  suc (bitsToℕ (maxFiniteExponent F)) ℕOrder.<
  2 ^ exponentWidth F
maxFiniteExponent-belowReserved F =
  maxFiniteExponentBits-belowReserved
    (BinaryInterchangeFormat.exponentBitsMinusOne F)

minPositiveSubnormal-representable :
  ∀ {F} →
  (bias : PositiveExponentBias F) →
  FiniteTextbookMagnitude bias (minPositiveSubnormalMagnitude F)
minPositiveSubnormal-representable {F} bias =
  representable-subnormal
    (leastSubnormalTrailing F)
    (leastPositiveBits-positive
      (BinaryInterchangeFormat.trailingSignificandBitsMinusOne F))

maxSubnormalMagnitude-representable :
  ∀ {F} →
  (bias : PositiveExponentBias F) →
  FiniteTextbookMagnitude bias (maxSubnormalMagnitude F)
maxSubnormalMagnitude-representable {F} bias =
  representable-subnormal
    (maxTrailing F)
    (allOnesBits-positive
      (BinaryInterchangeFormat.trailingSignificandBitsMinusOne F))

minPositiveNormal-representable :
  ∀ {F} →
  (bias : PositiveExponentBias F) →
  (range : NondegenerateExponentRange F) →
  FiniteTextbookMagnitude bias (minPositiveNormalMagnitude F)
minPositiveNormal-representable {F} bias range =
  representable-normal
    (leastNormalExponent F)
    (zeroTrailing F)
    (leastNormalExponent-positive range)
    (leastNormalExponent-belowReserved range)
    (normalSignificand-invariant {F = F} (zeroTrailing F))

maxFiniteMagnitude-representable :
  ∀ {F} →
  (bias : PositiveExponentBias F) →
  (range : NondegenerateExponentRange F) →
  FiniteTextbookMagnitude bias (maxFiniteMagnitude F)
maxFiniteMagnitude-representable {F} bias range =
  representable-normal
    (maxFiniteExponent F)
    (maxTrailing F)
    (maxFiniteExponent-positive range)
    (maxFiniteExponent-belowReserved F)
    (normalSignificand-invariant {F = F} (maxTrailing F))

minPositiveNormal-finiteRange-representable :
  ∀ {F} →
  (range : FiniteRangeFormat F) →
  FiniteTextbookMagnitude
    (FiniteRangeFormat.positiveBias range)
    (minPositiveNormalMagnitude F)
minPositiveNormal-finiteRange-representable range =
  minPositiveNormal-representable
    (FiniteRangeFormat.positiveBias range)
    (FiniteRangeFormat.nondegenerateExponentRange range)

maxFiniteMagnitude-finiteRange-representable :
  ∀ {F} →
  (range : FiniteRangeFormat F) →
  FiniteTextbookMagnitude
    (FiniteRangeFormat.positiveBias range)
    (maxFiniteMagnitude F)
maxFiniteMagnitude-finiteRange-representable range =
  maxFiniteMagnitude-representable
    (FiniteRangeFormat.positiveBias range)
    (FiniteRangeFormat.nondegenerateExponentRange range)