--
--
-- Order
--
--

ordering :=
  algebraicDataMatcher
    | less
    | equal
    | greater

compare m n :=
  if isCollection m
    then compareC m n
    else if m < n then Less else if m = n then Equal else Greater

compareC c1 c2 :=
  match (c1, c2) as (list something, list something) with
    | ([], []) -> Equal
    | ([], _) -> Less
    | (_, []) -> Greater
    | ($x :: $xs, #x :: $ys) -> compareC xs ys
    | ($x :: _, $y :: _) -> compare x y

min $x $y := if x < y then x else y

max $x $y := if x > y then x else y

min/fn f $xs := foldl1 2#(if f %1 %2 = Less then %1 else %2) xs

max/fn f $xs := foldl1 2#(if f %1 %2 = Greater then %1 else %2) xs

minimum $xs := foldl1 min xs

maximum $xs := foldl1 max xs

splitByOrdering := 2#(splitByOrdering/fn compare %1 %2)

splitByOrdering/fn f p xs :=
  match xs as list something with
    | [] -> ([], [], [])
    | $x :: $rs ->
      let (ys1, ys2, ys3) := splitByOrdering/fn f p rs
       in match f x p as ordering with
            | less -> (x :: ys1, ys2, ys3)
            | equal -> (ys1, x :: ys2, ys3)
            | greater -> (ys1, ys2, x :: ys3)

sort := 1#(sort/fn compare %1)

sort/fn f xs :=
  match xs as list something with
    | [] -> []
    | $x :: [] -> [x]
    | _ ->
      let n := length xs
          p := nth (quotient n 2) xs
          (ys1, ys2, ys3) := splitByOrdering/fn f p xs
       in sort/fn f ys1 ++ ys2 ++ sort/fn f ys3

sortStrings xs :=
  sort/fn 2#(compareC (map ctoi (unpack %1)) (map ctoi (unpack %2))) xs

merge xs ys :=
  match (xs, ys) as (list something, list something) with
    | ([], _) -> ys
    | (_, []) -> xs
    | ($x :: $txs, ?(>= x) :: _) -> x :: merge txs ys
    | (_, $y :: $tys) -> y :: merge xs tys

merge/fn f xs ys :=
  match (xs, ys) as (list something, list something) with
    | ([], _) -> ys
    | (_, []) -> xs
    | ($x :: $txs, ?1#(f %1 x = Greater) :: _) -> x :: merge txs ys
    | (_, $y :: $tys) -> y :: merge xs tys

minimize f xs :=
  foldl1 2#(if compare (f %1) (f %2) = Less then %1 else %2) xs

maximize f xs :=
  foldl1 2#(if compare (f %1) (f %2) = Greater then %1 else %2) xs
