packages feed

swarm-0.1.0.0: example/list.sw

/*******************************************************************/
/*                       LIST ENCODING IN INT                      */
/*                                                                 */
/* This file defines functions that allow storing arbitrary list   */
/* of values in one integer. It is possible because an integer can */
/* have unlimited length and can be split into chunks of smaller   */
/* integers. See Haskell docs for Integer (used by swarm for int). */
/*                                                                 */
/* For examples of usage see the unit tests in testLIST function.  */
/*                                                                 */
/*******************************************************************/

// Definitions:
// - MAIN: nil (Haskell []), cons (Haskell (:)), head, tail
// - BONUS: headTail, index, for, for_each, for_each_i
// - ARITH: len, mod, shiftL, shiftR, shiftLH, shiftRH
// - HELPERS: consP, getLenPart, setLenPart, to1numA, getLenA
// - TESTS: assert, testLIST, testLIST_BIG, testLIST_ALL

/*******************************************************************/
/*                              TYPE                               */
/*******************************************************************/

// Type of list of ints - the inner ints can themselves be lists,
// so *any* type can be encoded inside.
//
// It is the callers responsibility to make sure a program using this
// "type" is type safe. Notably 2 == [0] != [] == 0 but [] !! x == 0.
// 
// TODO: once #153 is resolved, add types to definitions
//
// type listI = int

/*******************************************************************/
/*                             LAYOUT                              */
/*******************************************************************/

// The length of a number is kept in the header and split into 7bit
// chunks each prefixed by 1bit that marks if the byte is last in
// the header (0=YES).

/* EXAMPLE - [short_x,long_y] - concretly e.g. [42, 2^(2^7)] 

0   < len short_x < 2^7
2^7 < len long_y  < 2^14 

cons short_x   $          cons long_y          $ nil
vvvvvvvvvvvv       vvvvvvvvvvvvvvvvvvvvvvvvv     vvv
  0|len x|x    |   1|len y%2^7|0|len y/2^7|y   |  0
^^^^^^^^^^^^       ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  head                         tail            
*/

/*******************************************************************/
/*                         ARITH PRIMITIVES                        */
/*******************************************************************/

// bitlength of a number (shifting by one)
def naive_len : int -> int = \i.
  if (i == 0) {0} {1 + naive_len (i/2)}
end

// modulus function (%)
def mod : int -> int -> int = \i.\m.
  i - m * (i / m)
end

// f X Y = Y * 2^(-X)
def shiftL : int -> int -> int = \s.\i.
  i / 2^s
end

// f X Y = Y * 2^(X)
def shiftR : int -> int -> int = \s.\i.
  i * 2^s
end

// shift by (8bit) bytes
def shiftLH : int -> int -> int = \s. shiftL (s*8) end
def shiftRH : int -> int -> int = \s. shiftR (s*8) end

// bitlength of a number (shifting by 64)
def len : int -> int = \i.
  let next = i / 2^64 in
  if (next == 0) {naive_len i} {64 + len next}
end

/*******************************************************************/
/*                       helper functions                          */
/*******************************************************************/

def getLenPart : int -> int = \i. mod (i/2) (2^7) end
def setLenPart : int -> int = \i. 2 * (mod i (2^7)) end

// Split length into 7-bit parts and prefix all but last with 1
def to1numA : int -> int * int = \i.
  let nextPart = shiftL 7 i in
  if (nextPart == 0)
    { ((2 * i), 1) } /* last part */
    { let p = to1numA nextPart in
      ( 1 /* header isEnd bit */ + setLenPart i + shiftRH 1 (fst p)
      , 1 + snd p
      )
    }
end

// Get bitlength of the first number in the list
// and also the list starting at the number itself
//
// getLenA : listI -> int * int
def getLenA = \xs.
  let isEnd = 0 == mod xs 2 in
  let l = getLenPart xs in
  let nextPart = shiftLH 1 xs in
  if isEnd
    { (l, nextPart) }
    { let p = getLenA nextPart in
      (l + shiftR 7 (fst p), snd p)
    }
end

/*******************************************************************/
/*                         LIST FUNCTIONS                          */
/*******************************************************************/

// headTail : listI -> {int} * {listI} 
def headTail = \xs.
  let sign = mod xs 2 in
  let ns = xs / 2 in
  let p = getLenA ns in
  ( { let x = mod (snd p) $ 2 ^ fst p in
      if (sign == 0) {x} {-x}
    }
  , { shiftL (fst p) (snd p) }
  )
end

// head : listI -> int
def head : int -> int = \xs.
  force $ fst $ headTail xs
end

// tail : listI -> listI 
def tail = \xs.
  force $ snd $ headTail xs
end

// nil : listI
def nil = 0 end

// Add non-negative number to beggining of list (cons adds the sign)
// consP : nat -> listI -> int
def consP = \x.\xs.
  if (x == 0)
    { 2 /* header says one bit length */ + shiftR (8+1) xs}
    { let l = len x in
      let pl = to1numA l in
      (fst pl + shiftRH (snd pl) (x + shiftR l xs))
    }
end

// Add integer to the beggining of the list
// consP : int -> listI -> listI
def cons = \x.\xs.
  if (x >= 0)
    {     2 * consP   x  xs }
    { 1 + 2 * consP (-x) xs }
end


/*******************************************************************/
/*                       MORE LIST FUNCTIONS                       */
/*******************************************************************/

// index : int -> listI -> int
def index = \i.\xs.
  if (i <= 0)
    {head xs}
    {index (i-1) (tail xs)}
end

def for : int -> int -> (int -> cmd a) -> cmd unit = \s.\e.\act.
  if (s == e) {}
  {act s; for (s+1) e act}
end

// for_each_i : int -> listI int -> (int * int -> cmd a) -> cmd unit
def for_each_i = \i.\xs.\act.
  if (xs == nil) {}
  { let ht = headTail xs
    in act i (force $ fst ht); for_each_i (i+1) (force $ snd ht) act
  }
end

// for_each : listI int -> (int -> cmd a) -> cmd unit
def for_each = \xs.\act.
  for_each_i 0 xs (\i. act)
end

/*******************************************************************/
/*                           UNIT TESTS                            */
/*******************************************************************/

// TODO: show values when #248 is implemented
def assert = \b.\m.
  if b {} {log "FAIL:"; fail m}
end

def testLIST =
  log "STARTING TESTS OF LIST:";

  log "check [0]";
  let l0 = cons 0 nil in
  assert (l0 == 4)                   "[0] ~ 4";
  assert (head l0 == 0)              "head [0] == 0";
  assert (tail l0 == nil)            "tail [0] == []";

  log "check [1]";
  let l1 = cons 1 nil in
  assert (l1 == 516)                 "[1] ~ 516";
  assert (head l1 == 1)              "head [1] == 1";
  assert (tail l1 == nil)            "tail [1] == []";

  log "check [-1]";
  let ln1 = cons (-1) nil in
  assert (ln1 == 517)                "[-1] ~ 517";
  assert (head ln1 == -1)            "head [-1] == -1";
  assert (tail ln1 == nil)           "tail [-1] == []";
  
  log "check [42]";
  let l42 = cons 42 nil in
  assert (l42 == 21528)              "[42] ~ 21528";
  assert (head l42 == 42)            "head [42] == 42";
  assert (tail l42 == nil)           "tail [42] == []";
  
  log "check [499672]";
  let l499672 = cons 499672 nil in
  assert (l499672 == 255832140)      "[499672] ~ 255832140";
  assert (head l499672 == 499672)    "head [499672] == 499672";
  assert (tail l499672 == nil)       "tail [499672] == []";
  
  log "check [1,0]";
  let l1_0 = cons 1 l0 in
  assert (l1_0 == 4612)              "[1,0] ~ 4612";
  assert (head l1_0 == 1)            "head [1,0] == 1";
  assert (tail l1_0 == l0)           "tail [1,0] == [0]";

  log "check [11,1,0]";
  let l11_1_0 = cons 11 l1_0 in
  assert (head l11_1_0 == 11)        "head [11,1,0] == 11";
  assert (tail l11_1_0 == l1_0)      "tail [11,1,0] == [1,0]";

  log "check [0..9]";
  let l0__9 = cons 0 $ cons 1 $ cons 2 $ cons 3 $ cons 4 $ cons 5 $ cons 6 $ cons 7 $ cons 8 $ cons 9 nil in
  assert (head l0__9 == 0)           "head [0..9] == 0";

  log "check indices";
  assert (index 0 l0__9 == 0)        "[0..9] !! 0 == 0";
  assert (index 9 l0__9 == 9)        "[0..9] !! 9 == 9";

  // TODO: log the number of iteration once #248 is implemented

  log "check for";
  for 0 9 (\i.
    assert (index i l0__9 == i)      "[0..9] - every index I has value I"
  );

  log "check for_each";
  for_each l0__9 (\x.
    assert (x < 10)                  "[0..9] - every value X < 10"
  );

  log "check for_each_i";
  for_each_i 0 l0__9 (\i.\x.
    assert (i == x)                  "[0..9] - I == X for every value X at index I"
  );

  log "OK - ALL TEST PASSED\a"
end

// This tests uses VERY LONG lists of size up to 2097332 bits.
// TIP: increase the game speed (ticks/s) when you run this test.
def testLIST_BIG =
  log "STARTING TESTS OF BIG LISTS:";

  log "check [2^(2^7)] aka [big]";
  let big = 2^2^7 in
  assert (len big == 129)            "len (2^2^7) == 2^7+1";
  assert (to1numA 129 == (515,2))    "to1numA: 129 == 1000_0001 --> 515 == [0000 001 0] [0000 001 1]";
  assert (getLenA 515 == (129,nil))  "getLenA: 515 --> 129";
  let ibig = 2 * (shiftR 16 big + 515) in
  let lbig = cons big nil in
  assert (lbig == ibig)              "[big] ~ 2 * ((big * 2^(2*8)) + 515)";
  assert (head lbig == big)          "head [big] == big";
  assert (tail lbig == nil)          "tail [big] == []";

  log "check [2^(2^21)] aka [bigger]";
  let bigger = 2^(2^21) in
  let lbigger = cons bigger nil in
  assert (head lbigger == bigger)    "head [bigger] == bigger";
  assert (tail lbigger == nil)       "tail [bigger] == []";

  log "check [2^(2^21),2^(2^7)] aka [bigger,big]";
  let lbiggest = cons bigger lbig in
  assert (head lbiggest == bigger)  "head [bigger,big] == bigger";
  assert (tail lbiggest == lbig)    "tail [bigger,big] == [big]";
  
  log "OK - ALL TEST PASSED";
end

def testLIST_ALL =
  testLIST;
  testLIST_BIG;
end