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)); } }