%export rep_real
        rep_plus rep_negative rep_minus
        rep_create_real rep_trit_expansion
        rep_showreal
%include "~ids/misc/lib/miralib/stdenv.def"

|| REALS IMPLEMENTED AS FLOATING-POINT TRITSTREAMS: IMPLEMENTATION MODULE

|| TYPES

rep_real ::= rewrite_tag $RR rep_real_structure
  Ready $RR (e $EM m)
    => Rewritten___WARNING_Don't_Set_This___Always_Set_Ready
       $RR
       (new_e $EM new_m)
       where
       (new_e, new_m) = rewrite_real (e, m)

rewrite_tag ::= Ready | Rewritten___WARNING_Don't_Set_This___Always_Set_Ready
rep_real_structure ::= exponent $EM mantissa

exponent == int
mantissa == tritstream

int ::= ZMinus nat1up | ZZero | ZPlus nat1up      
tritstream == stream trit

nat1up ::= N1One | N1Succ nat1up

stream * ::= * $Attach stream *

trit ::= TMinus | TZero | TPlus
tritsign == char || '-' | '0' | '+'

|| auxiliary types

nat ::= NZero | NSucc nat

tritstream_with_halves == stream trit_with_halves
trit_with_halves ::= THMinus | THMinusHalf | THZero | THPlusHalf | THPlus






|| USER-LEVEL FUNCTIONS

|| arithmetical operations

rep_plus :: rep_real -> rep_real -> rep_real
|| adds two reals

  || unravel exponents to find which if either is greater

  || both exponents >= 3, mantissas begin with same sign
  (tag $RR (ZPlus (N1Succ (N1Succ e1)) $EM (t $Attach m1)))
  $rep_plus
  (tag $RR (ZPlus (N1Succ (N1Succ e2)) $EM (t $Attach m2)))
    = Ready $RR (ZPlus (N1Succ esum) $EM msum)
      where newtag $RR (ZPlus esum $EM msum)
        = (tag $RR (ZPlus (N1Succ e1) $EM (t $Attach m1)))
          $rep_plus
          (tag $RR (ZPlus (N1Succ e2) $EM (t $Attach m2)))

  || both exponents >= 3, mantissas begin with opposite signs
  (tag $RR (ZPlus (N1Succ (N1Succ e1)) $EM (t $Attach m1)))
  $rep_plus
  (tag $RR (ZPlus (N1Succ (N1Succ e2)) $EM (u $Attach m2)))
    = Ready $RR (zsucc esum $EM msum)
      where newtag $RR (esum $EM msum)
        = (tag $RR (ZPlus (N1Succ e1) $EM (t $Attach m1)))
          $rep_plus
          (tag $RR (ZPlus (N1Succ e2) $EM (u $Attach m2)))

  || first exponent >= 3, second exponent not
  (tag $RR (ZPlus (N1Succ (N1Succ e1)) $EM m1)) $rep_plus (tag $RR (e2 $EM m2))
    = Ready
      $RR
      (ZPlus (N1Succ (N1Succ (N1Succ e1)))
       $EM
       (m1
        $mantissa_mean
        (pad (ZPlus (N1Succ (N1Succ e1)) $int_to_nat_minus e2) m2)))

  || second exponent >= 3, first exponent not
  (tag $RR (e1 $EM m1)) $rep_plus (tag $RR (ZPlus (N1Succ (N1Succ e2)) $EM m2))
    = Ready
      $RR
      (ZPlus (N1Succ (N1Succ (N1Succ e2)))
       $EM
       ((pad (ZPlus (N1Succ (N1Succ e2)) $int_to_nat_minus e1) m1)
        $mantissa_mean
        m2))

  || both exponents <= -3
  (tag $RR (ZMinus (N1Succ (N1Succ e1)) $EM m1))
  $rep_plus
  (tag $RR (ZMinus (N1Succ (N1Succ e2)) $EM m2))
    = Ready $RR (ZMinus (N1Succ esum) $EM msum)
      where newtag $RR (ZMinus esum $EM msum)
        = (tag $RR (ZMinus (N1Succ e1) $EM m1))
          $rep_plus
          (tag $RR (ZMinus (N1Succ e2) $EM m2))

  || first exponent <= -3, second exponent not
  (tag $RR (ZMinus (N1Succ (N1Succ e1)) $EM m1)) $rep_plus (tag $RR (e2 $EM m2))
    = Ready
      $RR
      (zsucc e2
       $EM
       ((pad (e2 $int_to_nat_minus ZMinus (N1Succ (N1Succ e1))) m1)
        $mantissa_mean
        m2))

  || second exponent <= -3, first exponent not
  (tag $RR (e1 $EM m1)) $rep_plus (tag $RR (ZMinus (N1Succ (N1Succ e2)) $EM m2))
    = Ready
      $RR
      (zsucc e1
       $EM
       (m1
        $mantissa_mean
        (pad (e1 $int_to_nat_minus ZMinus (N1Succ (N1Succ e2))) m2)))

  || remaining case: both exponents are -2, -1, 0, 1 or 2
  (tag $RR (e1 $EM m1)) $rep_plus (tag $RR (e2 $EM m2))
    = iff (e1 $int_greateq e2)
      then (Ready $RR (zsucc e1
                       $EM
                       (m1 $mantissa_mean (pad (e1 $int_to_nat_minus e2) m2))))
      else (Ready $RR (zsucc e2
                       $EM
                       ((pad (e2 $int_to_nat_minus e1) m1) $mantissa_mean m2)))




rep_negative :: rep_real -> rep_real
|| negates a real
  rep_negative (tag $RR (e $EM m)) = tag $RR (e $EM (mantissa_negate m))

rep_minus :: rep_real -> rep_real -> rep_real
|| binary subtraction on reals
  x $rep_minus y = x $rep_plus rep_negative y


|| conversion facilities

|| conversion between reals and explicit exponent-mantissa pairs

rep_create_real :: (num, [tritsign]) -> rep_real
|| constructs a real from an exponent and a mantissa
  rep_create_real (e, m) = Ready $RR ((create_exponent e)
                                      $EM
                                      (create_mantissa m))

rep_trit_expansion :: rep_real -> (num, [tritsign])
|| gives a real in floating point form
  rep_trit_expansion (tag $RR (e $EM m))
    = (exponent_value e, mantissa_trit_expansion m)






|| AUXILIARY FUNCTIONS

|| rewrite rule for reals
rewrite_real :: (exponent, mantissa) -> (exponent, mantissa)
  rewrite_real (e, m) = rewrite_zeroes_into_exponent (e, rewrite_mantissa m)

rewrite_zeroes_into_exponent :: (exponent, mantissa) -> (exponent, mantissa)
  rewrite_zeroes_into_exponent (ZPlus (N1Succ e), TZero $Attach m)
    = rewrite_zeroes_into_exponent (ZPlus e, m)
  rewrite_zeroes_into_exponent (ZPlus N1One, TZero $Attach m)
    = rewrite_zeroes_into_exponent (ZZero, m)
  rewrite_zeroes_into_exponent (ZZero, TZero $Attach m)
    = rewrite_zeroes_into_exponent (ZMinus N1One, m)
  rewrite_zeroes_into_exponent (ZMinus n1up, m)
    = (ZMinus new_n1up, new_m)
      where (new_n1up, new_m) = rewrite_zeroes_into_nat1up (n1up, m)
  rewrite_zeroes_into_exponent (e, m) = (e, m)

rewrite_zeroes_into_nat1up :: (nat1up, mantissa) -> (nat1up, mantissa)
  rewrite_zeroes_into_nat1up (N1Succ n, m)
    = (N1Succ new_n, new_m)
      where (new_n, new_m) = rewrite_zeroes_into_nat1up (n, m)
  rewrite_zeroes_into_nat1up (N1One, TZero $Attach m)
    = (N1Succ new_n, new_m)
      where (new_n, new_m) = rewrite_zeroes_into_nat1up (N1One, m)
  rewrite_zeroes_into_nat1up (N1One, m) = (N1One, m)

rewrite_mantissa :: mantissa -> mantissa
  rewrite_mantissa (TZero $Attach m)
    = TZero $Attach rewrite_mantissa m
  rewrite_mantissa (TPlus $Attach TMinus $Attach m)
    = TZero $Attach rewrite_mantissa (TPlus $Attach m)
  rewrite_mantissa (TMinus $Attach TPlus $Attach m)
    = TZero $Attach rewrite_mantissa (TMinus $Attach m)
  rewrite_mantissa (head $Attach tail)
    = head $Attach rewrite_mantissa tail



|| supporting operations for addition

pad :: nat -> mantissa -> mantissa
|| pads out a mantissa with leading zeroes
  pad NZero m = m
  pad (NSucc n) m = TZero $Attach (pad n m)

mantissa_mean :: mantissa -> mantissa -> mantissa
|| computes the arithmetic mean of two mantissas
  m1 $mantissa_mean m2
    = purge_halves (stream_map trit_mean (m1 $stream_zip2 m2))

purge_halves :: tritstream_with_halves -> tritstream
|| decontaminates a tritstream of halves while preserving its value
  purge_halves x = newhead $Attach (purge_halves newtail)
                   where
                   (newhead, newtail) = purge_one_step x

purge_one_step :: tritstream_with_halves -> (trit, tritstream_with_halves)
|| peels off one guaranteed trit from its contaminated input stream
  purge_one_step (first $Attach second $Attach rest)
    = (newfirst, newsecond $Attach rest)
      where (newfirst, newsecond) = rewrite_one_step (first, second)


|| supporting operations for negation

mantissa_negate :: mantissa -> mantissa
|| negates a mantissa
  mantissa_negate = stream_map trit_negate

|| supporting operations for the conversion facilities

create_exponent :: num -> exponent
|| builds an exponent from a system integer
  create_exponent e
    = error "exponent must be an integer", ~integer e
    = num_to_int e, otherwise

create_mantissa :: [tritsign] -> mantissa
|| builds a mantissa from a list of tritsigns
  create_mantissa [] = stream_repeat TZero
  create_mantissa (head:tail) = trit_called head $Attach create_mantissa tail

exponent_value :: exponent -> num
|| expresses an exponent as a system integer
  exponent_value = int_to_num

mantissa_trit_expansion :: mantissa -> [tritsign]
|| gives a mantissa in tritsign form
  mantissa_trit_expansion (head $Attach tail)
    = tritsign_name head : mantissa_trit_expansion tail








|| LOW-LEVEL OPERATIONS

|| trit operations

trit_called :: tritsign -> trit
|| produces the named trit
  trit_called '-' = TMinus
  trit_called '0' = TZero
  trit_called '+' = TPlus
  trit_called anything_else
    = error ("'" ++ [anything_else] ++ "' is not a valid tritsign")


tritsign_name :: trit -> tritsign
|| gives the tritsign for a trit
  tritsign_name TMinus = '-'
  tritsign_name TZero  = '0'
  tritsign_name TPlus  = '+'


trit_mean :: (trit, trit) -> trit_with_halves
|| takes the mean of two trits
  trit_mean (TMinus, TMinus) = THMinus
  trit_mean (TMinus, TZero ) = THMinusHalf
  trit_mean (TMinus, TPlus ) = THZero 

  trit_mean (TZero , TMinus) = THMinusHalf
  trit_mean (TZero , TZero ) = THZero 
  trit_mean (TZero , TPlus ) = THPlusHalf

  trit_mean (TPlus , TMinus) = THZero 
  trit_mean (TPlus , TZero ) = THPlusHalf
  trit_mean (TPlus , TPlus ) = THPlus 


trit_negate :: trit -> trit
|| negates a trit
  trit_negate TMinus = TPlus
  trit_negate TZero  = TZero
  trit_negate TPlus  = TMinus


rewrite_one_step :: (trit_with_halves, trit_with_halves)
                      -> (trit, trit_with_halves)
|| decontaminates the left trit by rewriting both if necessary
  || purge a plus half
  rewrite_one_step (THMinusHalf, THMinus    ) = (TMinus, THZero     )
  rewrite_one_step (THMinusHalf, THMinusHalf) = (TMinus, THPlusHalf )
  rewrite_one_step (THMinusHalf, THZero     ) = (TZero , THMinus    )
  rewrite_one_step (THMinusHalf, THPlusHalf ) = (TZero , THMinusHalf)
  rewrite_one_step (THMinusHalf, THPlus     ) = (TZero , THZero     )

  || purge a minus half
  rewrite_one_step (THPlusHalf , THMinus    ) = (TZero , THZero     )
  rewrite_one_step (THPlusHalf , THMinusHalf) = (TZero , THPlusHalf )
  rewrite_one_step (THPlusHalf , THZero     ) = (TZero , THPlus     )
  rewrite_one_step (THPlusHalf , THPlusHalf ) = (TPlus , THMinusHalf)
  rewrite_one_step (THPlusHalf , THPlus     ) = (TPlus , THZero     )

  || otherwise leave well alone
  rewrite_one_step (THMinus, y) = (TMinus, y)
  rewrite_one_step (THZero , y) = (TZero , y)
  rewrite_one_step (THPlus , y) = (TPlus , y)



|| stream operations

stream_map :: (* -> **) -> stream * -> stream **
|| analogue of "map" for streams
  stream_map f (head $Attach tail) = (f head) $Attach (stream_map f tail)

stream_zip2 :: stream * -> stream ** -> stream (*, **)
|| analogue of "zip2" for streams
  (head1 $Attach tail1) $stream_zip2 (head2 $Attach tail2)
    = (head1, head2) $Attach (tail1 $stream_zip2 tail2)

stream_repeat :: * -> stream *
|| analogue of "repeat" for streams
  stream_repeat x = fix (x $Attach)



|| int, nat, nat1up operations

zsucc :: int -> int
  zsucc (ZPlus  x         ) = ZPlus  (N1Succ x)
  zsucc (ZZero            ) = ZPlus  N1One
  zsucc (ZMinus N1One     ) = ZZero
  zsucc (ZMinus (N1Succ x)) = ZMinus x

zpred :: int -> int
  zpred (ZMinus x         ) = ZMinus (N1Succ x)
  zpred (ZZero            ) = ZMinus N1One
  zpred (ZPlus  N1One     ) = ZZero
  zpred (ZPlus  (N1Succ x)) = ZPlus  x

int_greateq :: int -> int -> bool
  ZPlus (N1Succ x) $int_greateq ZPlus (N1Succ y)
    = ZPlus x $int_greateq ZPlus y
  ZPlus (N1Succ x) $int_greateq anything_else = True
  anything_else $int_greateq ZPlus (N1Succ y) = False
  ZMinus (N1Succ x) $int_greateq ZMinus (N1Succ y)
    = ZMinus x $int_greateq ZMinus y
  ZMinus (N1Succ x) $int_greateq anything_else = False
  anything_else $int_greateq ZMinus (N1Succ y) = True

  || from here on down, both inputs are -1, 0 or 1
  ZPlus N1One $int_greateq y = True
  ZZero $int_greateq y = y=ZZero \/ y=ZMinus N1One
  ZMinus N1One $int_greateq y = y=ZMinus N1One



num_to_int :: num -> int
  num_to_int x = error "input to num_to_int is not an integer", ~integer x
               = ZZero, x=0
               = ZPlus  (num_to_nat1up_shifted ( x-1)), x >= 1
               = ZMinus (num_to_nat1up_shifted (-x-1)), x <= -1

num_to_nat1up_shifted :: num -> nat1up
|| produces the nat1up version of one more than the input
  num_to_nat1up_shifted 0     = N1One
  num_to_nat1up_shifted (x+1) = N1Succ (num_to_nat1up_shifted x)

int_to_num :: int -> num
  int_to_num (ZMinus (N1Succ x)) = int_to_num (ZMinus x) - 1
  int_to_num (ZMinus N1One     ) = -1
  int_to_num (ZZero            ) = 0
  int_to_num (ZPlus  N1One     ) = 1
  int_to_num (ZPlus  (N1Succ x)) = int_to_num (ZPlus  x) + 1

int_to_nat_minus :: int -> int -> nat
  ZPlus (N1Succ x) $int_to_nat_minus ZPlus (N1Succ y)
    = ZPlus x $int_to_nat_minus ZPlus y
  ZPlus (N1Succ x) $int_to_nat_minus y
    = NSucc (ZPlus x $int_to_nat_minus y)
  ZPlus N1One $int_to_nat_minus y
    = ZZero $int_to_nat_minus zpred y
  ZZero $int_to_nat_minus ZZero = NZero
  ZZero $int_to_nat_minus ZMinus N1One = NSucc NZero
  ZZero $int_to_nat_minus ZMinus (N1Succ y)
    = NSucc (ZZero $int_to_nat_minus ZMinus y)
  ZMinus N1One $int_to_nat_minus y
    = ZZero $int_to_nat_minus zsucc y
  ZMinus (N1Succ x) $int_to_nat_minus ZMinus (N1Succ y)
    = ZMinus x $int_to_nat_minus ZMinus y

showint :: int -> [char]
  showint (ZMinus x) = "-" ++ shownat1up x
  showint (ZZero   ) = "0"
  showint (ZPlus  x) = "+" ++ shownat1up x

shownat1up :: nat1up -> [char]
  shownat1up N1One = "1"
  shownat1up (N1Succ x) = "s" ++ shownat1up x









|| PRINT-REPRESENTATIONS FOR ABSTRACT DATA TYPES

|| print-representation for type real

rep_showreal :: rep_real -> [char]
  rep_showreal (tag $RR (e $EM m))
    = "REAL WITH (BINARY) EXPONENT="
      ++ showexponent e
      ++ ", MANTISSA=."
      ++ showmantissa m

|| auxiliary functions for print-representations

showexponent :: exponent -> [char]
  showexponent = showint

showmantissa :: mantissa -> [char]
  showmantissa = mantissa_trit_expansion
