/* NetRexx */

options replace format comments java crossref savelog symbols binary

-- -----------------------------------------------------------------------------
class RCPolymorphism public final

method main(args = String[]) public constant

  parry = [Point                    -
      Point()                       -
    , Point(1.0)                    -
    , Point(1.0, 2.0)               -
    , Point(Point(0.3, 0.2))        -
    , Circle()                      -
    , Circle(2.0, 2.0)              -
    , Circle(5.0, 6.0, 7.0)         -
    , Circle(Point(8.0, 9.0))       -
    , Circle(Point(8.0, 9.0), 4.0)  -
    , Circle(Circle(1.5, 1.4, 1.3)) -
  ]

  loop pp = 0 to parry.length - 1
    parry[pp].print
    end pp

  return

-- -----------------------------------------------------------------------------
class RCPolymorphism.Point public binary

properties private
  x = double
  y = double
  className = Point.class.getSimpleName

method Point(x_ = double 0.0, y_ = double 0.0)
  setX(x_)
  setY(y_)
  return

method Point(p = Point)
  this(p.getX, p.getY)
  return

method display public returns String
  hx = '@'Rexx(Integer.toHexString(hashCode())).right(8, 0)
  str = Rexx(className).left(10)':'hx': (x,y) = (' || -
        Rexx(getX()).format(null, 3)',' -
        Rexx(getY()).format(null, 3)')'
  return str

method getX public returns double
  return x

method getY public returns double
  return y

method setX(x_ = double 0.0) inheritable
  x = x_
  return

method setY(y_ = double 0.0) inheritable
  y = y_
  return

method print inheritable
  say display
  return

-- -----------------------------------------------------------------------------
class RCPolymorphism.Circle public extends RCPolymorphism.Point binary

properties private
  r = double
  className = Circle.class.getSimpleName

method Circle(x_ = double 0.0, y_ = double 0.0, r_ = double 0.0)
  super(x_, y_)
  setR(r_)
  return

method Circle(p_ = RCPolymorphism.Point, r_ = double 0.0)
  this(p_.getX, p_.getY, r_)
  return

method Circle(c_ = Circle)
  this(c_.getX, c_.getY, c_.getR)
  return

method getR public returns double
  return r

method setR(r_ = double 0.0) inheritable
  r = r_
  return

method display public returns String
  hx = '@'Rexx(Integer.toHexString(hashCode())).right(8, 0)
  str = Rexx(className).left(10)':'hx': (x,y,r) = (' || -
        Rexx(getX()).format(null, 3)',' -
        Rexx(getY()).format(null, 3)',' -
        Rexx(getR()).format(null, 3)')'
  return str


type
  Point = object
    x, y: float

  Circle = object
    center: Point
    radius: float

# Constructors
proc createPoint(x, y = 0.0): Point =
  result.x = x
  result.y = y

proc createCircle(x, y = 0.0, radius = 1.0): Circle =
  result.center.x = x
  result.center.y = y
  result.radius = radius

var p1 = createPoint()
echo "p1: ", p1 # We use the default $ operator for printing
var p2 = createPoint(3, 4.2)
var p3 = createPoint(x = 2)
var p4 = createPoint(y = 2.5)

p2 = p4
p3 = createPoint()

var c1 = createCircle()
echo "c1: ", c1
var c2 = createCircle(2, 0.5, 4.2)
var c3 = createCircle(x = 2.1, y = 2)
var c4 = createCircle(radius = 10)

c1.center.x = 12
c1.radius = 5.2


bundle Default {
  class Point {
    @x : Int;
    @y : Int;

    New() {
      @x := 0;
      @y := 0;
    }

    New(x : Int, y : Int) {
      @x := x;
      @y := y;
    }

    New(p : Point) {
      @x := p->GetX();
      @y := p->GetY();
    }

    method : public : GetX() ~ Int {
      return @x;
    }

    method : public : GetY() ~ Int {
      return @y;
    }

    method : public : SetX(x : Int) ~ Nil {
      @x := x;
    }

    method : public : SetY(y : Int) ~ Nil {
      @y := y;
    }

    method : public : Print() ~ Nil {
      "Point"->PrintLine();
    }
  }

  class Circle from Point {
    @r : Int;

    New() {
      Parent();
      @r := 0;
    }

    New(p : Point) {
      Parent(p);
      @r := 0;
    }

    New(c : Circle) {
      Parent(c->GetX(), c->GetY());
      @r := c->GetR();
    }

    method : public : GetR() ~ Int {
      return @r;
    }

    method : public : SetR(r : Int) ~ Nil {
      @r := r;
    }

    method : public : Print() ~ Nil {
      "Circle"->PrintLine();
    }
  }

  class Poly {
    function : Main(args : String[]) ~ Nil {
      p := Point->New();
      c := Circle->New();
      p->Print();
      c->Print();
    }
  }
}


#import <Foundation/Foundation.h>

@interface RCPoint : NSObject {
  int x, y;
}
-(instancetype)initWithX:(int)x0;
-(instancetype)initWithX:(int)x0 andY:(int)y0;
-(instancetype)initWithPoint:(RCPoint *)p;
@property (nonatomic) int x;
@property (nonatomic) int y;
@end

@implementation RCPoint
@synthesize x, y;
-(instancetype)initWithX:(int)x0 { return [self initWithX:x0 andY:0]; }
-(instancetype)initWithX:(int)x0 andY:(int)y0 {
  if ((self = [super init])) {
    x = x0;
    y = y0;
  }
  return self;
}
-(instancetype)initWithPoint:(RCPoint *)p { return [self initWithX:p.x andY:p.y]; }
-(NSString *)description { return [NSString stringWithFormat:@"<RCPoint %p x: %d y: %d>", self, x, y]; }
@end

@interface RCCircle : RCPoint {
  int r;
}
-(instancetype)initWithCenter:(RCPoint *)p andRadius:(int)r0;
-(instancetype)initWithX:(int)x0 andY:(int)y0 andRadius:(int)r0;
-(instancetype)initWithCircle:(RCCircle *)c;
@property (nonatomic) int r;
@end

@implementation RCCircle
@synthesize r;
-(instancetype)initWithCenter:(RCPoint *)p andRadius:(int)r0 {
  if ((self = [super initWithPoint:p])) {
    r = r0;
  }
  return self;
}
-(instancetype)initWithX:(int)x0 andY:(int)y0 andRadius:(int)r0 {
  if ((self = [super initWithX:x0 andY:y0])) {
    r = r0;
  }
  return self;
}
-(instancetype)initWithCircle:(RCCircle *)c { return [self initWithX:c.x andY:c.y andRadius:c.r]; }
-(NSString *)description { return [NSString stringWithFormat:@"<RCCircle %p x: %d y: %d r: %d>", self, x, y, r]; }
@end

int main(int argc, const char *argv[]) {
  @autoreleasepool {

    NSLog(@"%@", [[RCPoint alloc] init]);
    NSLog(@"%@", [[RCPoint alloc] initWithX:3]);
    NSLog(@"%@", [[RCPoint alloc] initWithX:3 andY:4]);
    NSLog(@"%@", [[RCCircle alloc] init]);
    NSLog(@"%@", [[RCCircle alloc] initWithX:3]);
    NSLog(@"%@", [[RCCircle alloc] initWithX:3 andY:4]);
    NSLog(@"%@", [[RCCircle alloc] initWithX:3 andY:4 andRadius:7]);
    RCPoint *p = [[RCPoint alloc] initWithX:1 andY:2];
    NSLog(@"%@", [[RCCircle alloc] initWithPoint:p]);
    NSLog(@"%@", [[RCCircle alloc] initWithCenter:p andRadius:7]);
    NSLog(@"%d", p.x); // 1
    p.x = 8;
    NSLog(@"%d", p.x); // 8

  }
  return 0;
}


class point ?(x=0.0) ?(y=0.0) () = (* extra () used to erase the optional parameters *)
object (self)
  val mutable x = x
  val mutable y = y

  method x = x
  method y = y
  method set_x x' = x <- x'
  method set_y y' = y <- y'
  method print = Printf.sprintf "Point (%f, %f)" x y
  method copy = {< >}
end

class circle ?(r=1.0) ?(x=0.0) ?(y=0.0) () =
object (self)
  inherit point ~x:x ~y:y ()
  val mutable r = r

  method r = r
  method set_r r' = r <- r'
  method print = Printf.sprintf "Circle (%f, %f, %f)" r x y
end

let print x = print_endline x#print

let () =
  let p = new point () in
  let c = new circle () in
    print c;
    print p;
    c#set_x 10.0;
    print c;
    print (new point ~y:2.1 ())


Object Class new: Point(x, y)
Point method: initialize(x, y)  x := x y := y ;
Point method: _x   @x ;
Point method: _y   @y ;
Point method: <<   "(" << @x << ", " << @y << ")" << ;

Object Class new: Circle(x, y, r)
Circle method: initialize(x, y, r)  x := x y := y r := r ;
Circle method: _x  @x ;
Circle method: _y  @y ;
Circle method: _r  @r ;
Circle method: <<  "(" << @x << ", " << @y << ", " << @r << ")" << ;

Circle classMethod: newFromPoint(aPoint, r)  self new(aPoint _x, aPoint _y, r) ;


p = .point~new(3,2)
c = .circle~new(,2,6)

p~print
c~print

::class point
::method init
  expose x y
  use strict arg x = 0, y = 0   -- defaults to 0 for any non-specified coordinates

::attribute x
::attribute y

::method print
  expose x y
  say "A point at location ("||x","y")"

::class circle subclass point
::method init
  expose radius
  use strict arg x = 0, y = 0, radius = 0
  self~init:super(x, y)        -- call superclass constructor

::attribute radius

::method print
  expose radius
  say "A circle of radius" radius "centered at location ("||self~x","self~y")"


type tpoint  float xx,yy
type tcircle float xx,yy,rr

'==========
class point
'==========
'
has tpoint
'
method constructor  (float x=0,y=0){this<=x,y}
method destructor   {}
method V() as point {return @this}
method V(tpoint*a)  {this<=a.xx,a.yy}
method V(point *a)  {this<=a.xx,a.yy}
method X() as float {return xx}
method Y() as float {return yy}
method X(float a)   {xx=a}
method Y(float a)   {yy=a}
method clear()      {this<=.0,.0}
method show() as string {return "x=" xx ", y=" yy }
'
end class

'===========
class circle
'===========
'
has point
float rr
'
method constructor  (float x=.0,y=.0,r=1.0){this<=x,y,r}
method V(tcircle*a) {this<=a.xx,a.yy,a.rr}
method V(circle *a) {this<=a.xx,a.yy,a.rr}
method R() as float {return rr}
method R(float a)   {rr=a}
method clear()      {this<=.0,.0,.0}
method show() as string {return "x=" xx ", y=" yy ", r=" rr }
'
end class

'=====
'TESTS
'=====

new circle ca (r=.5)
new circle cb (x=10,y=10)
new circle cc (10,10,0.5)

cb.r="7.5" 'will convert a string value

cb.y=20

print cb.show 'result x=10, y=20 ,r=7.5

del ca : del cb : del cc


class Point
   feat
      x
      y

   meth init(x:X<=0.0 y:Y<=0.0)
      self.x = X
      self.y = Y
   end

   meth print
      {System.showInfo
       "Point("#
       "x:"#self.x#
       ", y:"#self.y#
       ")"}
   end
end

class Circle
   feat
      center
      r

   meth init(center:C<={New Point init} r:R<=1.0)
      self.center = C
      self.r = R
   end

   meth print
      {System.showInfo
       "Circle("#
       "x:"#self.center.x#
       ", y:"#self.center.y#
       ", r:"#self.r#
       ")"}
   end
end


{
     package Point;
     use Class::Spiffy -base;
     use Clone qw(clone);

     sub _print {
         my %self = %{shift()};
         while (my ($k,$v) = each %self) {
             print "$k: $v\n";
         }
     }

     sub members {
         no strict;
         grep {
             1 == length and defined *$_{CODE}
         } keys %{*{__PACKAGE__."\::"}};
     }

     sub new {
         my $class = shift;
         my %param = @_;
         $param{$_} = 0 for grep {!defined $param{$_}} members;
         bless \%param, $class;
     }

     sub copy_constructor {
         clone shift;
     }

     sub copy_assignment {
         my $self = shift;
         my $from = shift;
         $self->$_($from->$_) for $from->members;
     }

     field 'x';
     field 'y';
}

{
     package Circle;
     use base qw(Point);
     field 'r';
}

{
     package main;
     $_->_print, print "\n" for (
        Point->new,
        Point->new(x => 2),
        Point->new(y => 3),
        Point->new(x => 8, y => -5),
     );
     my $p1 = Point->new(x => 8, y => -5);

     my $p2 = $p1->copy_constructor;
     print "we are really different objects, not just references ".
           "to the same instance\n" unless \$p1 eq \$p2;

     # accessors autogenerated
     $p1->x(1);
     $p1->y(2);
     print $p1->x, "\n";
     print $p1->y, "\n";

     $p2->copy_assignment($p1);
     print $p2->x, "\n";
     print $p2->y, "\n";
     print "we now have the same values, but we are still ".
           "different objects\n" unless \$p1 eq \$p2;

     $_->_print, print "\n" for (
        Circle->new,
        Circle->new(x => 1),
        Circle->new(y => 2),
        Circle->new(r => 3),
        Circle->new(x => 4, y => 5),
        Circle->new(x => 6, r => 7),
        Circle->new(y => 8, r => 9),
        Circle->new(x => 1, y => 2, r => 3),
     );

     my $c = Circle->new(r => 4);
     print $c->r, "\n"; # accessor autogenerated
}


type point(object o)
    return sequence(o) and length(o)=2 and atom(o[1]) and atom(o[2])
end type

function new_point(atom x=0, atom y=0)
    return {x,y}
end function

type circle(object o)
    return sequence(o) and length(o)=2 and point(o[1]) and atom(o[2])
end type

function new_circle(object x=0, atom y=0, atom r=0)
    if point(x) then
        r = y           -- assume r got passed in y
        return {x,r}    -- {point,r}
    end if
    return {{x,y},r}    -- {point,r}
    -- (or {new_point(x,y),r} if you prefer)
end function

point p = new_point(4,5)
circle c1 = new_circle(p,6),
       c2 = new_circle(4,5,6}
?c1
?c2


class Point
{
  protected $_x;

  protected $_y;

  public function __construct()
  {
    switch( func_num_args() )
    {
      case 1:
        $point = func_get_arg( 0 );
        $this->setFromPoint( $point );
        break;
      case 2:
        $x = func_get_arg( 0 );
        $y = func_get_arg( 1 );
        $this->setX( $x );
        $this->setY( $y );
        break;
      default:
        throw new InvalidArgumentException( 'expecting one (Point) argument or two (numeric x and y) arguments' );
    }
  }

  public function setFromPoint( Point $point )
  {
    $this->setX( $point->getX() );
    $this->setY( $point->getY() );
  }

  public function getX()
  {
    return $this->_x;
  }

  public function setX( $x )
  {
    if( !is_numeric( $x ) )
    {
      throw new InvalidArgumentException( 'expecting numeric value' );
    }

    $this->_x = (float) $x;
  }

  public function getY()
  {
    return $this->_y;
  }

  public function setY( $y )
  {
    if( !is_numeric( $y ) )
    {
      throw new InvalidArgumentException( 'expecting numeric value' );
    }

    $this->_y = (float) $y;
  }

  public function output()
  {
    echo $this->__toString();
  }

  public function __toString()
  {
    return 'Point [x:' . $this->_x . ',y:' . $this->_y . ']';
  }
}


(class +Point)
# x y

(dm T (X Y)
   (=: x (or X 0))
   (=: y (or Y 0)) )

(dm print> ()
   (prinl "Point " (: x) "," (: y)) )

(class +Circle +Point)
# r

(dm T (X Y R)
   (super X Y)
   (=: r (or R 0)) )

(dm print> ()
   (prinl "Circle " (: x) "," (: y) "," (: r)) )


uses objectclass;
define :class Point;
    slot x = 0;
    slot y = 0;
enddefine;

define :class Circle;
    slot x = 0;
    slot y = 0;
    slot r = 1;
enddefine;

define :method print(p : Point);
    printf('Point(' >< x(p) >< ', ' >< y(p) >< ')\n');
enddefine;

define :method print(p : Circle);
    printf('Circle(' >< x(p) >< ', ' >< y(p) >< ', ' >< r(p) >< ')\n');
enddefine;


% Point
point_construct(X, Y, point(X1,Y1)) :-
	default(X, X1),
	default(Y, Y1).

% Circle
circle_construct(X, Y, R, circle(X1,Y1,R1)) :-
	default(X, X1),
	default(Y, Y1),
	default(R, R1).

% Accessors for general X,Y
% individual getters/setters can be made but it is not required
shape_x_y_set(point(_,_), X, Y, point(X,Y)).
shape_x_y_set(circle(_,_,R), X, Y, circle(X,Y,R)).

% Accessors for R
cicle_r_set(circle(X,Y,_), R, circle(X,Y,R)).

% Print
print_shape(point(X,Y)) :- format('Point (~p,~p)', [X,Y]).
print_shape(circle(X,Y,R)) :- format('Circle (~p,~p,~p)', [X,Y,R]).

% Default values for constructor (default to 0).
default(N, 0) :- var(N).
default(N, N) :- number(N).

% Tests
test_point :-
	point_construct(2,3,P),
	test_poly(P).

test_circle :-
	circle_construct(3,4,_,C),
	cicle_r_set(C, 5, C1),
	test_poly(C1).

test_poly(T) :-
	shape_x_y_set(_, X, Y, T),
	X1 is X * 2,
	Y1 is Y * 2,
	shape_x_y_set(T, X1, Y1, T1),
	print_shape(T1).


Class MyPoint

  BeginProtect
    x.i
    y.i
  EndProtect

  Public Method GetX()
    MethodReturn This\X
  EndMethod

  Public Method GetY()
    MethodReturn This\Y
  EndMethod

  Public Method SetX(n)
    This\X=n
  EndMethod

  Public Method SetY(n)
    This\Y=n
  EndMethod

  Public Method Print()
    PrintN("Point")
  EndMethod

  Public Method Init(x=0,y=0)
    This\x=x
    This\y=y
  EndMethod
EndClass

Class Circle Extends MyPoint

  Protect  Radie.i

  Public Method Circel(x=0, y=0, r=0)
    This\X  = x
    This\y  = y
    This\Radie=r
  EndMethod

  Public Method GetRadie()
    MethodReturn This\Radie
  EndMethod

  Public Method SetRadie(n)
    This\Radie = n
  EndMethod

  Public Method Print()
    PrintN("Circle: "+ _
    " X= "+Str(This\X)+ _
    " Y= "+Str(This\Y)+ _
    " R= "+Str(This\Radie))
  EndMethod

EndClass


class Point(object):
    def __init__(self, x=0.0, y=0.0):
        self.x = x
        self.y = y
    def __repr__(self):
        return '<Point 0x%x x: %f y: %f>' % (id(self), self.x, self.y)

class Circle(object):
    def __init__(self, center=None, radius=1.0):
        self.center = center or Point()
        self.radius = radius
    def __repr__(self):
        return '<Circle 0x%x x: %f y: %f radius: %f>' % (
            id(self), self.center.x, self.center.y, self.radius)


setClass("point",
   representation(
      x="numeric",
      y="numeric"),
   prototype(
      x=0,
      y=0))

# Instantiate class with some arguments
p1 <- new("point", x=3)
# Access some values
[email protected]    # 3
# Define a print method
setMethod("print", signature("point"),
   function(x, ...)
   {
      cat("This is a point, with location, (", [email protected], ",", [email protected], ").\n")
   })
print(p1)

# Define a circle class
setClass("circle",
   representation(
      centre="point",
      r="numeric"),
   prototype(
      centre=new("point"),
      r=1))
circS4 <- new("circle", r=5.5)
# Access some values
[email protected]    # 5.5
[email protected]@x   # 0
# Define a print method
setMethod("print", signature("circle"),
   function(x, ...)
   {
      cat("This is a circle, with radius", [email protected], "and centre (", [email protected]@x, ",", [email protected]@y, ").\n")
   })
print(circS4)


#lang racket
(define point%
  (class* object% (writable<%>) (super-new) (init-field [x 0] [y 0])
    (define/public (copy) (new point% [x x] [y y]))
    (define/public (show) (format "<point% ~a ~a>" x y))
    (define/public (custom-write out) (write (show) out))
    (define/public (custom-display out) (display (show) out))))

(define circle%
  (class point% (super-new) (inherit-field x y) (init-field [r 0])
    (define/override (copy) (new circle% [x x] [y y] [r r]))
    (define/override (show) (format "<circle% ~a ~a>" (super show) r))
    (define/override (custom-write out) (write (show) out))
    (define/override (custom-display out) (display (show) out))))


class Point {
    has Real $.x is rw = 0;
    has Real $.y is rw = 0;
    method Str { $ }
}

class Circle {
    has Point $.p is rw = Point.new;
    has Real $.r is rw = 0;
    method Str { $ }
}

my $c = Circle.new(p => Point.new(x => 1, y => 2), r => 3);
say $c;
$c.p.x = (-10..10).pick;
$c.p.y = (-10..10).pick;
$c.r   = (0..10).pick;
say $c;


class Point
  attr_accessor :x,:y
  def initialize(x=0, y=0)
    self.x = x
    self.y = y
  end
  def to_s
    "Point at #{x},#{y}"
  end
end

# When defining Circle class as the sub-class of the Point class:
class Circle < Point
  attr_accessor :r
  def initialize(x=0, y=0, r=0)
    self.x = x
    self.y = y
    self.r = r
  end
  def to_s
    "Circle at #{x},#{y} with radius #{r}"
  end
end


object PointCircle extends App {

  class Point(x: Int = 0, y: Int = 0) {

    def copy(x: Int = this.x, y: Int = this.y): Point = new Point(x, y)

    override def toString = s"Point x: $x,  y: $y"
  }

  object Point {
    def apply(x: Int = 0, y: Int = 0): Point = new Point(x, y)
  }

  case class Circle(x: Int = 0, y: Int = 0, r: Int = 0) extends Point(x, y) {

    def copy(r: Int): Circle = Circle(x, y, r)

    override def toString = s"Circle x: $x,  y: $y,  r: $r"
  }

  val p = Point()
  val c = Circle()
  println("Instantiated ", p)
  println("Instantiated ", c)

  val q = Point(5, 6)
  println("Instantiated ", q)
  val r = q.copy(y = 7) // change y coordinate
  println(r, " changed y coordinate")

  val d = Circle(5, 6, 7)
  println("Instantiated ", d)
  val e = d.copy(r = 8) // change radius
  println(e, " changed radius")

}


$ include "seed7_05.s7i";

const type: GraphicObj is new interface;

const proc: print (in GraphicObj: aGraphicObj) is DYNAMIC;

const type: Point is new struct
    var integer: x is 0;
    var integer: y is 0;
  end struct;

type_implements_interface(Point, GraphicObj);

const func Point: Point (in integer: x, in integer: y) is func
  result
    var Point: newPoint is Point.value;
  begin
    newPoint.x := x;
    newPoint.y := y;
  end func;

const proc: print (in Point: aPoint) is func
  begin
    writeln("Point(" <& aPoint.x <& ", " <& aPoint.y <& ")");
  end func;

const type: Circle is sub Point struct
    var integer: r is 0;
  end struct;

type_implements_interface(Circle, GraphicObj);

const func Circle: Circle (in integer: x, in integer: y, in integer: r) is func
  result
    var Circle: newCircle is Circle.value;
  begin
    newCircle.x := x;
    newCircle.y := y;
    newCircle.r := r;
  end func;

const proc: print (in Circle: aCircle) is func
  begin
    writeln("Circle(" <& aCircle.x <& ", " <& aCircle.y <& ", " <& aCircle.r <& ")");
  end func;

const proc: main is func
  local
    var Point: pnt is Point(1, 2);
    var Circle: circ is Circle(3, 4, 5);
    var GraphicObj: graph is Point.value;
  begin
    graph := pnt;
    print(graph);
    graph := circ;
    print(graph);
  end func;


traits point = (|
  parent* = traits clonable.
  printString = ('Point(', x asString, ':', y asString, ')').
  |)

point = (|
  parent* = traits point.
  x <- 0.
  y <- 0
  |)

traits circle = (|
  parent* = traits clonable.
  printString = ('Circle(', center asString, ',', r asString, ')').
  |)

circle = (|
  parent* = traits circle.
  center <- point copy.
  r <- 0
  |)


class Point(x=0, y=0) {

}

class Circle(x=0, y=0, r=0) {

}

func pp(Point obj) {
    say "Point at #{obj.x},#{obj.y}";
}

func pp(Circle obj) {
    say "Circle at #{obj.x},#{obj.y} with radius #{obj.r}";
}


type mypoint(mypoint) embed export
  embed
  integer x
  integer y

  reference
  function copy
  function print
end type

function mypoint.new(mypoint me, integer x=0, integer y=0)
  me.x = x
  me.y = y
end function me

function mypoint.copy(mypoint me)
  mypoint p

  p [email protected] mypoint.new(me.x, me.y)
end function p

function mypoint.print(mypoint me)
end function "mypoint"

type circle(mypoint) embed export
  reference
  mypoint midpoint resolve

  embed
  integer radius

  reference
  function copy
  function print
end type

function circle.new(circle me, integer x=0, integer y=0, integer radius=0, mypoint midpoint)
  if midpoint [email protected]= .nul
    me.midpoint [email protected] mypoint.new(x, y)
  else
    me.x = midpoint.x
    me.y = midpoint.y
  end if

  me.radius = radius
end function me

function circle.copy(circle me)
  circle c

  c [email protected] circle.new(radius=me.radius, midpoint=me.midpoint)
end function c

function circle.print(circle me)
end function "circle"

function main()
  type(mypoint) p, c
  string result

  p [email protected] mypoint.new()
  c [email protected] circle.new()

  result = p.print() + "{d}{a}" + c.print() + "{d}{a}"
end function result


!Object subclass: #Point
  instanceVariableNames: 'x y'
  classVariableNames: ''
  poolDictionaries: ''
  category: 'polymorphism' !

!Point class methodsFor: 'instance creation'!
new
  ^self newBasic x := 0; y := 0 ! !

!Point class methodsFor: 'instance creation'!
x: x y: y
  ^self newBasic x := x; y := y ! !

!Point methodsFor: 'member access'!
x
  ^x ! !

!Point methodsFor: 'member access'!
y
  ^y ! !

!Point methodsFor: 'member access'!
x: x
  ^self x := x ! !

!Point methodsFor: 'member access'!
y: y
  ^self y := y ! !

!Point methodsFor: 'member access'!
x: x y: y
  ^self x := x; y := y ! !

!Point methodsFor: 'polymorphism test'!
print
  Transcript show: x; space; show: y ! !

!Object subclass: #Circle
  instanceVariableNames: 'center r'
  classVariableNames: ''
  poolDictionaries: ''
  category: 'polymorphism' !

!Circle class methodsFor: 'instance creation'!
new
  ^self newBasic center := Point new; r := 0 ! !

!Circle class methodsFor: 'instance creation'!
radius: radius
  ^self newBasic center := Point new; r := radius ! !

!Circle class methodsFor: 'instance creation'!
at: point radius: r
  ^self newBasic center := point; r := r ! !

!Circle methodsFor: 'member access'!
center
  ^center ! !

!Circle methodsFor: 'member access'!
x: x y: y
  ^self center x: x y: y ! !

!Circle methodsFor: 'member access'!
radius
  ^r ! !

!Circle methodsFor: 'member access'!
radius: radius
  ^self r := radius ! !

!Circle methodsFor: 'polymorphism test'!
print
  Transcript show: center; space; show: radius ! !


class RCPoint : Printable {
  var x: Int
  var y: Int
  init(x: Int = 0, y: Int = 0) {
    self.x = x
    self.y = y
  }
  convenience init(p: RCPoint) {
    self.init(x:p.x, y:p.y)
  }
  var description: String {
  return "<RCPoint x: \(self.x) y: \(self.y)>"
  }
}

class RCCircle : RCPoint {
  var r: Int
  init(p: RCPoint, r: Int = 0) {
    self.r = r
    super.init(x:p.x, y:p.y)
  }
  init(x: Int = 0, y: Int = 0, r: Int = 0) {
    self.r = r
    super.init(x:x, y:y)
  }
  convenience init(c: RCCircle) {
    self.init(x:c.x, y:c.y, r:c.r)
  }
  override var description: String {
    return "<RCCircle x: \(x) y: \(y) r: \(r)>"
  }
}

println(RCPoint())
println(RCPoint(x:3))
println(RCPoint(x:3, y:4))
println(RCCircle())
println(RCCircle(x:3))
println(RCCircle(x:3, y:4))
println(RCCircle(x:3, y:4, r:7))
let p = RCPoint(x:1, y:2)
println(RCCircle(p:p))
println(RCCircle(p:p, r:7))
println(p.x) // 1
p.x = 8
println(p.x) // 8


package require TclOO
oo::class create Point {
    variable X Y
    constructor {x y} {
        set X $x
        set Y $y
    }
    method x args {
        set X {*}$args
    }
    method y args {
        set Y {*}$args
    }
    method print {} {
        puts "Point($X,$Y)"
    }
    method copy {} {
        set copy [oo::copy [self]]
        $copy x $X
        $copy y $Y
        return $copy
    }
}
oo::class create Circle {
    superclass Point
    variable R
    constructor {x y radius} {
        next $x $y
        set R $radius
    }
    method radius args {
        set R {*}$args
    }
    method print {} {
        puts "Circle([my x],[my y],$R)"
    }
    method copy {} {
        set copy [next]
        $copy radius $R
        return $copy
    }
}
# No destructors: unneeded by these classes

set p [Point  new 1.0 2.0]
set c [Circle new 3.0 4.0 5.0]
set cCopy [$c copy]
puts "$p is at ([$p x],[$p y])"
$c radius 1.5
set objects [list $p $c $cCopy]
foreach o $objects {
    $o print
}


class Point {
   var x
   var y
   new(ax, ay) {
       this.x = ax
       this.y = ay
   }
   new(point) {
        this(point.x, point.y)
   }
   method getX() { return x }
   method setX(newX) { x = newX }

   method getY() { return y }
   method setY(newY) { y = newY }

   method print() {
       console.println("Point")
   }
}

class Circle extends Point {
   var r

   new() { this(0,0,0) }
   new(point, aR) { super(point) ; r = aR }
   new(aX, aY, aR) { super(aX, aY); r = aR }

   method getR() { return r }
   method setR(newR) { r = newR }

   method print() {
       console.println("Circle")
   }

}

program polymorphism {
    val p = new Point()
    val c = new Circle()
    p.print()
    c.print()
}


class Point {
    construct new(x, y) {
        _x = x
        _y = y
    }

    static new(x) { new(x, 0) }
    static new()  { new(0, 0) }

    static fromPoint(p) { new(p.x, p.y) }

    x { _x }
    y { _y }

    print() { System.print("Point at (%(_x), %(_y))") }
}

class Circle is Point {
    construct new(x, y, r) {
        super(x, y)
        _r = r
    }

    static new(x, r) { new(x, 0, r) }
    static new(x)    { new(x, 0, 0) }
    static new()     { new(0, 0, 0) }

    static fromCircle(c) { new(c.x, c.y, c.r) }

    r { _r }

    print() { System.print("Circle at center(%(x), %(y)), radius %(_r)") }
}

var points = [Point.new(), Point.new(1), Point.new(2, 3), Point.fromPoint(Point.new(3, 4))]
for (point in points) point.print()
var circles = [
    Circle.new(), Circle.new(1), Circle.new(2, 3),
    Circle.new(4, 5, 6), Circle.fromCircle(Circle.new(7, 8, 9))
]
for (circle in circles) circle.print()


class Point{var x,y;
   fcn init(xyOrPoint=0,_=0){
      if(Point.isInstanceOf(xyOrPoint)) set(xyOrPoint);
      else x,y=vm.arglist.apply("toFloat")}
   fcn set(p){x=p.x;y=p.y}
   fcn toString{"(%d,%d)".fmt(x,y)}
}
class Circle{var center, radius;
   fcn init(a=0.0,b=0.0,r=1.0){
      switch [arglist]{
         case(Circle){ center=Point(a.center); radius=a.radius }
	 case(Point) { center=Point(a); radius=b.toFloat(); }
	 else { center=Point(a,b); radius=r.toFloat(); }
      }
   }
   fcn copy{self(self)}
   fcn toString{"(%s,%d)".fmt(center.toString(),radius)}
}
   // see if various constructors work
Point(); Point(1); Point(1,2), Point(Point());
Circle(); Circle(1); Circle(1,2); Circle(1,2,3);
Circle(Point()); Circle(Point(),1);
Circle(Circle());

c:=Circle(1,2,3);
c.println(); c.center.println();
c.copy().println();


SoundPlay, %A_WinDir%\Media\tada.wav, wait
SoundPlay, %A_WinDir%\Media\Windows XP Startup.wav, wait

; simulaneous play may require a second script

SoundPlay, %A_WinDir%\Media\tada.wav
SoundPlay, Nonexistent  ; stop before finishing

SoundSet +10  ; increase volume by 10%
Loop, 2
    SoundPlay, %A_WinDir%\Media\tada.wav, wait


 @echo off
 :main
 cls
 echo Drag a .wav file there and press enter
 set /p file1=
 sndrec32 /embedding /play /close %file1%
 cls
 echo Drag a second .wav file and both will play together
 set /p file2=
 sndrec32 /embedding /play /close %file1% | sndrec32 /embedding /play /close %file2%
 echo Thanks
 pause>nul
 exit


      SND_LOOP = 8
      SND_ASYNC = 1
      SND_FILENAME = &20000

      PRINT "Playing a MIDI file..."
      *PLAY C:\windows\media\canyon.mid

      WAIT 300
      PRINT "Playing the Windows TADA sound quietly..."
      wave$ = "\windows\media\tada.wav"
      volume% = 10000
      SYS "waveOutSetVolume", -1, volume% + (volume% << 16)
      SYS "PlaySound", wave$, 0, SND_FILENAME + SND_ASYNC

      WAIT 300
      PRINT "Playing the Windows TADA sound loudly on the left channel..."
      volume% = 65535
      SYS "waveOutSetVolume", -1, volume%
      SYS "PlaySound", wave$, 0, SND_FILENAME + SND_ASYNC

      WAIT 300
      PRINT "Playing the Windows TADA sound loudly on the right channel..."
      volume% = 65535
      SYS "waveOutSetVolume", -1, volume% << 16
      SYS "PlaySound", wave$, 0, SND_FILENAME + SND_ASYNC

      WAIT 300
      PRINT "Looping the Windows TADA sound on both channels..."
      volume% = 65535
      SYS "waveOutSetVolume", -1, volume% + (volume% << 16)
      SYS "PlaySound", wave$, 0, SND_FILENAME + SND_ASYNC + SND_LOOP

      WAIT 300
      SYS "PlaySound", 0, 0, 0
      PRINT "Stopped looping..."

      WAIT 300
      SOUND OFF
      PRINT "Stopped MIDI."

      END


using System;
using System.Threading;
using System.Media;

class Program
{
    static void Main(string[] args)
    {
        //load sound file
        SoundPlayer s1 = new SoundPlayer(); //
        s1.SoundLocation = file; // or "s1 = new SoundPlayer(file)"

        //play
        s1.Play();

        //play for 0.1 seconds
        s1.Play();
        Thread.Sleep(100);
        s1.Stop();

        //loops
        s1.PlayLooping();
    }
}


program PlayRecordedSounds;

{$APPTYPE CONSOLE}

uses MMSystem;

begin
  sndPlaySound('SoundFile.wav', SND_NODEFAULT OR SND_ASYNC);
end.


package main

import (
    "log"
    "os"
    "os/exec"
)

func main() {
    args := []string{
        "-m", "-v", "0.75", "a.wav", "-v", "0.25", "b.wav",
        "-d",
        "trim", "4", "6",
        "repeat", "5",
    }
    cmd := exec.Command("sox", args...)
    cmd.Stdout = os.Stdout
    cmd.Stderr = os.Stderr
    if err := cmd.Run(); err != nil {
        log.Fatal(err)
    }
}


'Supports .mid and .wav natively
'Midi may be played simultaneously
'with .wav but only one .wav voice
'is supported. Multiple voices can
'be achieved via API or Dlls such
'as FMOD or Wavemix

'to play a .mid and return its length
playmidi "my.mid", midiLength

'check where we are in .mid
if midipos()<midiLength then
    print "Midi still playing"
else
    print "Midi ended"
end if

'to stop a playing .mid
stopmidi

'to play a .wav and wait for it to end
playwave "my.wav"

'to play a .wav and continue procesing
playwave "my.wav", async

'to loop a .wav and continue processing
playwave "my.wav",loop

'to silence any .wav
playwave ""
or
playwave "next.wav"

'to adjust .wav vol
'set left and right full on
left =65535
right=65535
dwVol=right*65536+left
calldll #winmm, "waveOutSetVolume", 0 as long, _
    dwVol as long, ret as long


using Distributed, Gtk, LibSndFile, MP3
using FileIO: load

function recordingplayerapp(numfiles=2)
    # create window and widgets
    win = GtkWindow("Recorded Sound Player", 400, 400) |> (GtkFrame() |> (vbox = GtkBox(:v)))
    playprocess = @async(0)
    filenames = fill("", numfiles)
    filedurations = zeros(numfiles)
    filebutton = GtkButton[]
    for i in 1:numfiles
        push!(filebutton, GtkButton("Select File $i"))
        push!(vbox, filebutton[i])
    end

    sequencebutton = GtkButton("Play In Sequence")
    simulbutton = GtkButton("Play Simultaneously")
    seqhandlerid = zero(UInt32)
    simulhandlerid = zero(UInt32)
    stopbutton = GtkButton("Stop Play")

    labelstart = GtkLabel("Start Position")
    startcontrol = GtkScale(false, 1:100)
    set_gtk_property!(startcontrol, :hexpand, true)
    adj = GtkAdjustment(startcontrol)

    labelstop = GtkLabel("Stop Position")
    endcontrol = GtkScale(false, 1:100)
    set_gtk_property!(endcontrol, :hexpand, true)
    adj = GtkAdjustment(endcontrol)

    labelrepeat = GtkLabel("Repeats")
    repeats = GtkScale(false, 0:8)
    set_gtk_property!(repeats, :hexpand, true)
    set_gtk_property!(GtkAdjustment(repeats), :value, 0)

    foreach(x -> push!(vbox, x), [sequencebutton, simulbutton, stopbutton, labelstart,
                                  startcontrol, labelstop, endcontrol, labelrepeat, repeats])
    twobox = GtkBox(:h)
    push!(vbox, twobox)

    fboxes = GtkBox[]
    volumecontrols = GtkScale[]
    startcontrols = GtkScale[]
    endcontrols = GtkScale[]
    loopcontrols = GtkScale[]
    for i in 1:numfiles
        push!(fboxes, GtkBox(:v))
        push!(twobox, fboxes[i])

        push!(volumecontrols, GtkScale(false, 0.0:0.05:1.0))
        set_gtk_property!(volumecontrols[i], :hexpand, true)
        adj = GtkAdjustment(volumecontrols[i])
        set_gtk_property!(adj, :value, 0.5)
        push!(fboxes[i], GtkLabel("Volume Stream $i"), volumecontrols[i])

        signal_connect(filebutton[i], :clicked) do widget
            filenames[i] = open_dialog("Pick a sound or music file")
            filenames[i] = replace(filenames[i], "\\" => "/")
            set_gtk_property!(filebutton[i], :label, filenames[i])
            if count(x -> x != "", filenames) > 0
                signal_handler_unblock(sequencebutton, seqhandlerid)
            end
            if count(x -> x != "", filenames) > 1
                signal_handler_unblock(simulbutton, simulhandlerid)
            end
            if occursin(r"\.mp3$", filenames[i])
                buf = load(filenames[i])
                filedurations[i] = MP3.nframes(buf) / MP3.samplerate(buf)
            else
                buf = load(filenames[i])
                filedurations[i] = LibSndFile.nframes(buf) / LibSndFile.samplerate(buf)
            end
            adj = GtkAdjustment(startcontrol)
            set_gtk_property!(adj, :lower, 0.0)
            set_gtk_property!(adj, :upper, maximum(filedurations))
            set_gtk_property!(adj, :value, 0.0)
            adj = GtkAdjustment(endcontrol)
            set_gtk_property!(adj, :lower, 0.0)
            set_gtk_property!(adj, :upper, maximum(filedurations))
            set_gtk_property!(adj, :value, maximum(filedurations))
        end
    end

    # run play after getting parameters from widgets
    function play(simul::Bool)
        args = simul ? String["-m"] : String[]
        tstart = Gtk.GAccessor.value(startcontrol)
        tend = Gtk.GAccessor.value(endcontrol)
        for i in 1:numfiles
            if filenames[i] != ""
                volume = Gtk.GAccessor.value(volumecontrols[i])
                push!(args, "-v", string(volume), filenames[i])
            end
        end
        repeat = Gtk.GAccessor.value(repeats)
        if repeat != 0
            push!(args, "repeat", string(repeat))
        end
        if !(tstart ≈ 0.0 && tend ≈ maximum(filedurations))
            push!(args, "trim", string(tstart), string(tend))
        end
        playprocess = run(`play $args`; wait=false)
        clearfornew()
    end

    function clearfornew()
        signal_handler_block(sequencebutton, seqhandlerid)
        if count(x -> x != "", filenames) > 1
            signal_handler_block(simulbutton, simulhandlerid)
        end
        filenames = fill("", numfiles)
        filedurations = zeros(numfiles)
        foreach(i -> set_gtk_property!(filebutton[i], :label, "Select File $i"), 1:numfiles)
        set_gtk_property!(GtkAdjustment(repeats), :value, 0)
        showall(win)
    end

    killplay(w) = kill(playprocess)

    playsimul(w) = play(true)
    playsequential(w) = play(false)

    seqhandlerid = signal_connect(playsequential, sequencebutton, :clicked)
    signal_handler_block(sequencebutton, seqhandlerid)
    simulhandlerid = signal_connect(playsimul, simulbutton, :clicked)
    signal_handler_block(simulbutton, simulhandlerid)
    signal_connect(killplay, stopbutton, :clicked)

    cond = Condition()
    endit(w) = notify(cond)
    signal_connect(endit, win, :destroy)
    showall(win)
    wait(cond)
end

recordingplayerapp()


a = Import["sound1.flac","FLAC"]; b = Import["sound2.flac","FLAC"];

ListPlay[a, {t, 0, 10}]; ListPlay[b, {t, 0, 10}];

ListPlay[{a,b}, {t, 0, 10}];

Stopping before the end can be done using the GUI or by reducing the parameter range of the ListPlay function.

While[True,ListPlay[{a,b}, {t, 0, 10}];]

ListPlay[{0.5*a, 0.3*b}, {t, 0, 10}];


import osproc

let args = ["-m", "-v", "0.75", "a.wav", "-v", "0.25", "b.wav",
            "-d",
            "trim", "4", "6",
            "repeat", "5"]
echo execProcess("sox", args = args, options = {poStdErrToStdOut, poUsePath})


integer xPlaySound = 0

procedure play_sound()--string filename)
    if platform()=WINDOWS then
        string filename = join_path({getenv("SYSTEMROOT"),"Media","chord.wav"})
        if xPlaySound=0 then
            atom winmm = open_dll("winmm.dll")
            xPlaySound = define_c_proc(winmm,"sndPlaySoundA",{C_PTR,C_INT})
        end if
        c_proc(xPlaySound,{filename,1})
    elsif platform()=LINUX then
        system("paplay chimes.wav")
        system("paplay chord.wav")
    end if
end procedure
play_sound()


InitSound()
; We need this to use Sound functions
UseOGGSoundDecoder()
; Now we can not only load wav sound files, but also ogg encoded ones.
;   With movie library more formats can be played (depends on system) but you cannot
;   handle them with Sound functions
If Not LoadSound(1,"Path/to/Sound/1.ogg") Or Not LoadSound(2,"Path/to/Sound/2.wav")
MessageRequester("Error","One of our sounds could not be loaded"+Chr(10)+"Use Debugger to check which one")
EndIf

;- simultaneous playing
PlaySound(1)
PlaySound(2)

;- manipulating sounds
Delay(1000)
; pause for one second, to let user hear something
SoundVolume(1,90)
SoundVolume(2,60)
; reduce volume of the sounds a bit
SoundPan(1,-80)
SoundPan(2,100)
; Sound 1 mostly left speaker, sound 2 only right speaker
SoundFrequency(1,30000)
; play sound one faster
Delay(1000)
; pause for one second, to let user hear effects of previous actions

;- stopping while playing
StopSound(-1)
; value -1 stops all playing sounds
PlaySound(1,#PB_Sound_Loop)
; continous looping without glitch

;suitable for 2D games and music playing.
; TODO: There is a Sound3D library for 3D Games, needs to be decribed here too


import time
from pygame import mixer

mixer.init(frequency=16000) #set frequency for wav file
s1 = mixer.Sound('test.wav')
s2 = mixer.Sound('test2.wav')

#individual
s1.play(-1)         #loops indefinitely
time.sleep(0.5)

#simultaneously
s2.play()          #play once
time.sleep(2)
s2.play(2)         #optional parameter loops three times
time.sleep(10)

#set volume down
s1.set_volume(0.1)
time.sleep(5)

#set volume up
s1.set_volume(1)
time.sleep(5)

s1.stop()
s2.stop()
mixer.quit()


#Setup
# Warning: The example files are Windows specific.
library(sound)
media_dir <- file.path(Sys.getenv("SYSTEMROOT"), "Media")
chimes <- loadSample(file.path(media_dir, "chimes.wav"))
chord <- loadSample(file.path(media_dir, "chord.wav"))

# Play sequentially
play(appendSample(chimes, chord))

# Play simultaneously
play(chimes + chord)

# Stopping before the end
play(cutSample(chimes, 0, 0.2))

#Looping
for(i in 1:3) play(chimes)                 #leaves a gap between instances

three_chimes <- lapply(vector(3, mode = "list"), function(x) chimes)
play(do.call(appendSample, three_chimes))  #no gap, see also cutSampleEnds

# Volume control
play(3 * chimes)

#Stereo effect
play(stereo(chimes, chord))

#Other actions (not obviously possible)


#lang racket/gui
(play-sound "some-sound.wav" #f)


Load "guilib.ring"
new qapp {
         q1=NULL  q2=NULL
         win1 = new qwidget() {
                setwindowtitle("play sound!") show()
                setgeometry(100,100,400,400)
         }
         new qpushbutton(win1) {
             setgeometry(50,50,100,30)
             settext("play1")
             setclickevent("playmusic1()")
             show()
         }
         new qpushbutton(win1) {
             setgeometry(200,50,100,30)
             settext("play2")
             setclickevent("playmusic2()")
             show()
         }
         new qpushbutton(win1) {
             setgeometry(50,100,100,30)
             settext("pause1")
             setclickevent("pauseplay1()")
         show()
         }
         new qpushbutton(win1) {
             setgeometry(200,100,100,30)
             settext("pause2")
             setclickevent("pauseplay2()")
             show()
         }
         new qpushbutton(win1) {
             setgeometry(50,150,100,30)
             settext("stop1")
             setclickevent("stopplay1()")
             show()
                }
         new qpushbutton(win1) {
             setgeometry(200,150,100,30)
             settext("stop2")
             setclickevent("stopplay2()")
             show()
         }
         lineedit1 = new qlineedit(win1) {
                     setGeometry(50,200,100,30)
                     settext("50")
                     show()
         }
         lineedit2 = new qlineedit(win1) {
                     setGeometry(200,200,100,30)
                     settext("50")
                     show()
         }
         new qpushbutton(win1) {
             setgeometry(50,250,100,30)
             settext("volume1")
             setclickevent("volume1()")
             show()
         }
         new qpushbutton(win1) {
             setgeometry(200,250,100,30)
             settext("volume2")
             setclickevent("volume2()")
             show()
         }
         new qpushbutton(win1) {
             setgeometry(50,300,100,30)
             settext("mute1")
             setclickevent("mute1()")
             show()
         }
         new qpushbutton(win1) {
             setgeometry(200,300,100,30)
             settext("mute2")
             setclickevent("mute2()")
             show()
         }
         exec()
         }

func playmusic1
     q1 = new qmediaplayer(win1)  {
          setmedia(new qurl("music1.wav"))
          setvolume(50) play()
     }

func playmusic2
     q2 = new qmediaplayer(win1)  {
          setmedia(new qurl("music2.wav"))
          setvolume(50) play()
     }

func pauseplay1
     q1.pause()

func pauseplay2
     q2.pause()

func stopplay1
     q1.stop()

func stopplay2
     q2.stop()

func volume1
     lineedit1 { vol1 = text() }
     q1 {setvolume(number(vol1))}

func volume2
     lineedit2 { vol2 = text() }
     q2 {setvolume(number(vol2))}

func mute1
     q1.setmuted(true)

func mute2
     q2.setmuted(true)


require 'win32/sound'
include Win32

sound1 = ENV['WINDIR'] + '\Media\Windows XP Startup.wav'
sound2 = ENV['WINDIR'] + '\Media\Windows XP Shutdown.wav'

puts "play the sounds sequentially"
[sound1, sound2].each do |s|
  t1 = Time.now
  Sound.play(s)
  puts "'#{s}' duration: #{(Time.now.to_f - t1.to_f)} seconds"
end

puts "attempt to play the sounds simultaneously"
[sound1, sound2].each {|s| Sound.play(s, Sound::ASYNC)}

puts <<END
the above only plays the second sound2 because the library only appears
to be able to play one sound at a time.
END

puts "loop a sound for a few seconds"
puts Time.now
Sound.play(sound1, Sound::ASYNC + Sound::LOOP)
sleep 10
Sound.stop
puts Time.now

puts "manipulate the volume"
vol_left, vol_right = Sound.wave_volume
Sound.play(sound1, Sound::ASYNC)
sleep 1
puts "right channel quiet"
Sound.set_wave_volume(vol_left, 0)
sleep 1
puts "left channel quiet"
Sound.set_wave_volume(0, vol_right)
sleep 1
puts "restore volume"
Sound.set_wave_volume(vol_left, vol_right)

sleep 1
puts "the asynchronous sound is cancelled when the program exits"


import AVFoundation

// This example uses AVAudioPlayer for playback.
// AVAudioPlayer is the player Apple recommends for playback, since it suitable for songs
// and offers control over numerous playback parameters.
// It can play any type that is natively supported by OSX or iOS

class PlayerControl: NSObject, AVAudioPlayerDelegate {
    let player1:AVAudioPlayer!
    let player2:AVAudioPlayer!
    var playedBoth = false
    var volume:Float {
        get {
            return player1.volume
        }

        set {
            player1.volume = newValue
            player2.volume = newValue
        }
    }

    init(player1:AVAudioPlayer, player2:AVAudioPlayer) {
        super.init()
        self.player1 = player1
        self.player2 = player2
        self.player1.delegate = self
        self.player2.delegate = self
    }

    func loop() {
        player1.numberOfLoops = 1
        player1.play()

        let time = Int64((Double(player1.duration) + 2.0) * Double(NSEC_PER_SEC))

        dispatch_after(dispatch_time(0, time), dispatch_get_main_queue()) {[weak self] in
            println("Stopping track")
            self?.player1.stop()
            exit(0)
        }
    }

    func playBoth() {
        player1.play()
        player2.play()
    }

    func audioPlayerDidFinishPlaying(player:AVAudioPlayer!, successfully flag:Bool) {
        if player === player2 && !playedBoth {
            playBoth()
            playedBoth = true
        } else if player === player2 && playedBoth {
            loop()
        }
    }
}

let url1 = NSURL(string: "file:///file1.mp3")
let url2 = NSURL(string: "file:///file2.mp3")

var err:NSError?
let player1 = AVAudioPlayer(contentsOfURL: url1, error: &err)
let player2 = AVAudioPlayer(contentsOfURL: url2, error: &err)

let player = PlayerControl(player1: player1, player2: player2)

// Setting the volume
player.volume = 0.5

// Play track 2
// When this track finishes it will play both of them
// by calling the audioPlayerDidFinishPlaying delegate method
// Once both tracks finish playing, it will then loop the first track twice
// stopping the track after 2 seconds of the second play
player.player2.play()

CFRunLoopRun()


package require sound
# Potentially also require driver support for particular formats

# Load some sounds in; this part can be slow
snack::sound s1
s1 read $soundFile1
snack::sound s2
s2 read $soundFile2

# Play a sound for a while (0.1 seconds; this is a low-latency operation)
s1 play
after 100 set done 1; vwait done; # Run the event loop for a while
s1 stop

# Play two sounds at once (for 30 seconds) while mixing together
s1 play
s2 play
after 30000 set done 1; vwait done
s1 stop
s2 stop


$$ MODE TUSCRIPT
audiofile="test.wav"
ERROR/STOP OPEN (audiofile,READ,-std-)
BROWSE $audiofile


#!/usr/bin/sh

# play.sh

# Plays .au files.
# Usage: play.sh <recorded_sound.au>

cat $1 >> /dev/audio # Write file $1 to the speaker's Character Special (/dev/audio).


Declare Function libPlaySound Lib "winmm.dll" Alias "sndPlaySoundA" _
    (ByVal filename As String, ByVal Flags As Long) As Long

Sub PlaySound(sWav As String)
  Call libPlaySound(sWav, 1) '1 to play asynchronously
End Sub


import "audio" for AudioEngine
import "dome" for Process

class Game {
    static init() {
        // load a .wav file and give it a friendly name
        AudioEngine.load("a", "a.wav")
        // play the file at volume v, looping l and pan p
        __v = 2      // twice 'normal' volume
        __l = true   // loop when finished
        __p = 0.5    // division between left and right audio channels (-1 to +1)
        __chan1 = AudioEngine.play("a", __v, __l, __p)
        __fc = 0     // frame counter, updates at 60 fps
    }

    static update() { __fc = __fc + 1 }

    static draw(dt) {
        if (__fc == 600) {
            // after 10 seconds load and play a second .wav file simultaneously, same settings
            AudioEngine.load("b", "b.wav")
            __chan2 = AudioEngine.play("b", __v, __l, __p)
        }
        if (__fc == 1200) {
            __chan1.stop()   // after a further 10 seconds, stop the first file
            AudioEngine.unload("a")  // and unload it
        } else if (__fc == 1800) {
            __chan2.stop() // after another 10 seconds, stop the second file
            AudioEngine.unload("b")  // and unload it
            Process.exit(0)  // exit the application
        }
    }
}


F analyzeHandHelper(faceCount, suitCount)
   V
      p1 = 0B
      p2 = 0B
      t  = 0B
      f  = 0B
      fl = 0B
      st = 0B

   L(fc) faceCount
      S fc
         2 {I p1 {p2 = 1B} E p1 = 1B}
         3 {t = 1B}
         4 {f = 1B}

   L(sc) suitCount
      I sc == 5
         fl = 1B

   I !p1 & !p2 & !t & !f
      V s = 0
      L(fc) faceCount
         I fc != 0
            s++
         E
            s = 0
         I s == 5
            L.break

      st = (s == 5) | (s == 4 & faceCount[0] != 0 & faceCount[1] == 0)

   I st & fl   {R ‘straight-flush’}
   E I f       {R ‘four-of-a-kind’}
   E I p1 & t  {R ‘full-house’}
   E I fl      {R ‘flush’}
   E I st      {R ‘straight’}
   E I t       {R ‘three-of-a-kind’}
   E I p1 & p2 {R ‘two-pair’}
   E I p1      {R ‘one-pair’}
   E           {R ‘high-card’}

F analyzeHand(inHand)
   V handLen = 5
   V face = ‘A23456789TJQK’
   V suit = ‘SHCD’
   V errorMessage = ‘invalid hand.’

   V hand = sorted(inHand.split(‘ ’))
   I hand.len != handLen
      R errorMessage
   I Set(hand).len != handLen
      R errorMessage‘ Duplicated cards.’

   V faceCount = [0] * face.len
   V suitCount = [0] * suit.len
   L(card) hand
      I card.len != 2
         R errorMessage
      V? n = face.find(card[0])
      V? l = suit.find(card[1])
      I n == N | l == N
         R errorMessage
      faceCount[n]++
      suitCount[l]++

   R analyzeHandHelper(faceCount, suitCount)

L(hand) [‘2H 2D 2S KS QD’,
         ‘2H 5H 7D 8S 9D’,
         ‘AH 2D 3S 4S 5S’,
         ‘2H 3H 2D 3S 3D’,
         ‘2H 7H 2D 3S 3D’,
         ‘2H 7H 7D 7S 7C’,
         ‘TH JH QH KH AH’,
         ‘4H 4C KC 5D TC’,
         ‘QC TC 7C 6C 4C’]
   print(hand‘: ’analyzeHand(hand))


PokerHand(hand){
	StringUpper, hand, hand
	Sort, hand, FCardSort D%A_Space%
	cardSeq	:= RegExReplace(hand, "[^2-9TJQKA]")
	Straight:= InStr("23456789TJQKA", cardSeq) || (cardSeq = "2345A") ? true : false
	hand 	:= cardSeq = "2345A" ? RegExReplace(hand, "(.*)\h(A.)", "$2 $1") : hand
	Royal 	:= InStr(hand, "A") ? "Royal": "Straight"
	return  (hand ~= "[2-9TJQKA](.)\h.\1\h.\1\h.\1\h.\1") && (Straight) 			? hand "`t" Royal " Flush"
			: (hand ~= "([2-9TJQKA]).*?\1.*?\1.*?\1") 				? hand "`tFour of a Kind"
			: (hand ~= "^([2-9TJQKA]).\h\1.\h(?!\1)([2-9TJQKA]).\h\2.\h\2.$") 	? hand "`tFull House"	; xxyyy
			: (hand ~= "^([2-9TJQKA]).\h\1.\h\1.\h(?!\1)([2-9TJQKA]).\h\2.$") 	? hand "`tFull House"	; xxxyy
			: (hand ~= "[2-9TJQKA](.)\h.\1\h.\1\h.\1\h.\1") 			? hand "`tFlush"
			: (Straight)								? hand "`tStraight"
			: (hand ~= "([2-9TJQKA]).*?\1.*?\1")					? hand "`tThree of a Kind"
			: (hand ~= "([2-9TJQKA]).\h\1.*?([2-9TJQKA]).\h\2")			? hand "`tTwo Pair"
			: (hand ~= "([2-9TJQKA]).\h\1")						? hand "`tOne Pair"
			: 									  hand "`tHigh Card"
}
CardSort(a, b){
	a := SubStr(a, 1, 1), b := SubStr(b, 1, 1)
	a := (a = "T") ? 10 : (a = "J") ? 11 : (a = "Q") ? 12 : (a = "K") ? 13 : a
	b := (b = "T") ? 10 : (b = "J") ? 11 : (b = "Q") ? 12 : (b = "K") ? 13 : b
	return a > b ? 1 : a < b ? -1 : 0
}


#include <stdio.h>
#include <ctype.h>
#include <string.h>
#include <stdlib.h>

#define TRUE 1
#define FALSE 0

#define FACES "23456789tjqka"
#define SUITS "shdc"

typedef int bool;

typedef struct {
    int face;  /* FACES map to 0..12 respectively */
    char suit;
} card;

card cards[5];

int compare_card(const void *a, const void *b) {
    card c1 = *(card *)a;
    card c2 = *(card *)b;
    return c1.face - c2.face;
}

bool equals_card(card c1, card c2) {
    if (c1.face == c2.face && c1.suit == c2.suit) return TRUE;
    return FALSE;
}

bool are_distinct() {
    int i, j;
    for (i = 0; i < 4; ++i)
        for (j = i + 1; j < 5; ++j)
            if (equals_card(cards[i], cards[j])) return FALSE;
    return TRUE;
}

bool is_straight() {
    int i;
    qsort(cards, 5, sizeof(card), compare_card);
    if (cards[0].face + 4 == cards[4].face) return TRUE;
    if (cards[4].face == 12 && cards[0].face == 0 &&
        cards[3].face == 3) return TRUE;
    return FALSE;
}

bool is_flush() {
    int i;
    char suit = cards[0].suit;
    for (i = 1; i < 5; ++i) if (cards[i].suit != suit) return FALSE;
    return TRUE;
}

const char *analyze_hand(const char *hand) {
    int i, j, gs = 0;
    char suit, *cp;
    bool found, flush, straight;
    int groups[13];
    if (strlen(hand) != 14) return "invalid";
    for (i = 0; i < 14; i += 3) {
        cp = strchr(FACES, tolower(hand[i]));
        if (cp == NULL) return "invalid";
        j = i / 3;
        cards[j].face = cp - FACES;
        suit = tolower(hand[i + 1]);
        cp = strchr(SUITS, suit);
        if (cp == NULL) return "invalid";
        cards[j].suit = suit;
    }
    if (!are_distinct()) return "invalid";
    for (i = 0; i < 13; ++i) groups[i] = 0;
    for (i = 0; i < 5; ++i) groups[cards[i].face]++;
    for (i = 0; i < 13; ++i) if (groups[i] > 0) gs++;
    switch(gs) {
        case 2:
            found = FALSE;
            for (i = 0; i < 13; ++i) if (groups[i] == 4) {
                found = TRUE;
                break;
            }
            if (found) return "four-of-a-kind";
            return "full-house";
        case 3:
            found = FALSE;
            for (i = 0; i < 13; ++i) if (groups[i] == 3) {
                found = TRUE;
                break;
            }
            if (found) return "three-of-a-kind";
            return "two-pairs";
        case 4:
            return "one-pair";
        default:
            flush = is_flush();
            straight = is_straight();
            if (flush && straight)
                return "straight-flush";
            else if (flush)
                return "flush";
            else if (straight)
                return "straight";
            else
                return "high-card";
    }
}

int main(){
    int i;
    const char *type;
    const char *hands[10] = {
        "2h 2d 2c kc qd",
        "2h 5h 7d 8c 9s",
        "ah 2d 3c 4c 5d",
        "2h 3h 2d 3c 3d",
        "2h 7h 2d 3c 3d",
        "2h 7h 7d 7c 7s",
        "th jh qh kh ah",
        "4h 4s ks 5d ts",
        "qc tc 7c 6c 4c",
        "ah ah 7c 6c 4c"
    };
    for (i = 0; i < 10; ++i) {
        type = analyze_hand(hands[i]);
        printf("%s: %s\n", hands[i], type);
    }
    return 0;
}


using System;
using System.Collections.Generic;
using static System.Linq.Enumerable;

public static class PokerHandAnalyzer
{
    private enum Hand {
        Invalid, High_Card, One_Pair, Two_Pair, Three_Of_A_Kind, Straight,
        Flush, Full_House, Four_Of_A_Kind, Straight_Flush, Five_Of_A_Kind
    }

    private const bool Y = true;
    private const char C = '♣', D = '♦', H = '♥', S = '♠';
    private const int rankMask = 0b11_1111_1111_1111;
    private const int suitMask = 0b1111 << 14;
    private static readonly string[] ranks = { "a", "2", "3", "4", "5", "6", "7", "8", "9", "10", "j", "q", "k" };
    private static readonly string[] suits = { C + "", D + "", H + "", S + "" };
    private static readonly Card[] deck = (from suit in Range(1, 4) from rank in Range(1, 13) select new Card(rank, suit)).ToArray();

    public static void Main() {
        string[] hands = {
            "2♥ 2♦ 2♣ k♣ q♦",
            "2♥ 5♥ 7♦ 8♣ 9♠",
            "a♥ 2♦ 3♣ 4♣ 5♦",
            "2♥ 3♥ 2♦ 3♣ 3♦",
            "2♥ 7♥ 2♦ 3♣ 3♦",
            "2♥ 7♥ 7♦ 7♣ 7♠",
            "10♥ j♥ q♥ k♥ a♥",
            "4♥ 4♠ k♠ 5♦ 10♠",
            "q♣ 10♣ 7♣ 6♣ 4♣",
            "4♥ 4♣ 4♥ 4♠ 4♦", //duplicate card
            "joker 2♦ 2♠ k♠ q♦",
            "joker 5♥ 7♦ 8♠ 9♦",
            "joker 2♦ 3♠ 4♠ 5♠",
            "joker 3♥ 2♦ 3♠ 3♦",
            "joker 7♥ 2♦ 3♠ 3♦",
            "joker 7♥ 7♦ 7♠ 7♣",
            "joker j♥ q♥ k♥ A♥",
            "joker 4♣ k♣ 5♦ 10♠",
            "joker k♣ 7♣ 6♣ 4♣",
            "joker 2♦ joker 4♠ 5♠",
            "joker Q♦ joker A♠ 10♠",
            "joker Q♦ joker A♦ 10♦",
            "joker 2♦ 2♠ joker q♦"
        };
        foreach (var h in hands) {
            Console.WriteLine($"{h}: {Analyze(h).Name()}");
        }
    }

    static string Name(this Hand hand) => string.Join('-', hand.ToString().Split('_')).ToLower();

    static List<T> With<T>(this List<T> list, int index, T item) {
        list[index] = item;
        return list;
    }

    struct Card : IEquatable<Card>, IComparable<Card>
    {
        public static readonly Card Invalid = new Card(-1, -1);
        public static readonly Card Joker = new Card(0, 0);

        public Card(int rank, int suit) {
            (Rank, Suit, Code) = (rank, suit) switch {
                (_, -1) => (-1, -1, -1),
                (-1, _) => (-1, -1, -1),
                (0, _) => (0, 0, 0),
                (1, _) => (rank, suit, (1 << (13 + suit)) | ((1 << 13) | 1)),
                (_, _) => (rank, suit, (1 << (13 + suit)) | (1 << (rank - 1)))
            };
        }

        public static implicit operator Card((int rank, int suit) tuple) => new Card(tuple.rank, tuple.suit);
        public int Rank { get; }
        public int Suit { get; }
        public int Code { get; }

        public override string ToString() => Rank switch {
            -1 => "invalid",
            0 => "joker",
            _ => $"{ranks[Rank-1]}{suits[Suit-1]}"
        };

        public override int GetHashCode() => Rank << 16 | Suit;
        public bool Equals(Card other) => Rank == other.Rank && Suit == other.Suit;

        public int CompareTo(Card other) {
            int c = Rank.CompareTo(other.Rank);
            if (c != 0) return c;
            return Suit.CompareTo(other.Suit);
        }
    }

    static Hand Analyze(string hand) {
        var cards = ParseHand(hand);
        if (cards.Count != 5) return Hand.Invalid; //hand must consist of 5 cards
        cards.Sort();
        if (cards[0].Equals(Card.Invalid)) return Hand.Invalid;
        int jokers = cards.LastIndexOf(Card.Joker) + 1;
        if (jokers > 2) return Hand.Invalid; //more than 2 jokers
        if (cards.Skip(jokers).Distinct().Count() + jokers != 5) return Hand.Invalid; //duplicate cards

        if (jokers == 2) return (from c0 in deck from c1 in deck select Evaluate(cards.With(0, c0).With(1, c1))).Max();
        if (jokers == 1) return (from c0 in deck select Evaluate(cards.With(0, c0))).Max();
        return Evaluate(cards);
    }

    static List<Card> ParseHand(string hand) =>
        hand.Split(default(char[]), StringSplitOptions.RemoveEmptyEntries)
        .Select(card => ParseCard(card.ToLower())).ToList();

    static Card ParseCard(string card) => (card.Length, card) switch {
        (5, "joker") => Card.Joker,
        (3, _) when card[..2] == "10" => (10, ParseSuit(card[2])),
        (2, _) => (ParseRank(card[0]), ParseSuit(card[1])),
        (_, _) => Card.Invalid
    };

    static int ParseRank(char rank) => rank switch {
        'a' => 1,
        'j' => 11,
        'q' => 12,
        'k' => 13,
        _ when rank >= '2' && rank <= '9' => rank - '0',
        _ => -1
    };

    static int ParseSuit(char suit) => suit switch {
        C => 1, 'c' => 1,
        D => 2, 'd' => 2,
        H => 3, 'h' => 3,
        S => 4, 's' => 4,
        _ => -1
    };

    static Hand Evaluate(List<Card> hand) {
        var frequencies = hand.GroupBy(c => c.Rank).Select(g => g.Count()).OrderByDescending(c => c).ToArray();
        (int f0, int f1) = (frequencies[0], frequencies.Length > 1 ? frequencies[1] : 0);

        return (IsFlush(), IsStraight(), f0, f1) switch {
            (_, _, 5, _) => Hand.Five_Of_A_Kind,
            (Y, Y, _, _) => Hand.Straight_Flush,
            (_, _, 4, _) => Hand.Four_Of_A_Kind,
            (_, _, 3, 2) => Hand.Full_House,
            (Y, _, _, _) => Hand.Flush,
            (_, Y, _, _) => Hand.Straight,
            (_, _, 3, _) => Hand.Three_Of_A_Kind,
            (_, _, 2, 2) => Hand.Two_Pair,
            (_, _, 2, _) => Hand.One_Pair,
                        _ => Hand.High_Card
        };

        bool IsFlush() => hand.Aggregate(suitMask, (r, c) => r & c.Code) > 0;

        bool IsStraight() {
            int r = hand.Aggregate(0, (r, c) => r | c.Code) & rankMask;
            for (int i = 0; i < 4; i++) r &= r << 1;
            return r > 0;
        }
    }

}


#include <iostream>
#include <sstream>
#include <algorithm>
#include <vector>

using namespace std;

class poker
{
public:
    poker() { face = "A23456789TJQK"; suit = "SHCD"; }
    string analyze( string h )
    {
	memset( faceCnt, 0, 13 ); memset( suitCnt, 0, 4 ); vector<string> hand;
	transform( h.begin(), h.end(), h.begin(), toupper ); istringstream i( h );
	copy( istream_iterator<string>( i ), istream_iterator<string>(), back_inserter<vector<string> >( hand ) );
	if( hand.size() != 5 ) return "invalid hand."; vector<string>::iterator it = hand.begin();
	sort( it, hand.end() ); if( hand.end() != adjacent_find( it, hand.end() ) ) return "invalid hand.";
	while( it != hand.end() )
	{
	    if( ( *it ).length() != 2 ) return "invalid hand.";
	    int n = face.find( ( *it ).at( 0 ) ), l = suit.find( ( *it ).at( 1 ) );
	    if( n < 0 || l < 0 ) return "invalid hand.";
	    faceCnt[n]++; suitCnt[l]++; it++;
	}
	cout << h << ": "; return analyzeHand();
    }
private:
    string analyzeHand()
    {
	bool p1 = false, p2 = false, t = false, f = false, fl = false, st = false;
	for( int x = 0; x < 13; x++ )
	    switch( faceCnt[x] )
	    {
		case 2: if( p1 ) p2 = true; else p1 = true; break;
		case 3: t = true; break;
		case 4: f = true;
	    }
	for( int x = 0; x < 4; x++ )if( suitCnt[x] == 5 ){ fl = true; break; }

	if( !p1 && !p2 && !t && !f )
        {
	    int s = 0;
	    for( int x = 0; x < 13; x++ )
	    {
		if( faceCnt[x] ) s++; else s = 0;
		if( s == 5 ) break;
	    }
	    st = ( s == 5 ) || ( s == 4 && faceCnt[0] && !faceCnt[1] );
	}

	if( st && fl ) return "straight-flush";
	else if( f ) return "four-of-a-kind";
	else if( p1 && t ) return "full-house";
	else if( fl ) return "flush";
	else if( st ) return "straight";
	else if( t ) return "three-of-a-kind";
	else if( p1 && p2 ) return "two-pair";
	else if( p1 ) return "one-pair";
        return "high-card";
    }
    string face, suit;
    unsigned char faceCnt[13], suitCnt[4];
};

int main( int argc, char* argv[] )
{
    poker p;
    cout << p.analyze( "2h 2d 2s ks qd" ) << endl; cout << p.analyze( "2h 5h 7d 8s 9d" ) << endl;
    cout << p.analyze( "ah 2d 3s 4s 5s" ) << endl; cout << p.analyze( "2h 3h 2d 3s 3d" ) << endl;
    cout << p.analyze( "2h 7h 2d 3s 3d" ) << endl; cout << p.analyze( "2h 7h 7d 7s 7c" ) << endl;
    cout << p.analyze( "th jh qh kh ah" ) << endl; cout << p.analyze( "4h 4c kc 5d tc" ) << endl;
    cout << p.analyze( "qc tc 7c 6c 4c" ) << endl << endl; return system( "pause" );
}


(defn rank [card]
  (let [[fst _] card]
    (if (Character/isDigit fst)
      (Integer/valueOf (str fst))
      ({\T 10, \J 11, \Q 12, \K 13, \A 14} fst))))

(defn suit [card]
  (let [[_ snd] card]
    (str snd)))

(defn n-of-a-kind [hand n]
  (not (empty? (filter #(= true %) (map #(>= % n) (vals (frequencies (map rank hand))))))))

(defn ranks-with-ace [hand]
  (let [ranks (sort (map rank hand))]
    (if (some #(= 14 %) ranks) (cons 1 ranks) ranks)))

(defn pair? [hand]
  (n-of-a-kind hand 2))

(defn three-of-a-kind? [hand]
  (n-of-a-kind hand 3))

(defn four-of-a-kind? [hand]
  (n-of-a-kind hand 4))

(defn flush? [hand]
  (not (empty? (filter #(= true %) (map #(>= % 5) (vals (frequencies (map suit hand))))))))

(defn full-house? [hand]
  (true? (and
    (some #(= 2 %) (vals (frequencies (map rank hand))))
    (some #(= 3 %) (vals (frequencies (map rank hand)))))))

(defn two-pairs? [hand]
  (or
    (full-house? hand)
    (four-of-a-kind? hand)
    (= 2 (count (filter #(= true %) (map #(>= % 2) (vals (frequencies (map rank hand)))))))))

(defn straight? [hand]
  (let [hand-a (ranks-with-ace hand)
        fst (first hand-a)
        snd (second hand-a)]
    (or
      (= (take 5 hand-a) (range fst (+ fst 5)))
      (= (drop 1 hand-a) (range snd (+ snd 5))))))

(defn straight-flush? [hand]
  (and
    (straight? hand)
    (flush? hand)))

(defn invalid? [hand]
  (not= 5 (count (set hand))))

(defn check-hand [hand]
  (cond
    (invalid? hand) "invalid"
    (straight-flush? hand) "straight-flush"
    (four-of-a-kind? hand) "four-of-a-kind"
    (full-house? hand) "full-house"
    (flush? hand) "flush"
    (straight? hand) "straight"
    (three-of-a-kind? hand) "three-of-a-kind"
    (two-pairs? hand) "two-pair"
    (pair? hand) "one-pair"
    :else "high-card"))

; Test examples
(def hands [["2H" "2D" "2S" "KS" "QD"]
            ["2H" "5H" "7D" "8S" "9D"]
            ["AH" "2D" "3S" "4S" "5S"]
            ["2H" "3H" "2D" "3S" "3D"]
            ["2H" "7H" "2D" "3S" "3D"]
            ["2H" "7H" "7D" "7S" "7C"]
            ["TH" "JH" "QH" "KH" "AH"]
            ["4H" "4C" "KC" "5D" "TC"]
            ["QC" "TC" "7C" "6C" "4C"]])
(run! println (map #(str % " : " (check-hand %)) hands))


import std.stdio, std.string, std.algorithm, std.range;

string analyzeHand(in string inHand) pure /*nothrow @safe*/ {
    enum handLen = 5;
    static immutable face = "A23456789TJQK", suit = "SHCD";
    static immutable errorMessage = "invalid hand.";

    /*immutable*/ const hand = inHand.toUpper.split.sort().release;
    if (hand.length != handLen)
        return errorMessage;
    if (hand.uniq.walkLength != handLen)
        return errorMessage ~ " Duplicated cards.";

    ubyte[face.length] faceCount;
    ubyte[suit.length] suitCount;
    foreach (immutable card; hand) {
        if (card.length != 2)
            return errorMessage;
        immutable n = face.countUntil(card[0]);
        immutable l = suit.countUntil(card[1]);
        if (n < 0 || l < 0)
            return errorMessage;
        faceCount[n]++;
        suitCount[l]++;
    }

    return analyzeHandHelper(faceCount, suitCount);
}

private string analyzeHandHelper(const ref ubyte[13] faceCount,
                                 const ref ubyte[4] suitCount)
pure nothrow @safe @nogc {
    bool p1, p2, t, f, fl, st;

    foreach (immutable fc; faceCount)
        switch (fc) {
            case 2: (p1 ? p2 : p1) = true; break;
            case 3: t = true; break;
            case 4: f = true; break;
            default: // Ignore.
        }

    foreach (immutable sc; suitCount)
        if (sc == 5) {
            fl = true;
            break;
        }

    if (!p1 && !p2 && !t && !f) {
        uint s = 0;
        foreach (immutable fc; faceCount) {
            if (fc)
                s++;
            else
                s = 0;
            if (s == 5)
                break;
        }

        st = (s == 5) || (s == 4 && faceCount[0] && !faceCount[1]);
    }

    if (st && fl)      return "straight-flush";
    else if (f)        return "four-of-a-kind";
    else if (p1 && t)  return "full-house";
    else if (fl)       return "flush";
    else if (st)       return "straight";
    else if (t)        return "three-of-a-kind";
    else if (p1 && p2) return "two-pair";
    else if (p1)       return "one-pair";
    else               return "high-card";
}

void main() {
    // S = Spades, H = Hearts, C = Clubs, D = Diamonds.
    foreach (immutable hand; ["2H 2D 2S KS QD",
                              "2H 5H 7D 8S 9D",
                              "AH 2D 3S 4S 5S",
                              "2H 3H 2D 3S 3D",
                              "2H 7H 2D 3S 3D",
                              "2H 7H 7D 7S 7C",
                              "TH JH QH KH AH",
                              "4H 4C KC 5D TC",
                              "QC TC 7C 6C 4C"])
        writeln(hand, ": ", hand.analyzeHand);
}


defmodule Card do
  @faces   ~w(2 3 4 5 6 7 8 9 10 j q k a)
  @suits   ~w(♥ ♦ ♣ ♠)                          # ~w(h d c s)
  @ordinal @faces |> Enum.with_index |> Map.new

  defstruct ~w[face suit ordinal]a

  def new(str) do
    {face, suit} = String.split_at(str, -1)
    if face in @faces and suit in @suits do
      ordinal = @ordinal[face]
      %__MODULE__{face: face, suit: suit, ordinal: ordinal}
    else
      raise ArgumentError, "invalid card: #{str}"
    end
  end

  def deck do
    for face <- @faces, suit <- @suits, do: "#{face}#{suit}"
  end
end

defmodule Hand do
  @ranks ~w(high-card one-pair two-pair three-of-a-kind straight flush
            full-house four-of-a-kind straight-flush five-of-a-kind)a |>
         Enum.with_index |> Map.new
  @wheel_faces ~w(2 3 4 5 a)

  def new(str_of_cards) do
    cards = String.downcase(str_of_cards) |>
            String.split([" ", ","], trim: true) |>
            Enum.map(&Card.new &1)
    grouped = Enum.group_by(cards, &(&1.ordinal)) |> Map.values
    face_pattern = Enum.map(grouped, &(length &1)) |> Enum.sort
    {consecutive, wheel_faces} = consecutive?(cards)
    rank = categorize(cards, face_pattern, consecutive)
    rank_num = @ranks[rank]
    tiebreaker = if wheel_faces do
                   for ord <- 3..-1, do: {1,ord}
                 else
                   Enum.map(grouped, &{length(&1), hd(&1).ordinal}) |>
                   Enum.sort |> Enum.reverse
                 end
    {rank_num, tiebreaker, str_of_cards, rank}
  end

  defp one_suit?(cards) do
    Enum.map(cards, &(&1.suit)) |> Enum.uniq |> length == 1
  end

  defp consecutive?(cards) do
    sorted = Enum.sort_by(cards, &(&1.ordinal))
    if Enum.map(sorted, &(&1.face)) == @wheel_faces do
      {true, true}
    else
      flag = Enum.map(sorted, &(&1.ordinal)) |>
             Enum.chunk(2,1) |>
             Enum.all?(fn [a,b] -> a+1 == b end)
      {flag, false}
    end
  end

  defp categorize(cards, face_pattern, consecutive) do
    case {consecutive, one_suit?(cards)} do
      {true, true}  -> :"straight-flush"
      {true, false} -> :straight
      {false, true} -> :flush
      _ ->  case face_pattern do
              [1,1,1,1,1] -> :"high-card"
              [1,1,1,2]   -> :"one-pair"
              [1,2,2]     -> :"two-pair"
              [1,1,3]     -> :"three-of-a-kind"
              [2,3]       -> :"full-house"
              [1,4]       -> :"four-of-a-kind"
              [5]         -> :"five-of-a-kind"
            end
    end
  end
end

test_hands = """
2♥ 2♦ 2♣ k♣ q♦
2♥ 5♥ 7♦ 8♣ 9♠
a♥ 2♦ 3♣ 4♣ 5♦
2♥ 3♥ 2♦ 3♣ 3♦
2♥ 7♥ 2♦ 3♣ 3♦
2♥ 6♥ 2♦ 3♣ 3♦
10♥ j♥ q♥ k♥ a♥
4♥ 4♠ k♠ 2♦ 10♠
4♥ 4♠ k♠ 3♦ 10♠
q♣ 10♣ 7♣ 6♣ 4♣
q♣ 10♣ 7♣ 6♣ 3♣
9♥ 10♥ q♥ k♥ j♣
2♥ 3♥ 4♥ 5♥ a♥
2♥ 2♥ 2♦ 3♣ 3♦
"""
hands = String.split(test_hands, "\n", trim: true) |> Enum.map(&Hand.new(&1))
IO.puts "High to low"
Enum.sort(hands) |> Enum.reverse |>
Enum.each(fn hand -> IO.puts "#{elem(hand,2)}: \t#{elem(hand,3)}" end)

# Extra Credit 2. Examples:
IO.puts "\nExtra Credit 2"
extra_hands = """
joker  2♦  2♠  k♠  q♦
joker  5♥  7♦  8♠  9♦
joker  2♦  3♠  4♠  5♠
joker  3♥  2♦  3♠  3♦
joker  7♥  2♦  3♠  3♦
joker  7♥  7♦  7♠  7♣
joker  j♥  q♥  k♥  A♥
joker  4♣  k♣  5♦ 10♠
joker  k♣  7♣  6♣  4♣
joker  2♦  joker  4♠  5♠
joker  Q♦  joker  A♠ 10♠
joker  Q♦  joker  A♦ 10♦
joker  2♦  2♠  joker  q♦
"""
deck = Card.deck
String.split(extra_hands, "\n", trim: true) |>
Enum.each(fn hand ->
  [a,b,c,d,e] = String.split(hand) |>
                Enum.map(fn c -> if c=="joker", do: deck, else: [c] end)
  cards_list = for v<-a, w<-b, x<-c, y<-d, z<-e, do: "#{v} #{w} #{x} #{y} #{z}"
  best = Enum.map(cards_list, &Hand.new &1) |> Enum.max
  IO.puts "#{hand}:\t#{elem(best,3)}"
end)


type Card = int * int

type Cards = Card list

let joker = (69,69)

let rankInvalid = "invalid", 99

let allCards = {0..12} |> Seq.collect (fun x->({0..3} |> Seq.map (fun y->x,y)))

let allSame = function | y::ys -> List.forall ((=) y) ys | _-> false

let straightList (xs:int list) = xs |> List.sort |> List.mapi (fun i n->n - i) |> allSame

let cardList (s:string): Cards =
  s.Split() |> Seq.map (fun s->s.ToLower())
  |> Seq.map (fun s ->
    if s="joker" then joker
    else
      match (s |> List.ofSeq) with
      | '1'::'0'::xs -> (9, xs) | '!'::xs -> (-1, xs) | x::xs-> ("a23456789!jqk".IndexOf(x), xs) | _  as xs-> (-1, xs)
      |> function | -1, _  -> (-1, '!') | x, y::[] -> (x, y) | _  -> (-1, '!')
      |> function
      | x, 'h' | x, '♥' -> (x, 0) | x, 'd' | x, '♦' -> (x, 1) | x, 'c' | x, '♣' -> (x, 2)
      | x, 's' | x, '♠' -> (x, 3) | _ -> (-1, -1)
    )
  |> Seq.filter (fst >> ((<>) -1)) |> List.ofSeq

let rank (cards: Cards) =
  if cards.Length<>5 then rankInvalid
  else
    let cts = cards |> Seq.groupBy fst |> Seq.map (snd >> Seq.length) |> List.ofSeq |> List.sort |> List.rev
    if cts.[0]=5 then ("five-of-a-kind", 1)
    else
      let flush = cards |> List.map snd |> allSame
      let straight =
        let (ACE, ALT_ACE) = 0, 13
        let faces = cards |> List.map fst |> List.sort
        (straightList faces) || (if faces.Head<>ACE then false else (straightList (ALT_ACE::(faces.Tail))))
      if straight && flush then ("straight-flush", 2)
      else
        let cts = cards |> Seq.groupBy fst |> Seq.map (snd >> Seq.length) |> List.ofSeq |> List.sort |> List.rev
        if cts.[0]=4 then ("four-of-a-kind", 3)
        elif cts.[0]=3 && cts.[1]=2 then ("full-house", 4)
        elif flush then ("flush", 5)
        elif straight then ("straight", 6)
        elif cts.[0]=3 then ("three-of-a-kind", 7)
        elif cts.[0]=2 && cts.[1]=2 then ("two-pair", 8)
        elif cts.[0]=2 then ("one-pair", 9)
        else ("high-card", 10)

let pickBest (xs: seq<Cards>) =
  let cmp a b = (<) (snd a) (snd b)
  let pick currentBest x = if (cmp (snd x) (snd currentBest)) then x else currentBest
  xs |> Seq.map (fun x->x, (rank x)) |> Seq.fold pick ([], rankInvalid)

let calcHandRank handStr =
  let cards = handStr |> cardList
  if cards.Length<>5
    then (cards, rankInvalid)
    else
      cards |> List.partition ((=) joker) |> fun (x,y) -> x.Length, y
      |> function
      | (0,xs) when (xs |> Seq.distinct |> Seq.length)=5 -> xs, (rank xs)
      | (1,xs) -> allCards |> Seq.map (fun x->x::xs) |> pickBest
      | (2,xs) -> allCards |> Seq.collect (fun x->allCards |> Seq.map (fun y->y::x::xs)) |> pickBest
      | _ -> cards, rankInvalid

let showHandRank handStr =
  // handStr |> calcHandRank |> fun (cards, (rankName,_)) -> printfn "%s: %A %s" handStr cards rankName
  handStr |> calcHandRank |> (snd >> fst) |> printfn "%s: %s" handStr

[
"2♥ 2♦ 2♣ k♣ q♦"
"2♥ 5♥ 7♦ 8♣ 9♠"
"a♥ 2♦ 3♣ 4♣ 5♦"
"2♥ 3♥ 2♦ 3♣ 3♦"
"2♥ 7♥ 2♦ 3♣ 3♦"
"2♥ 7♥ 7♦ 7♣ 7♠"
"10♥ j♥ q♥ k♥ a♥"
"4♥ 4♠ k♠ 5♦ 10♠"
"q♣ 10♣ 7♣ 6♣ 4♣"
"joker  2♦  2♠  k♠  q♦"
"joker  5♥  7♦  8♠  9♦"
"joker  2♦  3♠  4♠  5♠"
"joker  3♥  2♦  3♠  3♦"
"joker  7♥  2♦  3♠  3♦"
"joker  7♥  7♦  7♠  7♣"
"joker  j♥  q♥  k♥  A♥"
"joker  4♣  k♣  5♦ 10♠"
"joker  k♣  7♣  6♣  4♣"
"joker  2♦  joker  4♠  5♠"
"joker  Q♦  joker  A♠ 10♠"
"joker  Q♦  joker  A♦ 10♦"
"joker  2♦  2♠  joker  q♦"
]
|> List.iter showHandRank


USING: formatting kernel poker sequences ;
{
    "2H 2D 2C KC QD"
    "2H 5H 7D 8C 9S"
    "AH 2D 3C 4C 5D"
    "2H 3H 2D 3C 3D"
    "2H 7H 2D 3C 3D"
    "2H 7H 7D 7C 7S"
    "TH JH QH KH AH"
    "4H 4S KS 5D TS"
    "QC TC 7C 6C 4C"
} [ dup string>hand-name "%s: %s\n" printf ] each


package main

import (
    "fmt"
    "sort"
    "strings"
)

type card struct {
    face byte
    suit byte
}

const faces = "23456789tjqka"
const suits = "shdc"

func isStraight(cards []card) bool {
    sorted := make([]card, 5)
    copy(sorted, cards)
    sort.Slice(sorted, func(i, j int) bool {
        return sorted[i].face < sorted[j].face
    })
    if sorted[0].face+4 == sorted[4].face {
        return true
    }
    if sorted[4].face == 14 && sorted[0].face == 2 && sorted[3].face == 5 {
        return true
    }
    return false
}

func isFlush(cards []card) bool {
    suit := cards[0].suit
    for i := 1; i < 5; i++ {
        if cards[i].suit != suit {
            return false
        }
    }
    return true
}

func analyzeHand(hand string) string {
    temp := strings.Fields(strings.ToLower(hand))
    splitSet := make(map[string]bool)
    var split []string
    for _, s := range temp {
        if !splitSet[s] {
            splitSet[s] = true
            split = append(split, s)
        }
    }
    if len(split) != 5 {
        return "invalid"
    }
    var cards []card

    for _, s := range split {
        if len(s) != 2 {
            return "invalid"
        }
        fIndex := strings.IndexByte(faces, s[0])
        if fIndex == -1 {
            return "invalid"
        }
        sIndex := strings.IndexByte(suits, s[1])
        if sIndex == -1 {
            return "invalid"
        }
        cards = append(cards, card{byte(fIndex + 2), s[1]})
    }

    groups := make(map[byte][]card)
    for _, c := range cards {
        groups[c.face] = append(groups[c.face], c)
    }

    switch len(groups) {
    case 2:
        for _, group := range groups {
            if len(group) == 4 {
                return "four-of-a-kind"
            }
        }
        return "full-house"
    case 3:
        for _, group := range groups {
            if len(group) == 3 {
                return "three-of-a-kind"
            }
        }
        return "two-pair"
    case 4:
        return "one-pair"
    default:
        flush := isFlush(cards)
        straight := isStraight(cards)
        switch {
        case flush && straight:
            return "straight-flush"
        case flush:
            return "flush"
        case straight:
            return "straight"
        default:
            return "high-card"
        }
    }
}

func main() {
    hands := [...]string{
        "2h 2d 2c kc qd",
        "2h 5h 7d 8c 9s",
        "ah 2d 3c 4c 5d",
        "2h 3h 2d 3c 3d",
        "2h 7h 2d 3c 3d",
        "2h 7h 7d 7c 7s",
        "th jh qh kh ah",
        "4h 4s ks 5d ts",
        "qc tc 7c 6c 4c",
        "ah ah 7c 6c 4c",
    }
    for _, hand := range hands {
        fmt.Printf("%s: %s\n", hand, analyzeHand(hand))
    }
}


{-# LANGUAGE TupleSections #-}

import Data.Function (on)
import Data.List     (group, nub, any, sort, sortBy)
import Data.Maybe    (mapMaybe)
import Text.Read     (readMaybe)

data Suit = Club | Diamond | Spade | Heart deriving (Show, Eq)

data Rank = Ace | Two | Three | Four | Five | Six | Seven
          | Eight | Nine | Ten | Jack | Queen | King
          deriving (Show, Eq, Enum, Ord, Bounded)

data Card = Card { suit :: Suit, rank :: Rank } deriving (Show, Eq)

type Hand = [Card]

consumed = pure . (, "")

instance Read Suit where
  readsPrec d s = case s of "♥" -> consumed Heart
                            "♦" -> consumed Diamond
                            "♣" -> consumed Spade
                            "♠" -> consumed Club
                            "h" -> consumed Heart
                            _   -> []

instance Read Rank where
  readsPrec d s = case s of "a"  -> consumed Ace
                            "2"  -> consumed Two
                            "3"  -> consumed Three
                            "4"  -> consumed Four
                            "5"  -> consumed Five
                            "6"  -> consumed Six
                            "7"  -> consumed Seven
                            "8"  -> consumed Eight
                            "9"  -> consumed Nine
                            "10" -> consumed Ten
                            "j"  -> consumed Jack
                            "q"  -> consumed Queen
                            "k"  -> consumed King
                            _    -> []

instance Read Card where
  readsPrec d = fmap (, "") . mapMaybe card . lex
    where
      card (r, s) = Card <$> (readMaybe s :: Maybe Suit)
                         <*> (readMaybe r :: Maybe Rank)

-- Special hand
acesHigh :: [Rank]
acesHigh = [Ace, Ten, Jack, Queen, King]

isSucc :: (Enum a, Eq a, Bounded a) => [a] -> Bool
isSucc []  = True
isSucc [x] = True
isSucc (x:y:zs) = (x /= maxBound && y == succ x) && isSucc (y:zs)

nameHand :: Hand -> String
nameHand [] = "Invalid Input"
nameHand cards | invalidHand          = "Invalid hand"
               | straight && flush    = "Straight flush"
               | ofKind 4             = "Four of a kind"
               | ofKind 3 && ofKind 2 = "Full house"
               | flush                = "Flush"
               | straight             = "Straight"
               | ofKind 3             = "Three of a kind"
               | uniqRanks == 3       = "Two pair"
               | uniqRanks == 4       = "One pair"
               | otherwise            = "High card"
 where
  sortedRank  = sort $ rank <$> cards
  rankCounts  = sortBy (compare `on` snd) $ (,) <$> head <*> length <$> group sortedRank
  uniqRanks   = length rankCounts
  ofKind n    = any ((==n) . snd) rankCounts
  straight    = isSucc sortedRank || sortedRank == acesHigh
  flush       = length (nub $ suit <$> cards) == 1
  invalidHand = length (nub cards) /= 5

testHands :: [(String, Hand)]
testHands = (,) <$> id <*> mapMaybe readMaybe . words <$>
  [ "2♥ 2♦ 2♣ k♣ q♦"
  , "2♥ 5♥ 7♦ 8♣ 9♠"
  , "a♥ 2♦ 3♣ 4♣ 5♦"
  , "2♥ 3♥ 2♦ 3♣ 3♦"
  , "2♥ 7♥ 2♦ 3♣ 3♦"
  , "2♥ 7♥ 7♦ 7♣ 7♠"
  , "10♥ j♥ q♥ k♥ a♥"
  , "4♥ 4♠ k♠ 5♦ 10♠"
  , "q♣ 10♣ 7♣ 6♣ 4♣"
  , "q♣ 10♣ 7♣ 6♣ 7♣" -- duplicate cards
  , "Bad input" ]

main :: IO ()
main = mapM_ (putStrLn . (fst <> const ": " <> nameHand . snd)) testHands


parseHand=: <;._2@,&' '@u:~&7 NB. hand must be well formed
Suits=: <"> 7 u: '♥♦♣♦'       NB. or Suits=: 'hdcs'
Faces=: <;._1 ' 2 3 4 5 6 7 8 9 10 j q k a'

suits=: {:&.>
faces=: }:&.>
flush=: 1 =&#&~. suits
straight=: 1 = (i.#Faces) +/@E.~ Faces /:[email protected] faces
kinds=: #/.~ @:faces
five=: 5 e. kinds NB. jokers or other cheat
four=: 4 e. kinds
three=: 3 e. kinds
two=: 2 e. kinds
twoPair=: 2 = 2 +/ .= kinds
highcard=: 5 = 1 +/ .= kinds

IF=: 2 :'(,&(<m) ^: v)"1'
Or=: 2 :'u ^:(5 e. $) @: v'

Deck=: ,Faces,&.>/Suits
Joker=: <'joker'
joke=: [: ,/^:(#@$ - 2:) (({. ,"1 Deck ,"0 1 }[email protected]}.)^:(5>[)~ i.&Joker)"1^:2@,:
punchLine=: {:@-.&a:@,@|:
rateHand=: [:;:inv [: (, [: punchLine -1 :(0 :0-.LF)@joke) parseHand
 ('invalid' IF 1:) Or
 ('high-card' IF highcard) Or
 ('one-pair' IF two) Or
 ('two-pair' IF twoPair) Or
 ('three-of-a-kind' IF three) Or
 ('straight' IF straight) Or
 ('flush' IF flush) Or
 ('full-house' IF (two * three)) Or
 ('four-of-a-kind' IF four) Or
 ('straight-flush' IF (straight * flush)) Or
 ('five-of-a-kind' IF five)
)


import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;

public class PokerHandAnalyzer {

    final static String faces = "AKQJT98765432";
    final static String suits = "HDSC";
    final static String[] deck = buildDeck();

    public static void main(String[] args) {
        System.out.println("Regular hands:\n");
        for (String input : new String[]{"2H 2D 2S KS QD",
            "2H 5H 7D 8S 9D",
            "AH 2D 3S 4S 5S",
            "2H 3H 2D 3S 3D",
            "2H 7H 2D 3S 3D",
            "2H 7H 7D 7S 7C",
            "TH JH QH KH AH",
            "4H 4C KC 5D TC",
            "QC TC 7C 6C 4C",
            "QC TC 7C 7C TD"}) {
            System.out.println(analyzeHand(input.split(" ")));
        }

        System.out.println("\nHands with wildcards:\n");
        for (String input : new String[]{"2H 2D 2S KS WW",
            "2H 5H 7D 8S WW",
            "AH 2D 3S 4S WW",
            "2H 3H 2D 3S WW",
            "2H 7H 2D 3S WW",
            "2H 7H 7D WW WW",
            "TH JH QH WW WW",
            "4H 4C KC WW WW",
            "QC TC 7C WW WW",
            "QC TC 7H WW WW"}) {
            System.out.println(analyzeHandWithWildcards(input.split(" ")));
        }
    }

    private static Score analyzeHand(final String[] hand) {
        if (hand.length != 5)
            return new Score("invalid hand: wrong number of cards", -1, hand);

        if (new HashSet<>(Arrays.asList(hand)).size() != hand.length)
            return new Score("invalid hand: duplicates", -1, hand);

        int[] faceCount = new int[faces.length()];
        long straight = 0, flush = 0;
        for (String card : hand) {

            int face = faces.indexOf(card.charAt(0));
            if (face == -1)
                return new Score("invalid hand: non existing face", -1, hand);
            straight |= (1 << face);

            faceCount[face]++;

            if (suits.indexOf(card.charAt(1)) == -1)
                return new Score("invalid hand: non-existing suit", -1, hand);
            flush |= (1 << card.charAt(1));
        }

        // shift the bit pattern to the right as far as possible
        while (straight % 2 == 0)
            straight >>= 1;

        // straight is 00011111; A-2-3-4-5 is 1111000000001
        boolean hasStraight = straight == 0b11111 || straight == 0b1111000000001;

        // unsets right-most 1-bit, which may be the only one set
        boolean hasFlush = (flush & (flush - 1)) == 0;

        if (hasStraight && hasFlush)
            return new Score("straight-flush", 9, hand);

        int total = 0;
        for (int count : faceCount) {
            if (count == 4)
                return new Score("four-of-a-kind", 8, hand);
            if (count == 3)
                total += 3;
            else if (count == 2)
                total += 2;
        }

        if (total == 5)
            return new Score("full-house", 7, hand);

        if (hasFlush)
            return new Score("flush", 6, hand);

        if (hasStraight)
            return new Score("straight", 5, hand);

        if (total == 3)
            return new Score("three-of-a-kind", 4, hand);

        if (total == 4)
            return new Score("two-pair", 3, hand);

        if (total == 2)
            return new Score("one-pair", 2, hand);

        return new Score("high-card", 1, hand);
    }

    private static WildScore analyzeHandWithWildcards(String[] hand) {
        if (Collections.frequency(Arrays.asList(hand), "WW") > 2)
            throw new IllegalArgumentException("too many wildcards");

        return new WildScore(analyzeHandWithWildcardsR(hand, null), hand.clone());
    }

    private static Score analyzeHandWithWildcardsR(String[] hand,
            Score best) {

        for (int i = 0; i < hand.length; i++) {
            if (hand[i].equals("WW")) {
                for (String card : deck) {
                    if (!Arrays.asList(hand).contains(card)) {
                        hand[i] = card;
                        best = analyzeHandWithWildcardsR(hand, best);
                    }
                }
                hand[i] = "WW";
                break;
            }
        }
        Score result = analyzeHand(hand);
        if (best == null || result.weight > best.weight)
            best = result;
        return best;
    }

    private static String[] buildDeck() {
        String[] dck = new String[suits.length() * faces.length()];
        int i = 0;
        for (char s : suits.toCharArray()) {
            for (char f : faces.toCharArray()) {
                dck[i] = "" + f + s;
                i++;
            }
        }
        return dck;
    }

    private static class Score {
        final int weight;
        final String name;
        final String[] hand;

        Score(String n, int w, String[] h) {
            weight = w;
            name = n;
            hand = h != null ? h.clone() : h;
        }

        @Override
        public String toString() {
            return Arrays.toString(hand) + " " + name;
        }
    }

    private static class WildScore {
        final String[] wild;
        final Score score;

        WildScore(Score s, String[] w) {
            score = s;
            wild = w;
        }

        @Override
        public String toString() {
            return String.format("%s%n%s%n", Arrays.toString(wild),
                    score.toString());
        }
    }
}


const FACES = ['2', '3', '4', '5', '6', '7', '8', '9', '10', 'j', 'q', 'k', 'a'];
const SUITS = ['♥', '♦', '♣', '♠'];

function analyzeHand(hand){
	let cards  = hand.split(' ').filter(x => x !== 'joker');
	let jokers = hand.split(' ').length - cards.length;

	let faces = cards.map( card => FACES.indexOf(card.slice(0,-1)) );
	let suits = cards.map( card => SUITS.indexOf(card.slice(-1)) );

	if( cards.some( (card, i, self) => i !== self.indexOf(card) ) || faces.some(face => face === -1) || suits.some(suit => suit === -1) )
		return 'invalid';

	let flush    = suits.every(suit => suit === suits[0]);
	let groups   = FACES.map( (face,i) => faces.filter(j => i === j).length).sort( (x, y) => y - x );
	let shifted  = faces.map(x => (x + 1) % 13);
	let distance = Math.min( Math.max(...faces) - Math.min(...faces), Math.max(...shifted) - Math.min(...shifted));
	let straight = groups[0] === 1 && distance < 5;
	groups[0] += jokers;

	if      (groups[0] === 5)                    return 'five-of-a-kind'
	else if (straight && flush)                  return 'straight-flush'
	else if (groups[0] === 4)                    return 'four-of-a-kind'
	else if (groups[0] === 3 && groups[1] === 2) return 'full-house'
	else if (flush)                              return 'flush'
	else if (straight)                           return 'straight'
	else if (groups[0] === 3)                    return 'three-of-a-kind'
	else if (groups[0] === 2 && groups[1] === 2) return 'two-pair'
	else if (groups[0] === 2)                    return 'one-pair'
	else                                         return 'high-card';
}


sorteddeck = [string(r) * s for s in "♣♦♥♠", r in "23456789TJQKA"]

cardlessthan(card1, card2) = indexin(x, sorteddeck)[1] < indexin(y, sorteddeck)[1]

decksort(d) = sort(d, lt=cardlessthan)

highestrank(d) = string(highestcard(d)[1])

function hasduplicate(d)
    s = sort(d)
    for i in 1:length(s)-1
        if s[i] == s[i+1]
            return true
        end
    end
    false
end

invalid(d) = !all(x -> x in sorteddeck, d) || hasduplicate(d)

function countranks(d)
    ranks = Dict()
    for c in d
        r = string(c[1])
        if !haskey(ranks, r)
            ranks[r] = 1
        else
            ranks[r] += 1
        end
    end
    ranks
end

function countsuits(d)
    suits = Dict()
    for c in d
        s = string(c[2])
        if !haskey(suits, s)
            suits[s] = 1
        else
            suits[s] += 1
        end
    end
    suits
end

const rankmodifiers = Dict("A" => 130, "K" => 120, "Q" => 110, "J" => 100, "T" => 90,
                    "9" => 80, "8" => 70, "7" => 60, "6" => 50, "5" => 40,
                    "4" => 30, "3" => 20, "2" => 10)

rank(card) = rankmodifiers[string(card[1])]

const suitmodifiers = Dict("♠" => 4, "♥" => 3, "♦" => 2, "♣" => 1)

suitrank(card) = suitmodifiers[string(card[2])]

function isstraight(ranksdict)
    v = collect(values(ranksdict))
    if maximum(v) != 1
        return false
    else
        s = sort(map(x->rankmodifiers[x], collect(keys(ranksdict))))
        if s == [10, 20, 30, 40, 130]  # aces low straight
            return true
        else
            for i in 1:length(s)-1
                if abs(s[i] - s[i+1]) > 10
                    return false
                end
            end
        end
    end
    true
end

highestsuit(suitsdict) = sort(collect(keys(suitsdict)), lt=(x,y)->suitsdict[x] < suitsdict[y])[end]

isflush(suitsdict) = length(collect(values(suitsdict))) == 1

isstraightflush(ranks, suits) = isstraight(ranks) && isflush(suits)

isfourofakind(ranksdict) = maximum(values(ranksdict)) == 4 ? true : false

isfullhouse(ranksdict) = sort(collect(values(ranksdict))) == [2, 3]

isthreeofakind(ranksdict) = maximum(values(ranksdict)) == 3 && !isfullhouse(ranksdict) ? true : false

istwopair(ranksdict) = sort(collect(values(ranksdict)))[end-1: end] == [2,2]

isonepair(ranksdict) = sort(collect(values(ranksdict)))[end-1: end] == [1,2]

ishighcard(ranks, suits) = maximum(values(ranks)) == 1 && !isflush(suits) && !isstraight(ranks)

function scorehand(d)
    suits = countsuits(d)
    ranks = countranks(d)
    if invalid(d)
        return "invalid"
    end
    if isstraightflush(ranks, suits) return "straight-flush"
    elseif isfourofakind(ranks)      return "four-of-a-kind"
    elseif isfullhouse(ranks)        return "full-house"
    elseif isflush(suits)            return "flush"
    elseif isstraight(ranks)         return "straight"
    elseif isthreeofakind(ranks)     return "three-of-a-kind"
    elseif istwopair(ranks)          return "two-pair"
    elseif isonepair(ranks)          return "one-pair"
    elseif ishighcard(ranks, suits)  return "high-card"
    end
end

const hands =  [["2♥", "2♦", "2♣", "K♣", "Q♦"], ["2♥", "5♥", "7♦", "8♣", "9♠"],
   ["A♥", "2♦", "3♣", "4♣", "5♦"], ["2♥", "3♥", "2♦", "3♣", "3♦"],
   ["2♥", "7♥", "2♦", "3♣", "3♦"], ["2♥", "7♥", "7♦", "7♣", "7♠"],
   ["T♥", "J♥", "Q♥", "K♥", "A♥"], ["4♥", "4♠", "K♠", "5♦", "T♠"],
   ["Q♣", "T♣", "7♣", "6♣", "4♣"]]

for hand in hands
    println("Hand $hand is a ", scorehand(hand), " hand.")
end


// version 1.1.2

class Card(val face: Int, val suit: Char)

const val FACES = "23456789tjqka"
const val SUITS = "shdc"

fun isStraight(cards: List<Card>): Boolean {
    val sorted = cards.sortedBy { it.face }
    if (sorted[0].face + 4 == sorted[4].face) return true
    if (sorted[4].face == 14 && sorted[0].face == 2 && sorted[3].face == 5) return true
    return false
}

fun isFlush(cards: List<Card>): Boolean {
    val suit = cards[0].suit
    if (cards.drop(1).all { it.suit == suit }) return true
    return false
}

fun analyzeHand(hand: String): String {
    val h = hand.toLowerCase()
    val split = h.split(' ').filterNot { it == "" }.distinct()
    if (split.size != 5) return "invalid"
    val cards = mutableListOf<Card>()

    for (s in split) {
        if (s.length != 2) return "invalid"
        val fIndex = FACES.indexOf(s[0])
        if (fIndex == -1) return "invalid"
        val sIndex = SUITS.indexOf(s[1])
        if (sIndex == -1) return "invalid"
        cards.add(Card(fIndex + 2, s[1]))
    }

    val groups = cards.groupBy { it.face }
    when (groups.size) {
        2 -> {
            if (groups.any { it.value.size == 4 }) return "four-of-a-kind"
            return "full-house"
        }
        3 -> {
            if (groups.any { it.value.size == 3 }) return "three-of-a-kind"
            return "two-pair"
        }
        4 -> return "one-pair"
        else -> {
            val flush = isFlush(cards)
            val straight = isStraight(cards)
            when {
                flush && straight -> return "straight-flush"
                flush             -> return "flush"
                straight          -> return "straight"
                else              -> return "high-card"
            }
        }
    }
}

fun main(args: Array<String>) {
    val hands = arrayOf(
        "2h 2d 2c kc qd",
        "2h 5h 7d 8c 9s",
        "ah 2d 3c 4c 5d",
        "2h 3h 2d 3c 3d",
        "2h 7h 2d 3c 3d",
        "2h 7h 7d 7c 7s",
        "th jh qh kh ah",
        "4h 4s ks 5d ts",
        "qc tc 7c 6c 4c",
        "ah ah 7c 6c 4c"
    )
    for (hand in hands) {
        println("$hand: ${analyzeHand(hand)}")
    }
}