class Number {
    this(int i) {
        _i = i;
    }
    
    @property int i() const nothrow pure {
        return _i;
    }
    
    @property void i(int i) nothrow pure {
        _i = i;
    }
    
    // Number has to be positive. Excludes the use of negative.
    state positive!negative {
        _i > 0;
        
        // Adds the checks in front of the basic implementation.
        @property void i(int i) nothrow pure {
            i > 0;
        }
    }
    
    // Number has to be negative. Excludes the use of negative.
    state negative!positive {
        _i < 0;
        
        @property void i(int i) nothrow pure {
            i < 0;
        }
    }
    
    private int _i;
}

////////////////////////////////////////////////////////////////////////
// 1) A default number.
auto n = new Number(100);

////////////////////////////////////////////////////////////////////////
// 2) A positive number.
auto n = new Number<positive>(100);

////////////////////////////////////////////////////////////////////////
// 3) -10 claims to be positive. Fails.
auto n = new Number<positive>(-10);

////////////////////////////////////////////////////////////////////////
// 4) A number which is both positive and negative. Fails at compile time.
auto n = new Number<positive, negative>(0);

////////////////////////////////////////////////////////////////////////
// 5) A negative number.
auto n = new Number<negative>(-42);

// Converting to default number results in const.
const Number dn = n;

// Compiler error.
Number dnfail = n;

////////////////////////////////////////////////////////////////////////
// 6) A default number.
auto n = new Number(512);

// Compiler error. See the following lines.
Number<positive> npos = n;

/* If we change npos through n, there is no failure but npos is negative.
 * So this should be impossible.
 */
n.i = -100;

////////////////////////////////////////////////////////////////////////
// 7) A default number.
auto n = new Number(1024);

void pureFunc(Number<positive> num) pure;
void nonpureFunc(Number<positive> num);
Number<positive> pureButReturnsPosNumber(Number<positive> num) pure;

/* Implicitly comverts n to a positive number by calling the checks of
 * the positive state.
 */
pureFunc(n);

/* Compiler error. The function could read global state, changing the
 * value of n, while it is known as positive in the function's scope.
 * This is a serious issue.
 */
nonpureFunc(n);

/* The function could return the number passed. This would lead to the
 * problem given in point 6. So do a runtime check in debug mode only
 * if the returned number is the same as the number passed. If so,
 * throw an Error.
 */
pureButReturnsPosNumber(n);

////////////////////////////////////////////////////////////////////////
// 8) A default number.
auto n = new Number(2048);

void func(Number<positive> num);

/* Works. Internally checks whether i is positive or not. But you have
 * to create a new Number though.
 */
func(new Number(n.i));

////////////////////////////////////////////////////////////////////////
// 9) A negative number.
auto n = new Number<negative>(-100);

// Compiler error. States exclude each other.
Number<positive> npos = n;

////////////////////////////////////////////////////////////////////////
// 10) Passing between functions.
auto n = new Number(42);
auto npos = new Number<positive>(42);

void func(Number<positive> n) pure {
    if (n == 1) {
        return;
    }
    n.i = n.i - 1;
    
    // No checks needed.
    func(n);
}

// Checks needed. Note: Requires func to be pure.
func(n);

// No checks needed.
func(npos);

////////////////////////////////////////////////////////////////////////
// 11) Defining states outside class definition.
// Can access only public members.
state Number:greater10!negative {
    i > 10;
    
    @property void i(int i) nothrow pure {
        i > 10;
    }
}

auto n = new Number<greater10>(42);

////////////////////////////////////////////////////////////////////////
// 12) Special states.
state Number:greater20!negative {
    i > 20;
    
    @property void i(int i) nothrow pure {
        i > 20;
    }
    
    state greater30 {
        i > 30;
        
        @property void i(int i) nothrow pure {
            i > 30;
        }
    }
}

auto n1 = new Number<greater30>(42);

// No checks needed.
Number<greater20> n2 = n1;

// Checks greater30 only.
auto n3 = new Number<greater20, greater30>(42);

////////////////////////////////////////////////////////////////////////
// 13) Invariants in states.
state Number:smaller30 {
    /* In debug mode, this behaves like an invariant. This makes sure
     * that the state is behaving as expected. It is executed more
     * frequently, because in release mode it is only called on
     * conversion.
     */
    i < 30;
    
    @property void i(int i) nothrow pure {
        i < 30;
    }
}

////////////////////////////////////////////////////////////////////////
// 14) Generic states.
state Number:smaller(int val) {
    // val is known at compile time.
    i < val;
    
    @property void i(int i) nothrow pure {
        i < val;
    }
}

auto n = new Number<smaller(20)>(19);