"An 8-bit byte. A `Byte` value represents a congruence class
 of [[integers|Integer]] modulo 256, and may be interpreted 
 as:
 
 - an [[unsigned]] integer value in the range `0..255`, or 
 - a [[signed]] integer value in the range `-128..127`. 
 
 `Byte` is not considered a full numeric type, supporting 
 only:
 
 - [[bitwise|Binary]] operations, and 
 - addition and subtraction modulo 256.
 
 `Byte`s with modular addition form a [[mathematical 
 group|Invertible]]. Thus, every byte `b` has an additive
 inverse `-b` where:
 
     (-b).signed == -b.signed
     (-b).unsigned == b.unsigned==0 then 0 else 256 - b.unsigned
 
 `Byte` is a [[recursive enumerable type|Enumerable]]. For
 example, the range:
 
     254.byte .. 1.byte
     
 contains the values `254.byte, 255.byte, 0.byte, 1.byte`.
 
 `Byte` does not have a [[total order|Comparable]] because
 any such order would:
  
 - be inconsistent with the definition of [[successor]] and 
   [[predecessor]] under modular addition, and
 - would depend on interpretation of the `Byte` value as
   signed or unsigned.
 
 Thus, to compare the magnitude of two bytes, it is 
 necessary to first convert them to either their `signed` or 
 `unsigned` integer values.
 
 `Byte`s are useful mainly because they can be efficiently 
 stored in an [[Array]]."
tagged("Basic types")
since("1.1.0")
shared native final class Byte(congruent) 
        extends Object()
        satisfies Binary<Byte> & 
                  Invertible<Byte> &
                  Enumerable<Byte> {
    
    "An integer member of the congruence class of the 
     resulting `Byte`.
     
     For any integer `x>=0`:
     
         x.byte.unsigned == x % 256
         x.byte.signed == x % 256
     
     And for an integer `x<0`:
     
         x.byte.unsigned == 256 + x % 256
         x.byte.signed == x % 256
     
     And for any integers `x` and `y` which are congruent
     modulo 256:
     
         x.byte == y.byte"
    Integer congruent;
    
    "This byte interpreted as an unsigned integer in the
     range `0..255` (that is, `0..#FF`)."
    shared native Integer unsigned 
            = (congruent % #100).and(#FF);
    
    "This byte interpreted as a signed integer in the range 
     `-128..127` (that is, `-#80..#7F`)."
    shared native Integer signed 
            => unsigned > #7F 
            then unsigned - #100 
            else unsigned;
    
    //shared Boolean[8] bits;
    
    "Whether this byte is even."
    shared native Boolean even => and(1.byte).zero;
    
    "Whether this byte is zero."
    shared native Boolean zero => unsigned == 0;
    
    "Whether this byte is one."
    shared native Boolean unit => unsigned == 1;
    
    "The additive inverse of this byte. For any integer `x`:
     
         (-x.byte).signed = -x.byte.signed"
    shared actual native Byte negated => (-signed).byte;
    
    "The modulo 256 sum of this byte and the given byte."
    shared actual native Byte plus(Byte other)
            => (this.unsigned + other.unsigned).byte;
    
    function indexInRange(Integer index)
            => 0 <= index < 8;
    
    function mask(Integer index) 
            => 1.byte.leftLogicalShift(index);
    
    shared actual native Boolean get(Integer index) 
            => indexInRange(index)
            then !and(mask(index)).zero 
            else false;
    
    shared actual native Byte flip(Integer index)
            => indexInRange(index)
            then xor(mask(index)) 
            else this;
    
    shared actual native Byte set(Integer index, Boolean bit)
            => indexInRange(index)
            then (bit then or(mask(index)) 
                      else and(mask(index).not))
            else this;
    
    shared actual native Byte clear(Integer index) 
            => indexInRange(index)
            then and(mask(index).not) 
            else this;
    
    shared actual native Byte not => unsigned.not.byte;
    
    shared actual native Byte and(Byte other) 
            => unsigned.and(other.unsigned).byte;
    
    shared actual native Byte or(Byte other)
            => unsigned.or(other.unsigned).byte;
    
    shared actual native Byte xor(Byte other)
            => unsigned.xor(other.unsigned).byte;
    
    "If [[shift]] is in the range `0..$111`, shift the bits 
     to the left by `shift` positions, using zero extension 
     to fill in the least significant bits. Otherwise shift 
     the addressable bits to the left by `shift.and($111)`
     positions, using zero extension."
    aliased ("leftShift")
    shared actual native Byte leftLogicalShift(Integer shift)
            => unsigned.leftLogicalShift(shift.and($111)).byte;
    
    "If [[shift]] is in the range `0..$111`, shift the bits 
     to the right by `shift` positions, using zero extension 
     to fill in the most significant bits. Otherwise shift 
     the addressable bits to the right by `shift.and($111)`
     positions, using zero extension."
    aliased ("rightShift")
    shared actual native Byte rightLogicalShift(Integer shift)
            => unsigned.rightLogicalShift(shift.and($111)).byte;
    
    "If [[shift]] is in the range `0..$111`, shift the bits 
     to the right by `shift` positions, using sign extension 
     to fill in the most significant bits. Otherwise shift 
     the addressable bits to the right by `shift.and($111)`
     positions, using sign extension."
    shared actual native Byte rightArithmeticShift(Integer shift)
            => signed.rightArithmeticShift(shift.and($111)).byte;
    
    shared actual native Byte predecessor => (unsigned-1).byte;
    shared actual native Byte successor => (unsigned+1).byte;
    
    shared actual native Byte neighbour(Integer offset) 
            => (unsigned + offset).byte;
    
    shared actual native Integer offset(Byte other)
            => minus(other).unsigned;
    
    shared actual native Integer offsetSign(Byte other)
            => this==other then 0 else 1;
    
    shared actual native Boolean equals(Object that) 
            => if (is Byte that) 
            then this.unsigned == that.unsigned
            else false;
    
    shared actual native Integer hash => signed;
    
    "The [[unsigned]] interpretation of this byte as a 
     string."
    shared actual native String string => unsigned.string;
    
}