// Copyright 2012 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

extern mod std;

fn test(actual: ~str, expected: ~str) {
    debug!(actual.clone());
    debug!(expected.clone());
    assert!((actual == expected));
}

pub fn main() {
    test(fmt!("hello %d friends and %s things", 10, ~"formatted"),
         ~"hello 10 friends and formatted things");

    test(fmt!("test"), ~"test");

    // a quadratic optimization in LLVM (jump-threading) makes this test a
    // bit slow to compile unless we break it up
    part1();
    part2();
    part3();
    part4();
    part5();
    part6();
    percent();
    more_floats();
}

fn part1() {
    // Simple tests for types

    test(fmt!("%d", 1), ~"1");
    test(fmt!("%i", 2), ~"2");
    test(fmt!("%i", -1), ~"-1");
    test(fmt!("%u", 10u), ~"10");
    test(fmt!("%s", ~"test"), ~"test");
    test(fmt!("%b", true), ~"true");
    test(fmt!("%b", false), ~"false");
    test(fmt!("%c", 'A'), ~"A");
    test(fmt!("%x", 0xff_u), ~"ff");
    test(fmt!("%X", 0x12ab_u), ~"12AB");
    test(fmt!("%o", 10u), ~"12");
    test(fmt!("%t", 0b11010101_u), ~"11010101");
    test(fmt!("%f", 5.82), ~"5.82");
    // 32-bit limits

    test(fmt!("%i", -2147483648), ~"-2147483648");
    test(fmt!("%i", 2147483647), ~"2147483647");
    test(fmt!("%u", 4294967295u), ~"4294967295");
    test(fmt!("%x", 0xffffffff_u), ~"ffffffff");
    test(fmt!("%o", 0xffffffff_u), ~"37777777777");
    test(fmt!("%t", 0xffffffff_u), ~"11111111111111111111111111111111");
}
fn part2() {
    // Widths

    test(fmt!("%1d", 500), ~"500");
    test(fmt!("%10d", 500), ~"       500");
    test(fmt!("%10d", -500), ~"      -500");
    test(fmt!("%10u", 500u), ~"       500");
    test(fmt!("%10s", ~"test"), ~"      test");
    test(fmt!("%10b", true), ~"      true");
    test(fmt!("%10x", 0xff_u), ~"        ff");
    test(fmt!("%10X", 0xff_u), ~"        FF");
    test(fmt!("%10o", 10u), ~"        12");
    test(fmt!("%10t", 0xff_u), ~"  11111111");
    test(fmt!("%10c", 'A'), ~"         A");
    test(fmt!("%10f", 5.82), ~"      5.82");
    // Left justify

    test(fmt!("%-10d", 500), ~"500       ");
    test(fmt!("%-10d", -500), ~"-500      ");
    test(fmt!("%-10u", 500u), ~"500       ");
    test(fmt!("%-10s", ~"test"), ~"test      ");
    test(fmt!("%-10b", true), ~"true      ");
    test(fmt!("%-10x", 0xff_u), ~"ff        ");
    test(fmt!("%-10X", 0xff_u), ~"FF        ");
    test(fmt!("%-10o", 10u), ~"12        ");
    test(fmt!("%-10t", 0xff_u), ~"11111111  ");
    test(fmt!("%-10c", 'A'), ~"A         ");
    test(fmt!("%-10f", 5.82), ~"5.82      ");
}

fn part3() {
    // Precision

    test(fmt!("%.d", 0), ~"");
    test(fmt!("%.u", 0u), ~"");
    test(fmt!("%.x", 0u), ~"");
    test(fmt!("%.t", 0u), ~"");
    test(fmt!("%.d", 10), ~"10");
    test(fmt!("%.d", -10), ~"-10");
    test(fmt!("%.u", 10u), ~"10");
    test(fmt!("%.s", ~"test"), ~"");
    test(fmt!("%.x", 127u), ~"7f");
    test(fmt!("%.o", 10u), ~"12");
    test(fmt!("%.t", 3u), ~"11");
    test(fmt!("%.c", 'A'), ~"A");
    test(fmt!("%.f", 5.82), ~"6");
    test(fmt!("%.0d", 0), ~"");
    test(fmt!("%.0u", 0u), ~"");
    test(fmt!("%.0x", 0u), ~"");
    test(fmt!("%.0t", 0u), ~"");
    test(fmt!("%.0d", 10), ~"10");
    test(fmt!("%.0d", -10), ~"-10");
    test(fmt!("%.0u", 10u), ~"10");
    test(fmt!("%.0s", ~"test"), ~"");
    test(fmt!("%.0x", 127u), ~"7f");
    test(fmt!("%.0o", 10u), ~"12");
    test(fmt!("%.0t", 3u), ~"11");
    test(fmt!("%.0c", 'A'), ~"A");
    test(fmt!("%.0f", 5.892), ~"6");
    test(fmt!("%.1d", 0), ~"0");
    test(fmt!("%.1u", 0u), ~"0");
    test(fmt!("%.1x", 0u), ~"0");
    test(fmt!("%.1t", 0u), ~"0");
    test(fmt!("%.1d", 10), ~"10");
    test(fmt!("%.1d", -10), ~"-10");
    test(fmt!("%.1u", 10u), ~"10");
    test(fmt!("%.1s", ~"test"), ~"t");
    test(fmt!("%.1x", 127u), ~"7f");
    test(fmt!("%.1o", 10u), ~"12");
    test(fmt!("%.1t", 3u), ~"11");
    test(fmt!("%.1c", 'A'), ~"A");
    test(fmt!("%.1f", 5.82), ~"5.8");
}
fn part4() {
    test(fmt!("%.5d", 0), ~"00000");
    test(fmt!("%.5u", 0u), ~"00000");
    test(fmt!("%.5x", 0u), ~"00000");
    test(fmt!("%.5t", 0u), ~"00000");
    test(fmt!("%.5d", 10), ~"00010");
    test(fmt!("%.5d", -10), ~"-00010");
    test(fmt!("%.5u", 10u), ~"00010");
    test(fmt!("%.5s", ~"test"), ~"test");
    test(fmt!("%.5x", 127u), ~"0007f");
    test(fmt!("%.5o", 10u), ~"00012");
    test(fmt!("%.5t", 3u), ~"00011");
    test(fmt!("%.5c", 'A'), ~"A");
    test(fmt!("%.5f", 5.82), ~"5.82000");
    test(fmt!("%.5f", 5.0), ~"5.00000");
    test(fmt!("%.100f", 1.1), ~"1.1000000000000000888178419700125232338905334472656250000000000000000000000000000000000000000000000000");

    // Bool precision. I'm not sure if it's good or bad to have bool
    // conversions support precision - it's not standard printf so we
    // can do whatever. For now I'm making it behave the same as string
    // conversions.

    test(fmt!("%.b", true), ~"");
    test(fmt!("%.0b", true), ~"");
    test(fmt!("%.1b", true), ~"t");
}

fn part5() {
    // Explicit + sign. Only for signed conversions

    test(fmt!("%+d", 0), ~"+0");
    test(fmt!("%+d", 1), ~"+1");
    test(fmt!("%+d", -1), ~"-1");
    test(fmt!("%+f", 0.0), ~"+0");
    // Leave space for sign

    test(fmt!("% d", 0), ~" 0");
    test(fmt!("% d", 1), ~" 1");
    test(fmt!("% d", -1), ~"-1");
    test(fmt!("% f", 0.0), ~" 0");
    // Plus overrides space

    test(fmt!("% +d", 0), ~"+0");
    test(fmt!("%+ d", 0), ~"+0");
    test(fmt!("% +f", 0.0), ~"+0");
    test(fmt!("%+ f", 0.0), ~"+0");
    // 0-padding

    test(fmt!("%05d", 0), ~"00000");
    test(fmt!("%05d", 1), ~"00001");
    test(fmt!("%05d", -1), ~"-0001");
    test(fmt!("%05u", 1u), ~"00001");
    test(fmt!("%05x", 127u), ~"0007f");
    test(fmt!("%05X", 127u), ~"0007F");
    test(fmt!("%05o", 10u), ~"00012");
    test(fmt!("%05t", 3u), ~"00011");
    test(fmt!("%05f", 5.82), ~"05.82");
    // 0-padding a string is undefined but glibc does this:

    test(fmt!("%05s", ~"test"), ~" test");
    test(fmt!("%05c", 'A'), ~"    A");
    test(fmt!("%05b", true), ~" true");
    // Left-justify overrides 0-padding

    test(fmt!("%-05d", 0), ~"0    ");
    test(fmt!("%-05d", 1), ~"1    ");
    test(fmt!("%-05d", -1), ~"-1   ");
    test(fmt!("%-05u", 1u), ~"1    ");
    test(fmt!("%-05x", 127u), ~"7f   ");
    test(fmt!("%-05X", 127u), ~"7F   ");
    test(fmt!("%-05o", 10u), ~"12   ");
    test(fmt!("%-05t", 3u), ~"11   ");
    test(fmt!("%-05s", ~"test"), ~"test ");
    test(fmt!("%-05c", 'A'), ~"A    ");
    test(fmt!("%-05b", true), ~"true ");
    test(fmt!("%-05f", 5.82), ~"5.82 ");
}
fn part6() {
    // Precision overrides 0-padding
    // FIXME #2481: Recent gcc's report some of these as warnings

    test(fmt!("%06.5d", 0), ~" 00000");
    test(fmt!("%06.5u", 0u), ~" 00000");
    test(fmt!("%06.5x", 0u), ~" 00000");
    test(fmt!("%06.5d", 10), ~" 00010");
    test(fmt!("%06.5d", -10), ~"-00010");
    test(fmt!("%06.5u", 10u), ~" 00010");
    test(fmt!("%06.5s", ~"test"), ~"  test");
    test(fmt!("%06.5c", 'A'), ~"     A");
    test(fmt!("%06.5x", 127u), ~" 0007f");
    test(fmt!("%06.5X", 127u), ~" 0007F");
    test(fmt!("%06.5o", 10u), ~" 00012");

    // Precision does not override zero-padding for floats
    test(fmt!("%08.5f", 5.82), ~"05.82000");

    // Signed combinations

    test(fmt!("% 5d", 1), ~"    1");
    test(fmt!("% 5d", -1), ~"   -1");
    test(fmt!("%+5d", 1), ~"   +1");
    test(fmt!("%+5d", -1), ~"   -1");
    test(fmt!("% 05d", 1), ~" 0001");
    test(fmt!("% 05d", -1), ~"-0001");
    test(fmt!("%+05d", 1), ~"+0001");
    test(fmt!("%+05d", -1), ~"-0001");
    test(fmt!("%- 5d", 1), ~" 1   ");
    test(fmt!("%- 5d", -1), ~"-1   ");
    test(fmt!("%-+5d", 1), ~"+1   ");
    test(fmt!("%-+5d", -1), ~"-1   ");
    test(fmt!("%- 05d", 1), ~" 1   ");
    test(fmt!("%- 05d", -1), ~"-1   ");
    test(fmt!("%-+05d", 1), ~"+1   ");
    test(fmt!("%-+05d", -1), ~"-1   ");
}

fn percent() {
    let s = fmt!("ab%%cd");
    assert!((s == ~"ab%cd"));
}

fn more_floats() {
    assert!(~"3.1416"      == fmt!("%.4f", 3.14159));
    assert!(~"3"           == fmt!("%.0f", 3.14159));
    assert!(~"99"          == fmt!("%.0f", 98.5));
    assert!(~"7.0000"      == fmt!("%.4f", 6.999999999));
    assert!(~"3.141590000" == fmt!("%.9f", 3.14159));
}
