import std.stdio;
import std.range;
import std.algorithm;
import std.typecons;
import std.traits;
import std.conv;
private void print(T, A,
string indent = " ",
string cum_indent = "",
string trailer = "\n") (A appender, ref T t) if(!isPointer!T) {
const string new_indent = cum_indent ~ indent;
static if (is(T == struct) || is(T == class)) {
static if(is(T == class)) {
if(!t) {
appender.put(text("null", trailer));
return;
}
}
static if(__traits(compiles, t.pprint(indent, cum_indent, trailer))) {
appender.put(t.pprint(indent, cum_indent, trailer));
return;
} else static if(isIterable!T) {
appender.put("[\n");
int index = 0;
foreach(ref item; t) {
appender.put(new_indent);
appender.put(text("[", index++, "]->"));
print!(typeof(item), A, indent, new_indent)(appender, item);
}
appender.put(text(indent, "]", trailer));
return;
} else {
appender.put("{\n");
foreach (i, field; t.tupleof) {
auto fieldName = T.tupleof[i].stringof;
appender.put(text(new_indent, fieldName, " = "));
print!(typeof(field), A, indent, new_indent)(appender, field);
}
appender.put(text(cum_indent, "}", trailer));
return;
}
} else static if(is(T == string)) {
appender.put(text('"', t,"\"", trailer));
} else static if(isArray!(T)) {
appender.put("[\n");
int index = 0;
foreach(item; t) {
appender.put(new_indent);
appender.put(text("[", index++, "]->"));
print!(typeof(item), A, indent, new_indent)(appender, item);
}
appender.put(text(indent, "]", trailer));
return;
} else static if(isAssociativeArray!(T)) {
appender.put("{\n");
foreach(k,v; t) {
appender.put(text(new_indent, '('));
print!(typeof(k), A, indent, new_indent, "")(appender, k);
appender.put(text(" => \n", new_indent~indent));
print!(typeof(v), A, indent, new_indent, "")(appender, v);
appender.put(text("),", trailer));
}
appender.put(text(indent, "}", trailer));
return;
} else {
appender.put(text(t, trailer));
return;
}
}
private void print(T, A,
string indent = " ",
string cum_indent = "",
string trailer = "\n")
(A appender, ref T t) if(isPointer!T) {
static if(isFunctionPointer!T) {
appender.put(text(typeid(T), trailer));
} else {
if(!t) {
appender.put(text("null", trailer));
} else {
print!(PointerTarget!T,A,indent,cum_indent,trailer)(appender, *t);
}
}
}
string pp(T, string indent = " ")(ref T item) {
auto appender = appender!string();
print!(T, typeof(appender), indent) (appender, item);
return appender.data;
}
struct TaxTable {
alias Tuple!(double, double) KeyValuePair;
alias KeyValuePair[] Table;
this(KeyValuePair)(KeyValuePair[] values...) {
foreach(kv; values) {
_table ~= kv;
}
sort(_table);
}
bool opEquals(ref const TaxTable other) const {
return _table == other._table;
}
double getRate(double taxableGrossIncome) const {
auto sortedRange = assumeSorted(_table[]);
auto needle = KeyValuePair(taxableGrossIncome, 0);
auto found = sortedRange.lowerBound(needle);
if(!found.empty) {
writeln(pp(found)); // fine
writeln(pp(found), taxableGrossIncome); // fine
writeln(taxableGrossIncome, pp(found)); // crash
return found[$-1][1];
}
return 0;
}
private {
Table _table;
}
}
void main() {
with(TaxTable) {
auto kv1 = KeyValuePair(50000, 0.18), kv2 = KeyValuePair(100000, 0.25);
TaxTable tbl = TaxTable(kv1, kv2);
assert(tbl == TaxTable(kv2, kv1));
assert(0 == tbl.getRate(25000));
assert(0 == tbl.getRate(50000));
assert(0.18 == tbl.getRate(50001));
assert(0.18 == tbl.getRate(100000));
assert(0.25 == tbl.getRate(100000.01));
}
}