package jazz.lang;

///////////////////////////////////////////////////////////////////////////////
//
//               Builtin operators, functions, and methods
//
//   The static members of class Builtin are always automatically imported
//
///////////////////////////////////////////////////////////////////////////////

public native interface _ {
  public toString(): String;
}

public final abstract class Builtin {
  
  ////////////////////////////////
  // Generic arithmetic operators
  ////////////////////////////////

  // Binary plus
  public static (+\2)<T: Arith>(x: T, y: T): T;

  // Unary plus
  public static (+\1)<T: Arith>(x: T): T;

  // Binary minus
  public static (-\2)<T: Arith>(x: T, y: T): T;

  // Unary minus
  public static (-\1)<T: Arith>(x: T): T;

  // Multiplications
  public static (*)<T: Arith>(x: T, y: T): T;

  // Power
  public static native (**)<T: float>(x: T, n: int): T = "%pow";

  // Modulo
  public static native (%)<T: per>(x: T, y: T): T = "%imod";

  // Euclidian division
  public static native (div)(x: rat, y: rat): int = "%idiv";

  // Exact division 
  public static native (/)<T: float>(x: T, y: T): T {rat <: T} = "%div";

  // Shift to lsb
  public static native (>>)<T: per>(x: T, n: int): T = "%rshift";

  // Shift to msb
  public static native (<<)<T: per>(x: T, n: int): T = "%lshift";

  // Equality
  public static (==)<T>(x: T, y: T): boolean;

  // By default, (x != y) is !(x == y)
  public static (!=)<T>(x: T, y: T): boolean;

  // Inequality
  public static (<=)<T: Comparable>(x: T, y: T): boolean;

  // By default, (x < y) is (x <= y && !(x == y))
  public static (<)<T: Comparable>(x: T, y: T): boolean;

  // By default, (x > y) is (y < x)
  public static (>)<T: Comparable>(x: T, y: T): boolean;

  // By default, (x >= y) is (y <= x)
  public static (>=)<T: Comparable>(x: T, y: T): boolean;

  // And
  public static (&)<T: Lattice>(x: T, y: T): T;

  // Or
  public static (|)<T: Lattice>(x: T, y: T): T;

  // Xor
  public static (^)<T: BooleanAlgebra>(x: T, y: T): T;

  // Not
  public static (~)<T: BooleanAlgebra>(x: T): T;

  // Conditional
  public static cond<T: BooleanAlgebra>(x: T, y: T, z: T): T;

  // Sequential logical and
  public static native (&&)(x: boolean, y: boolean): boolean = "%logical_and";

  // Sequential or
  public static native (||)(x: boolean, y: boolean): boolean = "%logical_or";

  // Logical not
  public static native (!)(x: boolean): boolean = "%logical_not";
}

///////////////////////////////////////////////////////////////////////////////
//
//                               Implementation
//
///////////////////////////////////////////////////////////////////////////////

// Default implementation of "toString"
toString@_() = s {
  if (native("boolean "
             "fr.ensmp.cma.jazz.runtime.Primitive.isTuple(java.lang.Object)",
             this)) {
    n = native("int fr.ensmp.cma.jazz.runtime.Tuple.size()", this): int;
    s' = ["(", i -> format("%s%s%a",
                           s'[i-1],
                           (i - 1 > 0 ? ", " : ""),
                           native("%tupleElement", this, i - 1))];
    s = format("%s)", s'[n]);
  } else {
    s = native("%defaultToString", this);
  }
}   

// Equality operators
native Builtin.(==)(x@_, y@_) = "%eq";
Builtin.(!=)(x@_, y@_) = !(x == y);
Builtin.(<)(x@_, y@_) = (x <= y) && !(x == y);
Builtin.(>)(x@_, y@_) = y < x;
Builtin.(>=)(x@_, y@_) = y <= x;