From 1253323027e0b0f1d6adf204d5591f2fda1da9e4 Mon Sep 17 00:00:00 2001 From: electricboogie <32370782+electricboogie@users.noreply.github.com> Date: Fri, 9 Apr 2021 14:28:46 -0500 Subject: [PATCH 01/55] Various fixes and performance improvements --- src/uu/sort/src/sort.rs | 114 ++++++++++++------ tests/.DS_Store | Bin 0 -> 6148 bytes tests/by-util/test_sort.rs | 49 ++++++-- tests/fixtures/.DS_Store | Bin 0 -> 6148 bytes ...ars_numeric_unique_reverse_stable.expected | 20 +++ .../fixtures/sort/multiple_decimals.expected | 33 +++++ .../sort/multiple_decimals_general.txt | 35 ++++++ .../sort/multiple_decimals_numeric.txt | 35 ++++++ 8 files changed, 242 insertions(+), 44 deletions(-) create mode 100644 tests/.DS_Store create mode 100644 tests/fixtures/.DS_Store create mode 100644 tests/fixtures/sort/mixed_floats_ints_chars_numeric_unique_reverse_stable.expected create mode 100644 tests/fixtures/sort/multiple_decimals.expected create mode 100644 tests/fixtures/sort/multiple_decimals_general.txt create mode 100644 tests/fixtures/sort/multiple_decimals_numeric.txt diff --git a/src/uu/sort/src/sort.rs b/src/uu/sort/src/sort.rs index 4e0e25d65..211f87672 100644 --- a/src/uu/sort/src/sort.rs +++ b/src/uu/sort/src/sort.rs @@ -22,6 +22,7 @@ use rand::distributions::Alphanumeric; use rand::{thread_rng, Rng}; use rayon::prelude::*; use semver::Version; +use std::borrow::Cow; use std::cmp::Ordering; use std::collections::BinaryHeap; use std::env; @@ -262,7 +263,7 @@ pub fn uumain(args: impl uucore::Args) -> i32 { Arg::with_name(OPT_CHECK_SILENT) .short("C") .long(OPT_CHECK_SILENT) - .help("exit successfully if the given file is already sorted, and exit with status 1 otherwise. "), + .help("exit successfully if the given file is already sorted, and exit with status 1 otherwise."), ) .arg( Arg::with_name(OPT_IGNORE_CASE) @@ -353,7 +354,7 @@ pub fn uumain(args: impl uucore::Args) -> i32 { if let Ok(n) = line { files.push( std::str::from_utf8(&n) - .expect("Could not parse zero terminated string from input.") + .expect("Could not parse string from zero terminated input.") .to_string(), ); } @@ -488,6 +489,8 @@ fn exec(files: Vec, settings: &mut Settings) -> i32 { } else { print_sorted(file_merger, &settings) } + } else if settings.mode == SortMode::Default && settings.unique { + print_sorted(lines.iter().dedup(), &settings) } else if settings.mode == SortMode::Month && settings.unique { print_sorted( lines @@ -499,7 +502,7 @@ fn exec(files: Vec, settings: &mut Settings) -> i32 { print_sorted( lines .iter() - .dedup_by(|a, b| get_nums_dedup(a) == get_nums_dedup(b)), + .dedup_by(|a, b| get_num_dedup(a, &settings) == get_num_dedup(b, &settings)), &settings, ) } else { @@ -603,12 +606,13 @@ fn default_compare(a: &str, b: &str) -> Ordering { #[inline(always)] fn leading_num_common(a: &str) -> &str { let mut s = ""; + + // check whether char is numeric, whitespace or decimal point or thousand seperator for (idx, c) in a.char_indices() { - // check whether char is numeric, whitespace or decimal point or thousand seperator if !c.is_numeric() && !c.is_whitespace() - && !c.eq(&DECIMAL_PT) && !c.eq(&THOUSANDS_SEP) + && !c.eq(&DECIMAL_PT) // check for e notation && !c.eq(&'e') && !c.eq(&'E') @@ -621,7 +625,7 @@ fn leading_num_common(a: &str) -> &str { break; } // If line is not a number line, return the line as is - s = a; + s = &a; } s } @@ -633,16 +637,17 @@ fn leading_num_common(a: &str) -> &str { // not recognize a positive sign or scientific/E notation so we strip those elements here. fn get_leading_num(a: &str) -> &str { let mut s = ""; - let b = leading_num_common(a); - // GNU numeric sort doesn't recognize '+' or 'e' notation so we strip - for (idx, c) in b.char_indices() { - if c.eq(&'e') || c.eq(&'E') || b.chars().nth(0).unwrap_or('\0').eq(&POSITIVE) { - s = &b[..idx]; + let a = leading_num_common(a); + + // GNU numeric sort doesn't recognize '+' or 'e' notation so we strip trailing chars + for (idx, c) in a.char_indices() { + if c.eq(&'e') || c.eq(&'E') || a.chars().nth(0).unwrap_or('\0').eq(&POSITIVE) { + s = &a[..idx]; break; } // If no further processing needed to be done, return the line as-is to be sorted - s = b; + s = &a; } // And empty number or non-number lines are to be treated as ‘0’ but only for numeric sort @@ -657,30 +662,32 @@ fn get_leading_num(a: &str) -> &str { // In contrast to numeric compare, GNU general numeric/FP sort *should* recognize positive signs and // scientific notation, so we strip those lines only after the end of the following numeric string. // For example, 5e10KFD would be 5e10 or 5x10^10 and +10000HFKJFK would become 10000. -fn get_leading_gen(a: &str) -> String { +fn get_leading_gen(a: &str) -> &str { // Make this iter peekable to see if next char is numeric - let mut p_iter = leading_num_common(a).chars().peekable(); - let mut r = String::new(); + let raw_leading_num = leading_num_common(a); + let mut p_iter = raw_leading_num.chars().peekable(); + let mut result = ""; // Cleanup raw stripped strings for c in p_iter.to_owned() { let next_char_numeric = p_iter.peek().unwrap_or(&'\0').is_numeric(); - // Only general numeric recognizes e notation and, see block below, the '+' sign - if (c.eq(&'e') && !next_char_numeric) || (c.eq(&'E') && !next_char_numeric) { - r = a.split(c).next().unwrap_or("").to_owned(); + // Only general numeric recognizes e notation and the '+' sign + if (c.eq(&'e') && !next_char_numeric) + || (c.eq(&'E') && !next_char_numeric) + // Only GNU (non-general) numeric recognize thousands seperators, takes only leading # + || c.eq(&THOUSANDS_SEP) + { + result = a.split(c).next().unwrap_or(""); break; // If positive sign and next char is not numeric, split at postive sign at keep trailing numbers // There is a more elegant way to do this in Rust 1.45, std::str::strip_prefix } else if c.eq(&POSITIVE) && !next_char_numeric { - let mut v: Vec<&str> = a.split(c).collect(); - let x = v.split_off(1); - r = x.join(""); + result = a.trim().trim_start_matches('+'); break; - // If no further processing needed to be done, return the line as-is to be sorted - } else { - r = a.to_owned(); } + // If no further processing needed to be done, return the line as-is to be sorted + result = a; } - r + result } fn get_months_dedup(a: &str) -> String { @@ -714,10 +721,10 @@ fn get_months_dedup(a: &str) -> String { } } -// *For all dedups/uniques we must compare leading numbers* +// *For all dedups/uniques expect default we must compare leading numbers* // Also note numeric compare and unique output is specifically *not* the same as a "sort | uniq" // See: https://www.gnu.org/software/coreutils/manual/html_node/sort-invocation.html -fn get_nums_dedup(a: &str) -> &str { +fn get_num_dedup<'a>(a: &'a str, settings: &&mut Settings) -> &'a str { // Trim and remove any leading zeros let s = a.trim().trim_start_matches('0'); @@ -731,20 +738,50 @@ fn get_nums_dedup(a: &str) -> &str { "" // Prepare lines for comparison of only the numerical leading numbers } else { - get_leading_num(s) + let result = match settings.mode { + SortMode::Numeric => get_leading_num(s), + SortMode::GeneralNumeric => get_leading_gen(s), + SortMode::HumanNumeric => get_leading_num(s), + SortMode::Version => get_leading_num(s), + _ => s, + }; + result + } +} + +#[inline(always)] +fn remove_thousands_sep<'a, S: Into>>(input: S) -> Cow<'a, str> { + let input = input.into(); + if input.contains(THOUSANDS_SEP) { + let output = input.replace(THOUSANDS_SEP, ""); + Cow::Owned(output) + } else { + input + } +} + +#[inline(always)] +fn remove_trailing_dec<'a, S: Into>>(input: S) -> Cow<'a, str> { + let input = input.into(); + if let Some(s) = input.find(DECIMAL_PT) { + let (leading, trailing) = input.split_at(s); + let output = [leading, ".", trailing.replace(DECIMAL_PT, "").as_str()].concat(); + Cow::Owned(output) + } else { + input } } /// Parse the beginning string into an f64, returning -inf instead of NaN on errors. #[inline(always)] fn permissive_f64_parse(a: &str) -> f64 { - // Remove thousands seperators - let a = a.replace(THOUSANDS_SEP, ""); - // GNU sort treats "NaN" as non-number in numeric, so it needs special care. // *Keep this trim before parse* despite what POSIX may say about -b and -n // because GNU and BSD both seem to require it to match their behavior - match a.trim().parse::() { + // + // Remove any trailing decimals, ie 4568..890... becomes 4568.890 + // Then, we trim whitespace and parse + match remove_trailing_dec(a).trim().parse::() { Ok(a) if a.is_nan() => std::f64::NEG_INFINITY, Ok(a) => a, Err(_) => std::f64::NEG_INFINITY, @@ -757,8 +794,13 @@ fn numeric_compare(a: &str, b: &str) -> Ordering { let sa = get_leading_num(a); let sb = get_leading_num(b); - let fa = permissive_f64_parse(sa); - let fb = permissive_f64_parse(sb); + // Avoids a string alloc for every line to remove thousands seperators here + // instead of inside the get_leading_num function, which is a HUGE performance benefit + let ta = remove_thousands_sep(sa); + let tb = remove_thousands_sep(sb); + + let fa = permissive_f64_parse(&ta); + let fb = permissive_f64_parse(&tb); // f64::cmp isn't implemented (due to NaN issues); implement directly instead if fa > fb { @@ -799,8 +841,8 @@ fn general_numeric_compare(a: &str, b: &str) -> Ordering { // these types of numbers, we rarely care about pure performance. fn human_numeric_convert(a: &str) -> f64 { let num_str = get_leading_num(a); - let suffix = a.trim_start_matches(num_str); - let num_part = permissive_f64_parse(num_str); + let suffix = a.trim_start_matches(&num_str); + let num_part = permissive_f64_parse(&num_str); let suffix: f64 = match suffix.parse().unwrap_or('\0') { // SI Units 'K' => 1E3, diff --git a/tests/.DS_Store b/tests/.DS_Store new file mode 100644 index 0000000000000000000000000000000000000000..5008ddfcf53c02e82d7eee2e57c38e5672ef89f6 GIT binary patch literal 6148 zcmZQzU|@7AO)+F(5MW?n;9!8z45|!R0Z1N%F(jFgL>QrFAPJ2!M?+vV1V%$(Gz3ON zU^D~25V%SxcdJP zRior+2#kinunYl47MEZbCs3t{!+W4QHvuXKVuPw;Mo^s$(F3lEVT}ML$bg~*R5_@+ b2Uo?6kTwK}57Iu`5P${HC_Nei0}uiLNUI8I literal 0 HcmV?d00001 diff --git a/tests/by-util/test_sort.rs b/tests/by-util/test_sort.rs index 43aaf1da1..6455d837b 100644 --- a/tests/by-util/test_sort.rs +++ b/tests/by-util/test_sort.rs @@ -1,5 +1,31 @@ use crate::common::util::*; +fn test_helper(file_name: &str, args: &str) { + new_ucmd!() + .arg(args) + .arg(format!("{}.txt", file_name)) + .succeeds() + .stdout_is_fixture(format!("{}.expected", file_name)); +} + +#[test] +fn test_multiple_decimals_general() { + new_ucmd!() + .arg("-g") + .arg("multiple_decimals_general.txt") + .succeeds() + .stdout_is("\n\n\n\n\n\n\n\nCARAvan\n-2028789030\n-896689\n-8.90880\n-1\n-.05\n000\n00000001\n1\n1.040000000\n1.444\n1.58590\n8.013\n45\n46.89\n576,446.88800000\n576,446.890\n 4567.\n4567.1\n4567.34\n\t\t\t\t\t\t\t\t\t\t4567..457\n\t\t\t\t37800\n\t\t\t\t\t\t45670.89079.098\n\t\t\t\t\t\t45670.89079.1\n4798908.340000000000\n4798908.45\n4798908.8909800\n"); +} + +#[test] +fn test_multiple_decimals_numeric() { + new_ucmd!() + .arg("-n") + .arg("multiple_decimals_numeric.txt") + .succeeds() + .stdout_is("-2028789030\n-896689\n-8.90880\n-1\n-.05\n\n\n\n\n\n\n\n\n000\nCARAvan\n00000001\n1\n1.040000000\n1.444\n1.58590\n8.013\n45\n46.89\n 4567.\n4567.1\n4567.34\n\t\t\t\t\t\t\t\t\t\t4567..457\n\t\t\t\t37800\n\t\t\t\t\t\t45670.89079.098\n\t\t\t\t\t\t45670.89079.1\n576,446.88800000\n576,446.890\n4798908.340000000000\n4798908.45\n4798908.8909800\n"); +} + #[test] fn test_check_zero_terminated_failure() { new_ucmd!() @@ -44,6 +70,21 @@ fn test_random_shuffle_contains_all_lines() { assert_eq!(result_sorted, expected); } +#[test] +fn test_random_shuffle_two_runs_not_the_same() { + // check to verify that two random shuffles are not equal; this has the + // potential to fail in the very unlikely event that the random order is the same + // as the starting order, or if both random sorts end up having the same order. + const FILE: &'static str = "default_unsorted_ints.expected"; + let (at, _ucmd) = at_and_ucmd!(); + let result = new_ucmd!().arg("-R").arg(FILE).run().stdout; + let expected = at.read(FILE); + let unexpected = new_ucmd!().arg("-R").arg(FILE).run().stdout; + + assert_ne!(result, expected); + assert_ne!(result, unexpected); +} + #[test] fn test_random_shuffle_contains_two_runs_not_the_same() { // check to verify that two random shuffles are not equal; this has the @@ -355,11 +396,3 @@ fn test_check_silent() { .fails() .stdout_is(""); } - -fn test_helper(file_name: &str, args: &str) { - new_ucmd!() - .arg(args) - .arg(format!("{}{}", file_name, ".txt")) - .succeeds() - .stdout_is_fixture(format!("{}{}", file_name, ".expected")); -} diff --git a/tests/fixtures/.DS_Store b/tests/fixtures/.DS_Store new file mode 100644 index 0000000000000000000000000000000000000000..607a7386a75b94e7df52bcee971a48217dc92331 GIT binary patch literal 6148 zcmZQzU|@7AO)+F(5MW?n;9!8zOq>i@0Z1N%F(jFwA|RR(WJXeXaY0f}ei8!%!%3*z zC^fi402FsD48;uj3`Gnj$nlp{kds+lVqkEck%^gwm5rSP1b8`OgER8WgG&-iN{gKm zi=siifW(rFBq%#1KR*Y~PD~2ROf8QW5OL1WD@n}EODzH^56(kXDc!NGpg2X=Pvp zvB2_RtqhC|5Uq^hZU_SdBe+WfqQTl37#YCY85kMB+8JQ&JVuCi21bZ>21aNPg%Q-F z0htfc&cF!K4s+fpJsJX|Api{lW(X|+s{dUX7;yFfA*x2n(GVC7fngZ}j4Up}E>56I z6NmRebuFkqO@PXSYJX65%m}Kd5n|w~mEQChs K(GZ}22mk;)qfplX literal 0 HcmV?d00001 diff --git a/tests/fixtures/sort/mixed_floats_ints_chars_numeric_unique_reverse_stable.expected b/tests/fixtures/sort/mixed_floats_ints_chars_numeric_unique_reverse_stable.expected new file mode 100644 index 000000000..bbce16934 --- /dev/null +++ b/tests/fixtures/sort/mixed_floats_ints_chars_numeric_unique_reverse_stable.expected @@ -0,0 +1,20 @@ +4798908.8909800 +4798908.45 +4798908.340000000000 +576,446.890 +576,446.88800000 + 37800 + 4567. +46.89 +45 +8.013 +1.58590 +1.444 +1.040000000 +1 + +-.05 +-1 +-8.90880 +-896689 +-2028789030 diff --git a/tests/fixtures/sort/multiple_decimals.expected b/tests/fixtures/sort/multiple_decimals.expected new file mode 100644 index 000000000..6afbdcaa0 --- /dev/null +++ b/tests/fixtures/sort/multiple_decimals.expected @@ -0,0 +1,33 @@ +-2028789030 +-896689 +-8.90880 +-1 +-.05 + + + + + + + + +000 +CARAvan +00000001 +1 +1.040000000 +1.444 +1.58590 +8.013 +45 +46.89 + 4567..457 + 4567. +4567.1 +4567.34 + 37800 +576,446.88800000 +576,446.890 +4798908.340000000000 +4798908.45 +4798908.8909800 diff --git a/tests/fixtures/sort/multiple_decimals_general.txt b/tests/fixtures/sort/multiple_decimals_general.txt new file mode 100644 index 000000000..4e65ecfda --- /dev/null +++ b/tests/fixtures/sort/multiple_decimals_general.txt @@ -0,0 +1,35 @@ +576,446.890 +576,446.88800000 + +4567.1 + 4567..457 + 45670.89079.1 + 45670.89079.098 +4567.34 + 4567. +45 +46.89 +-1 +1 +00000001 +4798908.340000000000 +4798908.45 +4798908.8909800 + + + 37800 + +-2028789030 +-896689 +CARAvan + +-8.90880 +-.05 +1.444 +1.58590 +1.040000000 + +8.013 + +000 + diff --git a/tests/fixtures/sort/multiple_decimals_numeric.txt b/tests/fixtures/sort/multiple_decimals_numeric.txt new file mode 100644 index 000000000..4e65ecfda --- /dev/null +++ b/tests/fixtures/sort/multiple_decimals_numeric.txt @@ -0,0 +1,35 @@ +576,446.890 +576,446.88800000 + +4567.1 + 4567..457 + 45670.89079.1 + 45670.89079.098 +4567.34 + 4567. +45 +46.89 +-1 +1 +00000001 +4798908.340000000000 +4798908.45 +4798908.8909800 + + + 37800 + +-2028789030 +-896689 +CARAvan + +-8.90880 +-.05 +1.444 +1.58590 +1.040000000 + +8.013 + +000 + From a9209049bf586ce40008d6a1151d2f21adef3c10 Mon Sep 17 00:00:00 2001 From: Sylvestre Ledru Date: Fri, 9 Apr 2021 22:18:52 +0200 Subject: [PATCH 02/55] fix a typo Co-authored-by: Michael Debertol --- src/uu/sort/src/sort.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/uu/sort/src/sort.rs b/src/uu/sort/src/sort.rs index 211f87672..4aa3fbed2 100644 --- a/src/uu/sort/src/sort.rs +++ b/src/uu/sort/src/sort.rs @@ -607,7 +607,7 @@ fn default_compare(a: &str, b: &str) -> Ordering { fn leading_num_common(a: &str) -> &str { let mut s = ""; - // check whether char is numeric, whitespace or decimal point or thousand seperator + // check whether char is numeric, whitespace or decimal point or thousand separator for (idx, c) in a.char_indices() { if !c.is_numeric() && !c.is_whitespace() From 2d9f15d12cca22d2a397a7de851b6b768ebc88fe Mon Sep 17 00:00:00 2001 From: electricboogie <32370782+electricboogie@users.noreply.github.com> Date: Sat, 10 Apr 2021 12:02:02 -0500 Subject: [PATCH 03/55] Fix month parse for months with leading whitespace --- src/uu/sort/src/sort.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/uu/sort/src/sort.rs b/src/uu/sort/src/sort.rs index 4f669f578..f60302622 100644 --- a/src/uu/sort/src/sort.rs +++ b/src/uu/sort/src/sort.rs @@ -1291,7 +1291,7 @@ fn month_parse(line: &str) -> Month { // GNU splits at any 3 letter match "JUNNNN" is JUN let pattern = if line.trim().len().ge(&3) { // Split a 3 and get first element of tuple ".0" - line.split_at(3).0 + line.trim().split_at(3).0 } else { "" }; From 77411f3fb5b95b893894958497fbef7dafb6a14c Mon Sep 17 00:00:00 2001 From: electricboogie <32370782+electricboogie@users.noreply.github.com> Date: Sat, 10 Apr 2021 13:36:57 -0500 Subject: [PATCH 04/55] Implement test for months whitespace fix --- Cargo.lock | 2 -- tests/by-util/test_sort.rs | 5 +++++ tests/fixtures/sort/months-whitespace.expected | 8 ++++++++ tests/fixtures/sort/months-whitespace.txt | 8 ++++++++ 4 files changed, 21 insertions(+), 2 deletions(-) create mode 100644 tests/fixtures/sort/months-whitespace.expected create mode 100644 tests/fixtures/sort/months-whitespace.txt diff --git a/Cargo.lock b/Cargo.lock index a6ddf7105..d45e41c16 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1,7 +1,5 @@ # This file is automatically @generated by Cargo. # It is not intended for manual editing. -version = 3 - [[package]] name = "advapi32-sys" version = "0.2.0" diff --git a/tests/by-util/test_sort.rs b/tests/by-util/test_sort.rs index 866beefff..c09ebd256 100644 --- a/tests/by-util/test_sort.rs +++ b/tests/by-util/test_sort.rs @@ -8,6 +8,11 @@ fn test_helper(file_name: &str, args: &str) { .stdout_is_fixture(format!("{}.expected", file_name)); } +#[test] +fn test_months_whitespace() { + test_helper("months-whitespace", "-M"); +} + #[test] fn test_multiple_decimals_general() { new_ucmd!() diff --git a/tests/fixtures/sort/months-whitespace.expected b/tests/fixtures/sort/months-whitespace.expected new file mode 100644 index 000000000..84a44d564 --- /dev/null +++ b/tests/fixtures/sort/months-whitespace.expected @@ -0,0 +1,8 @@ + + +JAN + FEb + apr + apr + JUNNNN +AUG diff --git a/tests/fixtures/sort/months-whitespace.txt b/tests/fixtures/sort/months-whitespace.txt new file mode 100644 index 000000000..45c477477 --- /dev/null +++ b/tests/fixtures/sort/months-whitespace.txt @@ -0,0 +1,8 @@ +JAN + JUNNNN +AUG + + apr + apr + + FEb From 9bcf752b0c3feb820a87a31968dfac2f121301fc Mon Sep 17 00:00:00 2001 From: electricboogie <32370782+electricboogie@users.noreply.github.com> Date: Sat, 10 Apr 2021 14:06:24 -0500 Subject: [PATCH 05/55] Confirm human numeric works as expected with whitespace with a test --- tests/by-util/test_sort.rs | 5 +++++ tests/fixtures/sort/human-numeric-whitespace.expected | 11 +++++++++++ tests/fixtures/sort/human-numeric-whitespace.txt | 11 +++++++++++ 3 files changed, 27 insertions(+) create mode 100644 tests/fixtures/sort/human-numeric-whitespace.expected create mode 100644 tests/fixtures/sort/human-numeric-whitespace.txt diff --git a/tests/by-util/test_sort.rs b/tests/by-util/test_sort.rs index c09ebd256..23ce3258d 100644 --- a/tests/by-util/test_sort.rs +++ b/tests/by-util/test_sort.rs @@ -13,6 +13,11 @@ fn test_months_whitespace() { test_helper("months-whitespace", "-M"); } +#[test] +fn test_human_numeric_whitespace() { + test_helper("human-numeric-whitespace", "-h"); +} + #[test] fn test_multiple_decimals_general() { new_ucmd!() diff --git a/tests/fixtures/sort/human-numeric-whitespace.expected b/tests/fixtures/sort/human-numeric-whitespace.expected new file mode 100644 index 000000000..6fb9291ff --- /dev/null +++ b/tests/fixtures/sort/human-numeric-whitespace.expected @@ -0,0 +1,11 @@ + + + + + + + +456K +4568K + 456M + 6.2G diff --git a/tests/fixtures/sort/human-numeric-whitespace.txt b/tests/fixtures/sort/human-numeric-whitespace.txt new file mode 100644 index 000000000..19db648b1 --- /dev/null +++ b/tests/fixtures/sort/human-numeric-whitespace.txt @@ -0,0 +1,11 @@ + + +456K + + 456M + + +4568K + + 6.2G + From 713327372529c335551eccf36ff17ab55266e651 Mon Sep 17 00:00:00 2001 From: electricboogie <32370782+electricboogie@users.noreply.github.com> Date: Sat, 10 Apr 2021 14:13:49 -0500 Subject: [PATCH 06/55] Correct arg help value name for --parallel --- src/uu/sort/src/sort.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/uu/sort/src/sort.rs b/src/uu/sort/src/sort.rs index 10e4229d4..6611a70e4 100644 --- a/src/uu/sort/src/sort.rs +++ b/src/uu/sort/src/sort.rs @@ -677,7 +677,7 @@ pub fn uumain(args: impl uucore::Args) -> i32 { .arg( Arg::with_name(OPT_PARALLEL) .long(OPT_PARALLEL) - .help("change the number of threads running concurrently to N") + .help("change the number of threads running concurrently to NUM_THREADS") .takes_value(true) .value_name("NUM_THREADS"), ) From c6021e10c2fa46d906cb8d3b804d8348b68d0c92 Mon Sep 17 00:00:00 2001 From: electricboogie <32370782+electricboogie@users.noreply.github.com> Date: Sat, 10 Apr 2021 15:27:16 -0500 Subject: [PATCH 07/55] Fix SemVer non version lines/empty line sorting with a test --- src/uu/sort/src/sort.rs | 15 +++++++++++++-- tests/by-util/test_sort.rs | 9 +++++++++ tests/fixtures/sort/version-empty-lines.expected | 11 +++++++++++ tests/fixtures/sort/version-empty-lines.txt | 11 +++++++++++ 4 files changed, 44 insertions(+), 2 deletions(-) create mode 100644 tests/fixtures/sort/version-empty-lines.expected create mode 100644 tests/fixtures/sort/version-empty-lines.txt diff --git a/src/uu/sort/src/sort.rs b/src/uu/sort/src/sort.rs index 6611a70e4..8bf6eb1e8 100644 --- a/src/uu/sort/src/sort.rs +++ b/src/uu/sort/src/sort.rs @@ -1262,10 +1262,21 @@ fn month_compare(a: &str, b: &str) -> Ordering { } } +fn version_parse(a: &str) -> Version { + let result = Version::parse(a); + + match result { + Ok(vers_a) => vers_a, + // Non-version lines parse to 0.0.0 + Err(_e) => Version::parse("0.0.0").unwrap(), + } +} + fn version_compare(a: &str, b: &str) -> Ordering { #![allow(clippy::comparison_chain)] - let ver_a = Version::parse(a); - let ver_b = Version::parse(b); + let ver_a = version_parse(a); + let ver_b = version_parse(b); + // Version::cmp is not implemented; implement comparison directly if ver_a > ver_b { Ordering::Greater diff --git a/tests/by-util/test_sort.rs b/tests/by-util/test_sort.rs index 23ce3258d..0f8020688 100644 --- a/tests/by-util/test_sort.rs +++ b/tests/by-util/test_sort.rs @@ -13,6 +13,15 @@ fn test_months_whitespace() { test_helper("months-whitespace", "-M"); } +#[test] +fn test_version_empty_lines() { + new_ucmd!() + .arg("-V") + .arg("version-empty-lines.txt") + .succeeds() + .stdout_is("\n\n\n\n\n\n\n1.2.3-alpha\n1.2.3-alpha2\n\t\t\t1.12.4\n11.2.3\n"); +} + #[test] fn test_human_numeric_whitespace() { test_helper("human-numeric-whitespace", "-h"); diff --git a/tests/fixtures/sort/version-empty-lines.expected b/tests/fixtures/sort/version-empty-lines.expected new file mode 100644 index 000000000..c496c0ff5 --- /dev/null +++ b/tests/fixtures/sort/version-empty-lines.expected @@ -0,0 +1,11 @@ + + + + + + + +1.2.3-alpha +1.2.3-alpha2 +11.2.3 + 1.12.4 diff --git a/tests/fixtures/sort/version-empty-lines.txt b/tests/fixtures/sort/version-empty-lines.txt new file mode 100644 index 000000000..9b6b89788 --- /dev/null +++ b/tests/fixtures/sort/version-empty-lines.txt @@ -0,0 +1,11 @@ +11.2.3 + + + +1.2.3-alpha2 + + +1.2.3-alpha + + + 1.12.4 From e6c195a675eb9043ad7ee1668e784de2387bf21b Mon Sep 17 00:00:00 2001 From: electricboogie <32370782+electricboogie@users.noreply.github.com> Date: Mon, 12 Apr 2021 14:24:22 -0500 Subject: [PATCH 08/55] ExtSort --- Cargo.lock | 139 +++++++++++++++++++++++++++++++++++++++- src/uu/sort/Cargo.toml | 6 +- src/uu/sort/src/sort.rs | 96 +++++++++++++++++++++++---- 3 files changed, 223 insertions(+), 18 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index d45e41c16..052d6de40 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -119,12 +119,40 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "560c32574a12a89ecd91f5e742165893f86e3ab98d21f8ea548658eb9eef5f40" +[[package]] +name = "bytecount" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72feb31ffc86498dacdbd0fcebb56138e7177a8cc5cea4516031d15ae85a742e" + [[package]] name = "byteorder" version = "1.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" +[[package]] +name = "cargo-platform" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0226944a63d1bf35a3b5f948dd7c59e263db83695c9e8bffc4037de02e30f1d7" +dependencies = [ + "serde", +] + +[[package]] +name = "cargo_metadata" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7714a157da7991e23d90686b9524b9e12e0407a108647f52e9328f4b3d51ac7f" +dependencies = [ + "cargo-platform", + "semver 0.11.0", + "semver-parser 0.10.2", + "serde", + "serde_json", +] + [[package]] name = "cast" version = "0.2.3" @@ -560,6 +588,26 @@ dependencies = [ "regex", ] +[[package]] +name = "error-chain" +version = "0.12.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2d2f06b9cac1506ece98fe3231e3cc9c4410ec3d5b1f24ae1c8946f0742cdefc" +dependencies = [ + "version_check", +] + +[[package]] +name = "extsort" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ffc5bb6fbca3c5ce6a51f6857eab8c35c898b2fbcb62ff1b728243dd19ec0c9f" +dependencies = [ + "rayon", + "skeptic", + "tempfile", +] + [[package]] name = "fake-simd" version = "0.1.2" @@ -944,6 +992,15 @@ dependencies = [ "proc-macro-hack", ] +[[package]] +name = "pest" +version = "2.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "10f4872ae94d7b90ae48754df22fd42ad52ce740b8f370b03da4835417403e53" +dependencies = [ + "ucd-trie", +] + [[package]] name = "pkg-config" version = "0.3.19" @@ -1009,6 +1066,17 @@ dependencies = [ "unicode-xid 0.2.1", ] +[[package]] +name = "pulldown-cmark" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ffade02495f22453cd593159ea2f59827aae7f53fa8323f756799b670881dcf8" +dependencies = [ + "bitflags", + "memchr 2.3.4", + "unicase", +] + [[package]] name = "quick-error" version = "1.2.3" @@ -1245,7 +1313,7 @@ version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a" dependencies = [ - "semver", + "semver 0.9.0", ] [[package]] @@ -1275,7 +1343,17 @@ version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403" dependencies = [ - "semver-parser", + "semver-parser 0.7.0", +] + +[[package]] +name = "semver" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f301af10236f6df4160f7c3f04eec6dbc70ace82d23326abad5edee88801c6b6" +dependencies = [ + "semver-parser 0.10.2", + "serde", ] [[package]] @@ -1284,11 +1362,23 @@ version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" +[[package]] +name = "semver-parser" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00b0bef5b7f9e0df16536d3961cfb6e84331c065b4066afb39768d0e319411f7" +dependencies = [ + "pest", +] + [[package]] name = "serde" version = "1.0.125" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "558dc50e1a5a5fa7112ca2ce4effcb321b0300c0d4ccf0776a9f60cd89031171" +dependencies = [ + "serde_derive", +] [[package]] name = "serde_cbor" @@ -1353,6 +1443,21 @@ dependencies = [ "generic-array", ] +[[package]] +name = "skeptic" +version = "0.13.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "188b810342d98f23f0bb875045299f34187b559370b041eb11520c905370a888" +dependencies = [ + "bytecount", + "cargo_metadata", + "error-chain", + "glob 0.3.0", + "pulldown-cmark", + "tempfile", + "walkdir", +] + [[package]] name = "smallvec" version = "0.6.14" @@ -1367,6 +1472,9 @@ name = "smallvec" version = "1.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fe0f37c9e8f3c5a4a66ad655a93c74daac4ad00c441533bf5c6e7990bb42604e" +dependencies = [ + "serde", +] [[package]] name = "strsim" @@ -1528,6 +1636,21 @@ version = "1.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "879f6906492a7cd215bfa4cf595b600146ccfac0c79bcbd1f3000162af5e8b06" +[[package]] +name = "ucd-trie" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56dee185309b50d1f11bfedef0fe6d036842e3fb77413abef29f8f8d1c5d4c1c" + +[[package]] +name = "unicase" +version = "2.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50f37be617794602aabbeee0be4f259dc1778fabe05e2d67ee8f79326d5cb4f6" +dependencies = [ + "version_check", +] + [[package]] name = "unicode-segmentation" version = "1.7.1" @@ -2289,12 +2412,16 @@ dependencies = [ name = "uu_sort" version = "0.0.6" dependencies = [ + "byteorder", "clap", + "extsort", "fnv", "itertools 0.10.0", "rand 0.7.3", "rayon", - "semver", + "semver 0.9.0", + "serde", + "serde_json", "smallvec 1.6.1", "uucore", "uucore_procs", @@ -2604,6 +2731,12 @@ version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191" +[[package]] +name = "version_check" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5fecdca9a5291cc2b8dcf7dc02453fee791a280f3743cb0905f8822ae463b3fe" + [[package]] name = "void" version = "1.0.2" diff --git a/src/uu/sort/Cargo.toml b/src/uu/sort/Cargo.toml index 6a9976278..8ad0a681f 100644 --- a/src/uu/sort/Cargo.toml +++ b/src/uu/sort/Cargo.toml @@ -15,13 +15,17 @@ edition = "2018" path = "src/sort.rs" [dependencies] +byteorder = "1.4.3" +extsort = "0.4.2" +serde_json = { version = "1.0", default-features = false, features = ["alloc"] } +serde = { version = "1.0", features = ["derive"] } rayon = "1.5" rand = "0.7" clap = "2.33" fnv = "1.0.7" itertools = "0.10.0" semver = "0.9.0" -smallvec = "1.6.1" +smallvec = { version = "1.6.1", features = ["serde"] } uucore = { version=">=0.0.8", package="uucore", path="../../uucore", features=["fs"] } uucore_procs = { version=">=0.0.5", package="uucore_procs", path="../../uucore_procs" } diff --git a/src/uu/sort/src/sort.rs b/src/uu/sort/src/sort.rs index 8bf6eb1e8..cb07f60b7 100644 --- a/src/uu/sort/src/sort.rs +++ b/src/uu/sort/src/sort.rs @@ -20,7 +20,6 @@ use fnv::FnvHasher; use itertools::Itertools; use rand::distributions::Alphanumeric; use rand::{thread_rng, Rng}; -use rayon::prelude::*; use semver::Version; use smallvec::SmallVec; use std::borrow::Cow; @@ -34,6 +33,14 @@ use std::mem::replace; use std::ops::{Range, RangeInclusive}; use std::path::Path; use uucore::fs::is_stdin_interactive; // for Iterator::dedup() +use extsort::*; +use std::str; +use serde::{Serialize, Deserialize}; +use std::ffi::OsString; +use std::usize; +use std::path::PathBuf; +use std::string::*; +use serde_json::Result; static NAME: &str = "sort"; static ABOUT: &str = "Display sorted concatenation of all FILE(s)."; @@ -72,6 +79,8 @@ static OPT_RANDOM: &str = "random-sort"; static OPT_ZERO_TERMINATED: &str = "zero-terminated"; static OPT_PARALLEL: &str = "parallel"; static OPT_FILES0_FROM: &str = "files0-from"; +static OPT_BUF_SIZE: &str = "buffer-size"; +static OPT_TMP_DIR: &str = "temporary-directory"; static ARG_FILES: &str = "files"; @@ -110,6 +119,8 @@ struct GlobalSettings { separator: Option, threads: String, zero_terminated: bool, + buffer_size: usize, + tmp_dir: PathBuf, } impl Default for GlobalSettings { @@ -133,6 +144,8 @@ impl Default for GlobalSettings { separator: None, threads: String::new(), zero_terminated: false, + buffer_size: 10000000usize, + tmp_dir: PathBuf::from(r"/tmp"), } } } @@ -162,7 +175,7 @@ impl From<&GlobalSettings> for KeySettings { } /// Represents the string selected by a FieldSelector. -#[derive(Debug)] +#[derive(Debug, Serialize, Deserialize, Clone)] enum Selection { /// If we had to transform this selection, we have to store a new string. String(String), @@ -182,13 +195,29 @@ impl Selection { type Field = Range; -#[derive(Debug)] +#[derive(Serialize, Deserialize, Debug, Clone)] struct Line { line: String, // The common case is not to specify fields. Let's make this fast. selections: SmallVec<[Selection; 1]>, } +impl Sortable for Line { + fn encode(&self, write: &mut W) { + let line = Line { line: self.line.clone(), selections: self.selections.clone() } ; + let serialized = serde_json::to_string(&line).unwrap(); + write.write_all(serialized.as_bytes()).unwrap(); + } + + fn decode(read: &mut R) -> Option { + let mut buf = String::new(); + read.read_to_string(&mut buf).ok(); + let line: Option = buf; + println!("deserialized = {:?}", line); + line + } +} + impl Line { fn new(line: String, settings: &GlobalSettings) -> Self { let fields = if settings @@ -681,6 +710,20 @@ pub fn uumain(args: impl uucore::Args) -> i32 { .takes_value(true) .value_name("NUM_THREADS"), ) + .arg( + Arg::with_name(OPT_BUF_SIZE) + .long(OPT_BUF_SIZE) + .help("sets the maximum SIZE of each segment in number of sorted items") + .takes_value(true) + .value_name("SIZE"), + ) + .arg( + Arg::with_name(OPT_TMP_DIR) + .long(OPT_TMP_DIR) + .help("use DIR for temporaries, not $TMPDIR or /tmp") + .takes_value(true) + .value_name("DIR"), + ) .arg( Arg::with_name(OPT_FILES0_FROM) .long(OPT_FILES0_FROM) @@ -744,6 +787,32 @@ pub fn uumain(args: impl uucore::Args) -> i32 { env::set_var("RAYON_NUM_THREADS", &settings.threads); } + if matches.is_present(OPT_BUF_SIZE) { + // 10000 is the default extsort buffer, but it's too small + settings.buffer_size = matches + .value_of(OPT_BUF_SIZE) + .map(String::from) + .unwrap_or( format! ( "{}", 10000000usize ) ) + .parse::() + .unwrap_or(10000000usize); + } + + if matches.is_present(OPT_TMP_DIR) { + let result = matches + .value_of(OPT_TMP_DIR) + .map(String::from) + .unwrap_or("/tmp".to_owned() ); + settings.tmp_dir = PathBuf::from(format!(r"{}", result)); + } else { + for (key, value) in env::vars_os() { + if key == OsString::from("TMPDIR") { + settings.tmp_dir = PathBuf::from(format!(r"{}", value.into_string().unwrap_or("/tmp".to_owned()))); + break + } + settings.tmp_dir = PathBuf::from(r"/tmp"); + } + } + settings.zero_terminated = matches.is_present(OPT_ZERO_TERMINATED); settings.merge = matches.is_present(OPT_MERGE); @@ -860,9 +929,9 @@ fn exec(files: Vec, settings: &GlobalSettings) -> i32 { if settings.check { return exec_check_file(&lines, &settings); - } else { - sort_by(&mut lines, &settings); } + + lines = sort_by(lines, &settings); if settings.merge { if settings.unique { @@ -917,8 +986,9 @@ fn exec_check_file(unwrapped_lines: &[Line], settings: &GlobalSettings) -> i32 { } } -fn sort_by(lines: &mut Vec, settings: &GlobalSettings) { - lines.par_sort_by(|a, b| compare_by(a, b, &settings)) +fn sort_by(lines: Vec, settings: &GlobalSettings) -> Vec { + let sorter = ExternalSorter::new().with_segment_size(settings.buffer_size).with_sort_dir(settings.tmp_dir.clone()).with_parallel_sort(); + sorter.sort_by(lines.into_iter(), |a, b| compare_by(a, b, &settings)).unwrap().collect() } fn compare_by(a: &Line, b: &Line, global_settings: &GlobalSettings) -> Ordering { @@ -1004,7 +1074,6 @@ fn leading_num_common(a: &str) -> &str { // not recognize a positive sign or scientific/E notation so we strip those elements here. fn get_leading_num(a: &str) -> &str { let mut s = ""; - let a = leading_num_common(a); // GNU numeric sort doesn't recognize '+' or 'e' notation so we strip @@ -1019,9 +1088,7 @@ fn get_leading_num(a: &str) -> &str { // And empty number or non-number lines are to be treated as ‘0’ but only for numeric sort // All '0'-ed lines will be sorted later, but only amongst themselves, during the so-called 'last resort comparison.' - if s.is_empty() { - s = "0"; - }; + if s.is_empty() { s = "0"; }; s } @@ -1087,8 +1154,8 @@ fn permissive_f64_parse(a: &str) -> f64 { // Remove any trailing decimals, ie 4568..890... becomes 4568.890 // Then, we trim whitespace and parse match remove_trailing_dec(a).trim().parse::() { - Ok(a) if a.is_nan() => std::f64::NEG_INFINITY, - Ok(a) => a, + Ok(val) if val.is_nan() => std::f64::NEG_INFINITY, + Ok(val) => val, Err(_) => std::f64::NEG_INFINITY, } } @@ -1107,7 +1174,6 @@ fn numeric_compare(a: &str, b: &str) -> Ordering { let fa = permissive_f64_parse(&ta); let fb = permissive_f64_parse(&tb); - // f64::cmp isn't implemented (due to NaN issues); implement directly instead if fa > fb { Ordering::Greater } else if fa < fb { @@ -1150,6 +1216,7 @@ fn human_numeric_convert(a: &str) -> f64 { let num_part = permissive_f64_parse(&num_str); let suffix: f64 = match suffix.parse().unwrap_or('\0') { // SI Units + 'b' => 1f64, 'K' => 1E3, 'M' => 1E6, 'G' => 1E9, @@ -1262,6 +1329,7 @@ fn month_compare(a: &str, b: &str) -> Ordering { } } +#[inline(always)] fn version_parse(a: &str) -> Version { let result = Version::parse(a); From c49f93c9af2582c276b85c8e78841797fe7e938d Mon Sep 17 00:00:00 2001 From: electricboogie <32370782+electricboogie@users.noreply.github.com> Date: Mon, 12 Apr 2021 18:05:37 -0500 Subject: [PATCH 09/55] Psuedo working extsort --- src/uu/sort/Cargo.toml | 2 +- src/uu/sort/src/sort.rs | 27 +++++++++++++++------------ 2 files changed, 16 insertions(+), 13 deletions(-) diff --git a/src/uu/sort/Cargo.toml b/src/uu/sort/Cargo.toml index 8ad0a681f..8a3d1ed25 100644 --- a/src/uu/sort/Cargo.toml +++ b/src/uu/sort/Cargo.toml @@ -17,7 +17,7 @@ path = "src/sort.rs" [dependencies] byteorder = "1.4.3" extsort = "0.4.2" -serde_json = { version = "1.0", default-features = false, features = ["alloc"] } +serde_json = { version = "1.0.64", default-features = false, features = ["alloc"] } serde = { version = "1.0", features = ["derive"] } rayon = "1.5" rand = "0.7" diff --git a/src/uu/sort/src/sort.rs b/src/uu/sort/src/sort.rs index cb07f60b7..986db59f8 100644 --- a/src/uu/sort/src/sort.rs +++ b/src/uu/sort/src/sort.rs @@ -40,7 +40,6 @@ use std::ffi::OsString; use std::usize; use std::path::PathBuf; use std::string::*; -use serde_json::Result; static NAME: &str = "sort"; static ABOUT: &str = "Display sorted concatenation of all FILE(s)."; @@ -195,7 +194,7 @@ impl Selection { type Field = Range; -#[derive(Serialize, Deserialize, Debug, Clone)] +#[derive(Debug, Serialize, Deserialize, Clone)] struct Line { line: String, // The common case is not to specify fields. Let's make this fast. @@ -203,18 +202,22 @@ struct Line { } impl Sortable for Line { + fn encode(&self, write: &mut W) { - let line = Line { line: self.line.clone(), selections: self.selections.clone() } ; - let serialized = serde_json::to_string(&line).unwrap(); - write.write_all(serialized.as_bytes()).unwrap(); + let line = Line {line: self.line.clone(), selections: self.selections.clone()}; + let serialized = serde_json::ser::to_string(&line).unwrap(); + write.write_all(format!("{}{}", serialized, "\n").as_bytes()).unwrap(); } fn decode(read: &mut R) -> Option { - let mut buf = String::new(); - read.read_to_string(&mut buf).ok(); - let line: Option = buf; - println!("deserialized = {:?}", line); - line + let buf_reader = BufReader::new(read); + + let mut result: Option = None; + for line in buf_reader.lines() { + let line_as_str: Line = serde_json::de::from_str(&line.unwrap()).unwrap(); + result = Some( Line {line: line_as_str.line, selections: line_as_str.selections} ); + } + result } } @@ -235,7 +238,7 @@ impl Line { .selectors .iter() .map(|selector| { - if let Some(range) = selector.get_selection(&line, fields.as_deref()) { + if let Some(range) = selector.get_field_selection(&line, fields.as_deref()) { if let Some(transformed) = transform(&line[range.to_owned()], &selector.settings) { @@ -411,7 +414,7 @@ impl FieldSelector { /// Look up the slice that corresponds to this selector for the given line. /// If needs_fields returned false, fields may be None. - fn get_selection<'a>( + fn get_field_selection<'a>( &self, line: &'a str, tokens: Option<&[Field]>, From 65e9c7b1b54cbeeed39c508c47992786d37b077d Mon Sep 17 00:00:00 2001 From: electricboogie <32370782+electricboogie@users.noreply.github.com> Date: Sat, 17 Apr 2021 21:30:03 -0500 Subject: [PATCH 10/55] Sorta working ExtSort - concat struct elements --- src/uu/sort/src/numeric_str_cmp.rs | 7 ++++--- src/uu/sort/src/sort.rs | 27 +++++++++++++++++---------- 2 files changed, 21 insertions(+), 13 deletions(-) diff --git a/src/uu/sort/src/numeric_str_cmp.rs b/src/uu/sort/src/numeric_str_cmp.rs index a50734ebd..ac615d1f7 100644 --- a/src/uu/sort/src/numeric_str_cmp.rs +++ b/src/uu/sort/src/numeric_str_cmp.rs @@ -15,19 +15,20 @@ //! From that follows the constraints of this algorithm: It is able to compare numbers in ±(1*10^[i64::MIN]..10*10^[i64::MAX]). use std::{cmp::Ordering, ops::Range}; +use serde::{Serialize, Deserialize}; -#[derive(Debug, PartialEq, Eq, PartialOrd, Ord)] +#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize, Clone)] enum Sign { Negative, Positive, } -#[derive(Debug, PartialEq)] +#[derive(Debug, PartialEq, Serialize, Deserialize, Clone)] pub struct NumInfo { exponent: i64, sign: Sign, } - +#[derive(Debug, PartialEq, Serialize, Deserialize, Clone)] pub struct NumInfoParseSettings { pub accept_si_units: bool, pub thousands_separator: Option, diff --git a/src/uu/sort/src/sort.rs b/src/uu/sort/src/sort.rs index b355c1e68..1e533cf65 100644 --- a/src/uu/sort/src/sort.rs +++ b/src/uu/sort/src/sort.rs @@ -176,6 +176,7 @@ impl From<&GlobalSettings> for KeySettings { } } +#[derive(Debug, Serialize, Deserialize, Clone)] /// Represents the string selected by a FieldSelector. enum SelectionRange { /// If we had to transform this selection, we have to store a new string. @@ -206,7 +207,7 @@ impl SelectionRange { } } } - +#[derive(Debug, Serialize, Deserialize, Clone)] enum NumCache { AsF64(f64), WithInfo(NumInfo), @@ -227,7 +228,7 @@ impl NumCache { } } } - +#[derive(Debug, Serialize, Deserialize, Clone)] struct Selection { range: SelectionRange, num_cache: NumCache, @@ -241,7 +242,7 @@ impl Selection { } type Field = Range; - +#[derive(Debug, Serialize, Deserialize, Clone)] struct Line { line: String, // The common case is not to specify fields. Let's make this fast. @@ -251,7 +252,7 @@ struct Line { impl Sortable for Line { fn encode(&self, write: &mut W) { - let line = Line {line: self.line.clone(), selections: self.selections.clone()}; + let line = Line {line: self.line.to_owned(), selections: self.selections.to_owned() }; let serialized = serde_json::ser::to_string(&line).unwrap(); write.write_all(format!("{}{}", serialized, "\n").as_bytes()).unwrap(); } @@ -259,11 +260,17 @@ impl Sortable for Line { fn decode(read: &mut R) -> Option { let buf_reader = BufReader::new(read); - let mut result: Option = None; - for line in buf_reader.lines() { - let line_as_str: Line = serde_json::de::from_str(&line.unwrap()).unwrap(); - result = Some( Line {line: line_as_str.line, selections: line_as_str.selections} ); - } + let result = { + let mut line_joined= String::new(); + let mut selections_joined= SmallVec::new(); + for line in buf_reader.lines() { + let mut deserialized_line: Line = serde_json::de::from_str(&line.unwrap()).unwrap(); + line_joined = format!("{}\n{}", line_joined, deserialized_line.line); + selections_joined.append(&mut deserialized_line.selections); + selections_joined.dedup(); + } + Some( Line {line: line_joined, selections: selections_joined} ) + }; result } } @@ -286,7 +293,7 @@ impl Line { .iter() .map(|selector| { let mut range = - if let Some(range) = selector.get_selection(&line, fields.as_deref()) { + if let Some(range) = selector.get_field_selection(&line, fields.as_deref()) { if let Some(transformed) = transform(&line[range.to_owned()], &selector.settings) { From 7a8767e359433044ae1e9428b85fa05e93a78caa Mon Sep 17 00:00:00 2001 From: electricboogie <32370782+electricboogie@users.noreply.github.com> Date: Sat, 17 Apr 2021 22:34:03 -0500 Subject: [PATCH 11/55] Cleanup --- src/uu/sort/src/sort.rs | 46 +++++++++++++++++++++++++++++------------ 1 file changed, 33 insertions(+), 13 deletions(-) diff --git a/src/uu/sort/src/sort.rs b/src/uu/sort/src/sort.rs index 1e533cf65..ce6ba8fa7 100644 --- a/src/uu/sort/src/sort.rs +++ b/src/uu/sort/src/sort.rs @@ -43,6 +43,7 @@ use std::ffi::OsString; use std::usize; use std::path::PathBuf; use std::string::*; +use rayon::prelude::*; static NAME: &str = "sort"; static ABOUT: &str = "Display sorted concatenation of all FILE(s)."; @@ -92,6 +93,9 @@ static THOUSANDS_SEP: char = ','; static NEGATIVE: char = '-'; static POSITIVE: char = '+'; +static DEFAULT_TMPDIR: &str = r"/tmp"; +static DEFAULT_BUF_SIZE: usize = 10000000usize; + #[derive(Eq, Ord, PartialEq, PartialOrd, Clone)] enum SortMode { Numeric, @@ -146,8 +150,8 @@ impl Default for GlobalSettings { separator: None, threads: String::new(), zero_terminated: false, - buffer_size: 10000000usize, - tmp_dir: PathBuf::from(r"/tmp"), + buffer_size: DEFAULT_BUF_SIZE, + tmp_dir: PathBuf::from(DEFAULT_TMPDIR), } } } @@ -242,7 +246,7 @@ impl Selection { } type Field = Range; -#[derive(Debug, Serialize, Deserialize, Clone)] +#[derive(Debug, Serialize, Deserialize)] struct Line { line: String, // The common case is not to specify fields. Let's make this fast. @@ -250,16 +254,20 @@ struct Line { } impl Sortable for Line { - fn encode(&self, write: &mut W) { let line = Line {line: self.line.to_owned(), selections: self.selections.to_owned() }; let serialized = serde_json::ser::to_string(&line).unwrap(); + // Valid JSON needs to be seperated by something, so here we use a newline write.write_all(format!("{}{}", serialized, "\n").as_bytes()).unwrap(); } + // This crate asks us to write one line at a time, but returns multiple lines(?). + // However, this crate also expects us to return a result of Option, + // so we concat the these lines into a single Option. So, this may be broken, + // and needs to be tested more thoroughly. Perhaps we need to rethink our struct or rewrite a + // ext sorter ourselves. fn decode(read: &mut R) -> Option { let buf_reader = BufReader::new(read); - let result = { let mut line_joined= String::new(); let mut selections_joined= SmallVec::new(); @@ -267,7 +275,6 @@ impl Sortable for Line { let mut deserialized_line: Line = serde_json::de::from_str(&line.unwrap()).unwrap(); line_joined = format!("{}\n{}", line_joined, deserialized_line.line); selections_joined.append(&mut deserialized_line.selections); - selections_joined.dedup(); } Some( Line {line: line_joined, selections: selections_joined} ) }; @@ -869,16 +876,16 @@ pub fn uumain(args: impl uucore::Args) -> i32 { settings.buffer_size = matches .value_of(OPT_BUF_SIZE) .map(String::from) - .unwrap_or( format! ( "{}", 10000000usize ) ) + .unwrap_or( format! ( "{}", DEFAULT_BUF_SIZE ) ) .parse::() - .unwrap_or(10000000usize); + .unwrap_or( DEFAULT_BUF_SIZE ); } if matches.is_present(OPT_TMP_DIR) { let result = matches .value_of(OPT_TMP_DIR) .map(String::from) - .unwrap_or("/tmp".to_owned() ); + .unwrap_or(DEFAULT_TMPDIR.to_owned() ); settings.tmp_dir = PathBuf::from(format!(r"{}", result)); } else { for (key, value) in env::vars_os() { @@ -886,7 +893,7 @@ pub fn uumain(args: impl uucore::Args) -> i32 { settings.tmp_dir = PathBuf::from(format!(r"{}", value.into_string().unwrap_or("/tmp".to_owned()))); break } - settings.tmp_dir = PathBuf::from(r"/tmp"); + settings.tmp_dir = PathBuf::from(DEFAULT_TMPDIR); } } @@ -1008,7 +1015,11 @@ fn exec(files: Vec, settings: &GlobalSettings) -> i32 { return exec_check_file(&lines, &settings); } - lines = sort_by(lines, &settings); + if ( settings.buffer_size != DEFAULT_BUF_SIZE ) || ( settings.tmp_dir.as_os_str() != DEFAULT_TMPDIR ) { + lines = ext_sort_by(lines, &settings); + } else { + sort_by(&mut lines, &settings); + } if settings.merge { if settings.unique { @@ -1063,9 +1074,18 @@ fn exec_check_file(unwrapped_lines: &[Line], settings: &GlobalSettings) -> i32 { } } -fn sort_by(lines: Vec, settings: &GlobalSettings) -> Vec { +fn ext_sort_by(lines: Vec, settings: &GlobalSettings) -> Vec { let sorter = ExternalSorter::new().with_segment_size(settings.buffer_size).with_sort_dir(settings.tmp_dir.clone()).with_parallel_sort(); - sorter.sort_by(lines.into_iter(), |a, b| compare_by(a, b, &settings)).unwrap().collect() + let result = sorter.sort_by(lines.into_iter(), |a, b| compare_by(a, b, &settings)).unwrap().collect(); + result +} + +fn sort_by(lines: &mut Vec, settings: &GlobalSettings) { + if settings.stable || settings.unique { + lines.par_sort_by(|a, b| compare_by(a, b, &settings)) + } else { + lines.par_sort_unstable_by(|a, b| compare_by(a, b, &settings)) + } } fn compare_by(a: &Line, b: &Line, global_settings: &GlobalSettings) -> Ordering { From 3a1e92fdd286f7078957a18b0eda2b4b45c11bd2 Mon Sep 17 00:00:00 2001 From: electricboogie <32370782+electricboogie@users.noreply.github.com> Date: Sat, 17 Apr 2021 22:39:05 -0500 Subject: [PATCH 12/55] More cleanup --- src/uu/sort/src/sort.rs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/uu/sort/src/sort.rs b/src/uu/sort/src/sort.rs index ce6ba8fa7..1c72e3427 100644 --- a/src/uu/sort/src/sort.rs +++ b/src/uu/sort/src/sort.rs @@ -180,7 +180,7 @@ impl From<&GlobalSettings> for KeySettings { } } -#[derive(Debug, Serialize, Deserialize, Clone)] +#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)] /// Represents the string selected by a FieldSelector. enum SelectionRange { /// If we had to transform this selection, we have to store a new string. @@ -211,7 +211,7 @@ impl SelectionRange { } } } -#[derive(Debug, Serialize, Deserialize, Clone)] +#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)] enum NumCache { AsF64(f64), WithInfo(NumInfo), @@ -232,7 +232,7 @@ impl NumCache { } } } -#[derive(Debug, Serialize, Deserialize, Clone)] +#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)] struct Selection { range: SelectionRange, num_cache: NumCache, @@ -275,6 +275,7 @@ impl Sortable for Line { let mut deserialized_line: Line = serde_json::de::from_str(&line.unwrap()).unwrap(); line_joined = format!("{}\n{}", line_joined, deserialized_line.line); selections_joined.append(&mut deserialized_line.selections); + selections_joined.dedup(); } Some( Line {line: line_joined, selections: selections_joined} ) }; From 4c8d62c2be89dba9158106abf47c0b97392a5f5e Mon Sep 17 00:00:00 2001 From: electricboogie <32370782+electricboogie@users.noreply.github.com> Date: Sat, 17 Apr 2021 23:24:32 -0500 Subject: [PATCH 13/55] More cleanup --- src/uu/sort/src/sort.rs | 42 ++++++++++++++++++++++++++++++++--------- 1 file changed, 33 insertions(+), 9 deletions(-) diff --git a/src/uu/sort/src/sort.rs b/src/uu/sort/src/sort.rs index 1c72e3427..9052b2a5b 100644 --- a/src/uu/sort/src/sort.rs +++ b/src/uu/sort/src/sort.rs @@ -257,14 +257,14 @@ impl Sortable for Line { fn encode(&self, write: &mut W) { let line = Line {line: self.line.to_owned(), selections: self.selections.to_owned() }; let serialized = serde_json::ser::to_string(&line).unwrap(); - // Valid JSON needs to be seperated by something, so here we use a newline + // Each instance of valid JSON needs to be seperated by something, so here we use a newline write.write_all(format!("{}{}", serialized, "\n").as_bytes()).unwrap(); } - // This crate asks us to write one line at a time, but returns multiple lines(?). + // This crate asks us to write one Line at a time, but returns multiple Lines to us(?). // However, this crate also expects us to return a result of Option, - // so we concat the these lines into a single Option. So, this may be broken, - // and needs to be tested more thoroughly. Perhaps we need to rethink our struct or rewrite a + // so we concat the these lines into a single Option. So, it's possible this is broken, + // and/or needs to be tested more thoroughly. Perhaps we need to rethink our Line struct or rewrite a // ext sorter ourselves. fn decode(read: &mut R) -> Option { let buf_reader = BufReader::new(read); @@ -274,6 +274,7 @@ impl Sortable for Line { for line in buf_reader.lines() { let mut deserialized_line: Line = serde_json::de::from_str(&line.unwrap()).unwrap(); line_joined = format!("{}\n{}", line_joined, deserialized_line.line); + // I think we've done our sorting already and these are irrelevant? @miDeb what's your sense? selections_joined.append(&mut deserialized_line.selections); selections_joined.dedup(); } @@ -873,13 +874,16 @@ pub fn uumain(args: impl uucore::Args) -> i32 { } if matches.is_present(OPT_BUF_SIZE) { - // 10000 is the default extsort buffer, but it's too small - settings.buffer_size = matches + // 10K is the default extsort buffer, but that's too small, so we set at 10M + // Although the "default" is never used unless extsort options are given + settings.buffer_size = { + let input = matches .value_of(OPT_BUF_SIZE) .map(String::from) - .unwrap_or( format! ( "{}", DEFAULT_BUF_SIZE ) ) - .parse::() - .unwrap_or( DEFAULT_BUF_SIZE ); + .unwrap_or( format! ( "{}", DEFAULT_BUF_SIZE ) ); + + human_numeric_convert(&input) + } } if matches.is_present(OPT_TMP_DIR) { @@ -1133,6 +1137,26 @@ fn compare_by(a: &Line, b: &Line, global_settings: &GlobalSettings) -> Ordering } } +// Brought back! Probably want to do through numstrcmp somehow now +fn human_numeric_convert(a: &str) -> usize { + let num_part = leading_num_common(a); + let (_, s) = a.split_at(num_part.len()); + let num_part = permissive_f64_parse(num_part); + let suffix = match s.parse().unwrap_or('\0') { + // SI Units + 'K' => 1E3, + 'M' => 1E6, + 'G' => 1E9, + 'T' => 1E12, + 'P' => 1E15, + 'E' => 1E18, + 'Z' => 1E21, + 'Y' => 1E24, + _ => 1f64, + }; + num_part as usize * suffix as usize +} + // Test output against BSDs and GNU with their locale // env var set to lc_ctype=utf-8 to enjoy the exact same output. #[inline(always)] From d7b7ce52bc28904f8af8ae36a9e43e698cbdd295 Mon Sep 17 00:00:00 2001 From: electricboogie <32370782+electricboogie@users.noreply.github.com> Date: Sun, 18 Apr 2021 11:54:18 -0500 Subject: [PATCH 14/55] Vendored ext_sorter, removed unstable, created a byte buffer sized vector instead of a numbered capacity vector --- Cargo.lock | 1 + src/uu/sort/Cargo.toml | 1 + src/uu/sort/src/ext_sorter.rs | 347 +++++++++++++++++++++++++++++ src/uu/sort/src/numeric_str_cmp.rs | 2 +- src/uu/sort/src/sort.rs | 112 +++++----- 5 files changed, 409 insertions(+), 54 deletions(-) create mode 100644 src/uu/sort/src/ext_sorter.rs diff --git a/Cargo.lock b/Cargo.lock index b7328009c..76f43d8b4 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2424,6 +2424,7 @@ dependencies = [ "serde", "serde_json", "smallvec 1.6.1", + "tempfile", "uucore", "uucore_procs", ] diff --git a/src/uu/sort/Cargo.toml b/src/uu/sort/Cargo.toml index 8a3d1ed25..e1e0d1b87 100644 --- a/src/uu/sort/Cargo.toml +++ b/src/uu/sort/Cargo.toml @@ -28,6 +28,7 @@ semver = "0.9.0" smallvec = { version = "1.6.1", features = ["serde"] } uucore = { version=">=0.0.8", package="uucore", path="../../uucore", features=["fs"] } uucore_procs = { version=">=0.0.5", package="uucore_procs", path="../../uucore_procs" } +tempfile = "3.1.0" [[bin]] name = "sort" diff --git a/src/uu/sort/src/ext_sorter.rs b/src/uu/sort/src/ext_sorter.rs new file mode 100644 index 000000000..c19f1262b --- /dev/null +++ b/src/uu/sort/src/ext_sorter.rs @@ -0,0 +1,347 @@ +// Copyright 2018 Andre-Philippe Paquet +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use rayon::prelude::*; +use std::{ + cmp::Ordering, + collections::VecDeque, + fs::{File, OpenOptions}, + io::{BufReader, BufWriter, Error, Read, Seek, SeekFrom, Write}, + path::{Path, PathBuf}, +}; + +/// Exposes external sorting (i.e. on disk sorting) capability on arbitrarily +/// sized iterator, even if the generated content of the iterator doesn't fit in +/// memory. +/// +/// It uses an in-memory buffer sorted and flushed to disk in segment files when +/// full. Once sorted, it returns a new sorted iterator with all items. In order +/// to remain efficient for all implementations, the crate doesn't handle +/// serialization, but leaves that to the user. +pub struct ExternalSorter { + segment_size: usize, + sort_dir: Option, + parallel: bool, +} + +impl ExternalSorter { + pub fn new() -> ExternalSorter { + ExternalSorter { + segment_size: 10000000, + sort_dir: None, + parallel: false, + } + } + + /// Sets the maximum size of each segment in number of sorted items. + /// + /// This number of items needs to fit in memory. While sorting, a + /// in-memory buffer is used to collect the items to be sorted. Once + /// it reaches the maximum size, it is sorted and then written to disk. + /// + /// Using a higher segment size makes sorting faster by leveraging + /// faster in-memory operations. + pub fn with_segment_size(mut self, size: usize) -> Self { + self.segment_size = size; + self + } + + /// Sets directory in which sorted segments will be written (if it doesn't + /// fit in memory). + pub fn with_sort_dir(mut self, path: PathBuf) -> Self { + self.sort_dir = Some(path); + self + } + + /// Uses Rayon to sort the in-memory buffer. + /// + /// This may not be needed if the buffer isn't big enough for parallelism to + /// be gainful over the overhead of multithreading. + pub fn with_parallel_sort(mut self) -> Self { + self.parallel = true; + self + } + + /// Sorts a given iterator, returning a new iterator with items + pub fn sort( + &self, + iterator: I, + ) -> Result Ordering + Send + Sync>, Error> + where + T: Sortable + Ord, + I: Iterator, + { + self.sort_by(iterator, |a, b| a.cmp(b)) + } + + /// Sorts a given iterator with a comparator function, returning a new iterator with items + pub fn sort_by(&self, iterator: I, cmp: F) -> Result, Error> + where + T: Sortable, + I: Iterator, + F: Fn(&T, &T) -> Ordering + Send + Sync, + { + let mut tempdir: Option = None; + let mut sort_dir: Option = None; + + let mut count = 0; + let mut segments_file: Vec = Vec::new(); + let size_of_items = std::mem::size_of::(); + let mut buffer: Vec = Vec::with_capacity(self.segment_size / size_of_items); + for next_item in iterator { + count += 1; + buffer.push(next_item); + if buffer.len() > self.segment_size { + let sort_dir = self.lazy_create_dir(&mut tempdir, &mut sort_dir)?; + self.sort_and_write_segment(sort_dir, &mut segments_file, &mut buffer, &cmp)?; + } + } + + // Write any items left in buffer, but only if we had at least 1 segment + // written. Otherwise we use the buffer itself to iterate from memory + let pass_through_queue = if !buffer.is_empty() && !segments_file.is_empty() { + let sort_dir = self.lazy_create_dir(&mut tempdir, &mut sort_dir)?; + self.sort_and_write_segment(sort_dir, &mut segments_file, &mut buffer, &cmp)?; + None + } else { + buffer.sort_by(&cmp); + Some(VecDeque::from(buffer)) + }; + + SortedIterator::new(tempdir, pass_through_queue, segments_file, count, cmp) + } + + /// Sorts a given iterator with a key extraction function, returning a new iterator with items + pub fn sort_by_key( + &self, + iterator: I, + f: F, + ) -> Result Ordering + Send + Sync>, Error> + where + T: Sortable, + I: Iterator, + F: Fn(&T) -> K + Send + Sync, + K: Ord, + { + self.sort_by(iterator, move |a, b| f(a).cmp(&f(b))) + } + + /// We only want to create directory if it's needed (i.e. if the dataset + /// doesn't fit in memory) to prevent filesystem latency + fn lazy_create_dir<'a>( + &self, + tempdir: &mut Option, + sort_dir: &'a mut Option, + ) -> Result<&'a Path, Error> { + if let Some(sort_dir) = sort_dir { + return Ok(sort_dir); + } + + *sort_dir = if let Some(ref sort_dir) = self.sort_dir { + Some(sort_dir.to_path_buf()) + } else { + *tempdir = Some(tempfile::TempDir::new()?); + Some(tempdir.as_ref().unwrap().path().to_path_buf()) + }; + + Ok(sort_dir.as_ref().unwrap()) + } + + fn sort_and_write_segment( + &self, + sort_dir: &Path, + segments: &mut Vec, + buffer: &mut Vec, + cmp: F, + ) -> Result<(), Error> + where + T: Sortable, + F: Fn(&T, &T) -> Ordering + Send + Sync, + { + if self.parallel { + buffer.par_sort_by(|a, b| cmp(a, b)); + } else { + buffer.sort_by(|a, b| cmp(a, b)); + } + + let segment_path = sort_dir.join(format!("{}", segments.len())); + let segment_file = OpenOptions::new() + .create(true) + .truncate(true) + .read(true) + .write(true) + .open(&segment_path)?; + let mut buf_writer = BufWriter::new(segment_file); + + for item in buffer.drain(0..) { + item.encode(&mut buf_writer); + } + + let file = buf_writer.into_inner()?; + segments.push(file); + + Ok(()) + } +} + +impl Default for ExternalSorter { + fn default() -> Self { + ExternalSorter::new() + } +} + +pub trait Sortable: Sized + Send { + fn encode(&self, writer: &mut W); + fn decode(reader: &mut R) -> Option; +} + +pub struct SortedIterator { + _tempdir: Option, + pass_through_queue: Option>, + segments_file: Vec>, + next_values: Vec>, + count: u64, + cmp: F, +} + +impl Ordering + Send + Sync> SortedIterator { + fn new( + tempdir: Option, + pass_through_queue: Option>, + mut segments_file: Vec, + count: u64, + cmp: F, + ) -> Result, Error> { + for segment in &mut segments_file { + segment.seek(SeekFrom::Start(0))?; + } + + let next_values = segments_file + .iter_mut() + .map(|file| T::decode(file)) + .collect(); + + let segments_file_buffered = segments_file.into_iter().map(BufReader::new).collect(); + + Ok(SortedIterator { + _tempdir: tempdir, + pass_through_queue, + segments_file: segments_file_buffered, + next_values, + count, + cmp, + }) + } + + pub fn sorted_count(&self) -> u64 { + self.count + } +} + +impl Ordering> Iterator for SortedIterator { + type Item = T; + + fn next(&mut self) -> Option { + // if we have a pass through, we dequeue from it directly + if let Some(ptb) = self.pass_through_queue.as_mut() { + return ptb.pop_front(); + } + + // otherwise, we iter from segments on disk + let mut smallest_idx: Option = None; + { + let mut smallest: Option<&T> = None; + for idx in 0..self.segments_file.len() { + let next_value = self.next_values[idx].as_ref(); + if next_value.is_none() { + continue; + } + + if smallest.is_none() + || (self.cmp)(next_value.unwrap(), smallest.unwrap()) == Ordering::Less + { + smallest = Some(next_value.unwrap()); + smallest_idx = Some(idx); + } + } + } + + smallest_idx.map(|idx| { + let file = &mut self.segments_file[idx]; + let value = self.next_values[idx].take().unwrap(); + self.next_values[idx] = T::decode(file); + value + }) + } +} + +#[cfg(test)] +pub mod test { + use super::*; + + use byteorder::{ReadBytesExt, WriteBytesExt}; + + #[test] + fn test_smaller_than_segment() { + let sorter = ExternalSorter::new(); + let data: Vec = (0..100u32).collect(); + let data_rev: Vec = data.iter().rev().cloned().collect(); + + let sorted_iter = sorter.sort(data_rev.into_iter()).unwrap(); + + // should not have used any segments (all in memory) + assert_eq!(sorted_iter.segments_file.len(), 0); + let sorted_data: Vec = sorted_iter.collect(); + + assert_eq!(data, sorted_data); + } + + #[test] + fn test_multiple_segments() { + let sorter = ExternalSorter::new().with_segment_size(100); + let data: Vec = (0..1000u32).collect(); + + let data_rev: Vec = data.iter().rev().cloned().collect(); + let sorted_iter = sorter.sort(data_rev.into_iter()).unwrap(); + assert_eq!(sorted_iter.segments_file.len(), 10); + + let sorted_data: Vec = sorted_iter.collect(); + assert_eq!(data, sorted_data); + } + + #[test] + fn test_parallel() { + let sorter = ExternalSorter::new() + .with_segment_size(100) + .with_parallel_sort(); + let data: Vec = (0..1000u32).collect(); + + let data_rev: Vec = data.iter().rev().cloned().collect(); + let sorted_iter = sorter.sort(data_rev.into_iter()).unwrap(); + assert_eq!(sorted_iter.segments_file.len(), 10); + + let sorted_data: Vec = sorted_iter.collect(); + assert_eq!(data, sorted_data); + } + + impl Sortable for u32 { + fn encode(&self, writer: &mut W) { + writer.write_u32::(*self).unwrap(); + } + + fn decode(reader: &mut R) -> Option { + reader.read_u32::().ok() + } + } +} diff --git a/src/uu/sort/src/numeric_str_cmp.rs b/src/uu/sort/src/numeric_str_cmp.rs index ac615d1f7..b15eec988 100644 --- a/src/uu/sort/src/numeric_str_cmp.rs +++ b/src/uu/sort/src/numeric_str_cmp.rs @@ -14,8 +14,8 @@ //! More specifically, exponent can be understood so that the original number is in (1..10)*10^exponent. //! From that follows the constraints of this algorithm: It is able to compare numbers in ±(1*10^[i64::MIN]..10*10^[i64::MAX]). +use serde::{Deserialize, Serialize}; use std::{cmp::Ordering, ops::Range}; -use serde::{Serialize, Deserialize}; #[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize, Clone)] enum Sign { diff --git a/src/uu/sort/src/sort.rs b/src/uu/sort/src/sort.rs index 9052b2a5b..07a8879b7 100644 --- a/src/uu/sort/src/sort.rs +++ b/src/uu/sort/src/sort.rs @@ -16,6 +16,8 @@ extern crate uucore; mod numeric_str_cmp; +pub mod ext_sorter; +pub use ext_sorter::{ExternalSorter, Sortable, SortedIterator}; use clap::{App, Arg}; use fnv::FnvHasher; @@ -24,26 +26,20 @@ use numeric_str_cmp::{numeric_str_cmp, NumInfo, NumInfoParseSettings}; use rand::distributions::Alphanumeric; use rand::{thread_rng, Rng}; use semver::Version; +use serde::{Deserialize, Serialize}; use smallvec::SmallVec; use std::borrow::Cow; use std::cmp::Ordering; use std::collections::BinaryHeap; use std::env; +use std::ffi::OsString; use std::fs::File; use std::hash::{Hash, Hasher}; use std::io::{stdin, stdout, BufRead, BufReader, BufWriter, Lines, Read, Write}; use std::mem::replace; use std::ops::{Range, RangeInclusive}; -use std::path::Path; +use std::path::{Path, PathBuf}; use uucore::fs::is_stdin_interactive; // for Iterator::dedup() -use extsort::*; -use std::str; -use serde::{Serialize, Deserialize}; -use std::ffi::OsString; -use std::usize; -use std::path::PathBuf; -use std::string::*; -use rayon::prelude::*; static NAME: &str = "sort"; static ABOUT: &str = "Display sorted concatenation of all FILE(s)."; @@ -255,30 +251,39 @@ struct Line { impl Sortable for Line { fn encode(&self, write: &mut W) { - let line = Line {line: self.line.to_owned(), selections: self.selections.to_owned() }; + let line = Line { + line: self.line.to_owned(), + selections: self.selections.to_owned(), + }; let serialized = serde_json::ser::to_string(&line).unwrap(); // Each instance of valid JSON needs to be seperated by something, so here we use a newline - write.write_all(format!("{}{}", serialized, "\n").as_bytes()).unwrap(); + write + .write_all(format!("{}{}", serialized, "\n").as_bytes()) + .unwrap(); } // This crate asks us to write one Line at a time, but returns multiple Lines to us(?). - // However, this crate also expects us to return a result of Option, - // so we concat the these lines into a single Option. So, it's possible this is broken, + // However, this crate also expects us to return a result of Option, + // so we concat the these lines into a single Option. So, it's possible this is broken, // and/or needs to be tested more thoroughly. Perhaps we need to rethink our Line struct or rewrite a - // ext sorter ourselves. + // ext sorter ourselves. fn decode(read: &mut R) -> Option { let buf_reader = BufReader::new(read); let result = { - let mut line_joined= String::new(); - let mut selections_joined= SmallVec::new(); - for line in buf_reader.lines() { + let mut line_joined = String::new(); + let mut selections_joined = SmallVec::new(); + let p_iter = buf_reader.lines().peekable(); + for line in p_iter { let mut deserialized_line: Line = serde_json::de::from_str(&line.unwrap()).unwrap(); - line_joined = format!("{}\n{}", line_joined, deserialized_line.line); - // I think we've done our sorting already and these are irrelevant? @miDeb what's your sense? + line_joined = format!("{}\n{}\n", line_joined, deserialized_line.line); + // I think we've done our sorting already and these are irrelevant? + // @miDeb what's your sense? Could we just return an empty vec? selections_joined.append(&mut deserialized_line.selections); - selections_joined.dedup(); } - Some( Line {line: line_joined, selections: selections_joined} ) + Some(Line { + line: line_joined.strip_suffix("\n").unwrap().to_owned(), + selections: selections_joined, + }) }; result } @@ -798,6 +803,7 @@ pub fn uumain(args: impl uucore::Args) -> i32 { ) .arg( Arg::with_name(OPT_BUF_SIZE) + .short("S") .long(OPT_BUF_SIZE) .help("sets the maximum SIZE of each segment in number of sorted items") .takes_value(true) @@ -805,6 +811,7 @@ pub fn uumain(args: impl uucore::Args) -> i32 { ) .arg( Arg::with_name(OPT_TMP_DIR) + .short("T") .long(OPT_TMP_DIR) .help("use DIR for temporaries, not $TMPDIR or /tmp") .takes_value(true) @@ -875,13 +882,13 @@ pub fn uumain(args: impl uucore::Args) -> i32 { if matches.is_present(OPT_BUF_SIZE) { // 10K is the default extsort buffer, but that's too small, so we set at 10M - // Although the "default" is never used unless extsort options are given - settings.buffer_size = { + // Although the "default" is never used unless extsort options are given + settings.buffer_size = { let input = matches - .value_of(OPT_BUF_SIZE) - .map(String::from) - .unwrap_or( format! ( "{}", DEFAULT_BUF_SIZE ) ); - + .value_of(OPT_BUF_SIZE) + .map(String::from) + .unwrap_or(format!("{}", DEFAULT_BUF_SIZE)); + human_numeric_convert(&input) } } @@ -890,13 +897,16 @@ pub fn uumain(args: impl uucore::Args) -> i32 { let result = matches .value_of(OPT_TMP_DIR) .map(String::from) - .unwrap_or(DEFAULT_TMPDIR.to_owned() ); + .unwrap_or(DEFAULT_TMPDIR.to_owned()); settings.tmp_dir = PathBuf::from(format!(r"{}", result)); } else { for (key, value) in env::vars_os() { if key == OsString::from("TMPDIR") { - settings.tmp_dir = PathBuf::from(format!(r"{}", value.into_string().unwrap_or("/tmp".to_owned()))); - break + settings.tmp_dir = PathBuf::from(format!( + r"{}", + value.into_string().unwrap_or("/tmp".to_owned()) + )); + break; } settings.tmp_dir = PathBuf::from(DEFAULT_TMPDIR); } @@ -1019,12 +1029,9 @@ fn exec(files: Vec, settings: &GlobalSettings) -> i32 { if settings.check { return exec_check_file(&lines, &settings); } - - if ( settings.buffer_size != DEFAULT_BUF_SIZE ) || ( settings.tmp_dir.as_os_str() != DEFAULT_TMPDIR ) { - lines = ext_sort_by(lines, &settings); - } else { - sort_by(&mut lines, &settings); - } + + + lines = sort_by(lines, &settings); if settings.merge { if settings.unique { @@ -1079,20 +1086,18 @@ fn exec_check_file(unwrapped_lines: &[Line], settings: &GlobalSettings) -> i32 { } } -fn ext_sort_by(lines: Vec, settings: &GlobalSettings) -> Vec { - let sorter = ExternalSorter::new().with_segment_size(settings.buffer_size).with_sort_dir(settings.tmp_dir.clone()).with_parallel_sort(); - let result = sorter.sort_by(lines.into_iter(), |a, b| compare_by(a, b, &settings)).unwrap().collect(); +fn sort_by(lines: Vec, settings: &GlobalSettings) -> Vec { + let sorter = ExternalSorter::new() + .with_segment_size(settings.buffer_size) + .with_sort_dir(settings.tmp_dir.clone()) + .with_parallel_sort(); + let result = sorter + .sort_by(lines.into_iter(), |a, b| compare_by(a, b, &settings)) + .unwrap() + .collect(); result } -fn sort_by(lines: &mut Vec, settings: &GlobalSettings) { - if settings.stable || settings.unique { - lines.par_sort_by(|a, b| compare_by(a, b, &settings)) - } else { - lines.par_sort_unstable_by(|a, b| compare_by(a, b, &settings)) - } -} - fn compare_by(a: &Line, b: &Line, global_settings: &GlobalSettings) -> Ordering { for (idx, selector) in global_settings.selectors.iter().enumerate() { let a_selection = &a.selections[idx]; @@ -1137,14 +1142,14 @@ fn compare_by(a: &Line, b: &Line, global_settings: &GlobalSettings) -> Ordering } } -// Brought back! Probably want to do through numstrcmp somehow now +// It's back to do conversions for command options! Probably want to do through numstrcmp somehow now fn human_numeric_convert(a: &str) -> usize { let num_part = leading_num_common(a); let (_, s) = a.split_at(num_part.len()); let num_part = permissive_f64_parse(num_part); let suffix = match s.parse().unwrap_or('\0') { // SI Units - 'K' => 1E3, + 'K' | 'k' => 1E3, 'M' => 1E6, 'G' => 1E9, 'T' => 1E12, @@ -1164,7 +1169,7 @@ fn default_compare(a: &str, b: &str) -> Ordering { a.cmp(b) } -// This function does the initial detection of numeric lines. +/// This function does the initial detection of numeric lines for FP compares. // Lines starting with a number or positive or negative sign. // It also strips the string of any thing that could never // be a number for the purposes of any type of numeric comparison. @@ -1195,7 +1200,7 @@ fn leading_num_common(a: &str) -> &str { s } -// This function cleans up the initial comparison done by leading_num_common for a general numeric compare. +/// This function cleans up the initial comparison done by leading_num_common for a general numeric compare. // In contrast to numeric compare, GNU general numeric/FP sort *should* recognize positive signs and // scientific notation, so we strip those lines only after the end of the following numeric string. // For example, 5e10KFD would be 5e10 or 5x10^10 and +10000HFKJFK would become 10000. @@ -1318,7 +1323,7 @@ fn month_parse(line: &str) -> Month { "" }; - match pattern.to_uppercase().as_ref() { + let result = match pattern.to_uppercase().as_ref() { "JAN" => Month::January, "FEB" => Month::February, "MAR" => Month::March, @@ -1332,7 +1337,8 @@ fn month_parse(line: &str) -> Month { "NOV" => Month::November, "DEC" => Month::December, _ => Month::Unknown, - } + }; + result } fn month_compare(a: &str, b: &str) -> Ordering { From da94e350448239ca6ddd6442d76e4cd610dd6d98 Mon Sep 17 00:00:00 2001 From: electricboogie <32370782+electricboogie@users.noreply.github.com> Date: Sun, 18 Apr 2021 13:02:50 -0500 Subject: [PATCH 15/55] Cleanup, removed unused code, add copyright --- src/uu/sort/src/ext_sorter.rs | 94 +---------------------------------- src/uu/sort/src/sort.rs | 20 +++++--- 2 files changed, 14 insertions(+), 100 deletions(-) diff --git a/src/uu/sort/src/ext_sorter.rs b/src/uu/sort/src/ext_sorter.rs index c19f1262b..d607cbd3e 100644 --- a/src/uu/sort/src/ext_sorter.rs +++ b/src/uu/sort/src/ext_sorter.rs @@ -1,4 +1,5 @@ // Copyright 2018 Andre-Philippe Paquet +// Copyright 2021 Robert Swinford // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -38,7 +39,7 @@ pub struct ExternalSorter { impl ExternalSorter { pub fn new() -> ExternalSorter { ExternalSorter { - segment_size: 10000000, + segment_size: 16000000000, sort_dir: None, parallel: false, } @@ -73,18 +74,6 @@ impl ExternalSorter { self } - /// Sorts a given iterator, returning a new iterator with items - pub fn sort( - &self, - iterator: I, - ) -> Result Ordering + Send + Sync>, Error> - where - T: Sortable + Ord, - I: Iterator, - { - self.sort_by(iterator, |a, b| a.cmp(b)) - } - /// Sorts a given iterator with a comparator function, returning a new iterator with items pub fn sort_by(&self, iterator: I, cmp: F) -> Result, Error> where @@ -122,21 +111,6 @@ impl ExternalSorter { SortedIterator::new(tempdir, pass_through_queue, segments_file, count, cmp) } - /// Sorts a given iterator with a key extraction function, returning a new iterator with items - pub fn sort_by_key( - &self, - iterator: I, - f: F, - ) -> Result Ordering + Send + Sync>, Error> - where - T: Sortable, - I: Iterator, - F: Fn(&T) -> K + Send + Sync, - K: Ord, - { - self.sort_by(iterator, move |a, b| f(a).cmp(&f(b))) - } - /// We only want to create directory if it's needed (i.e. if the dataset /// doesn't fit in memory) to prevent filesystem latency fn lazy_create_dir<'a>( @@ -243,10 +217,6 @@ impl Ordering + Send + Sync> SortedIterator cmp, }) } - - pub fn sorted_count(&self) -> u64 { - self.count - } } impl Ordering> Iterator for SortedIterator { @@ -285,63 +255,3 @@ impl Ordering> Iterator for SortedIterator { }) } } - -#[cfg(test)] -pub mod test { - use super::*; - - use byteorder::{ReadBytesExt, WriteBytesExt}; - - #[test] - fn test_smaller_than_segment() { - let sorter = ExternalSorter::new(); - let data: Vec = (0..100u32).collect(); - let data_rev: Vec = data.iter().rev().cloned().collect(); - - let sorted_iter = sorter.sort(data_rev.into_iter()).unwrap(); - - // should not have used any segments (all in memory) - assert_eq!(sorted_iter.segments_file.len(), 0); - let sorted_data: Vec = sorted_iter.collect(); - - assert_eq!(data, sorted_data); - } - - #[test] - fn test_multiple_segments() { - let sorter = ExternalSorter::new().with_segment_size(100); - let data: Vec = (0..1000u32).collect(); - - let data_rev: Vec = data.iter().rev().cloned().collect(); - let sorted_iter = sorter.sort(data_rev.into_iter()).unwrap(); - assert_eq!(sorted_iter.segments_file.len(), 10); - - let sorted_data: Vec = sorted_iter.collect(); - assert_eq!(data, sorted_data); - } - - #[test] - fn test_parallel() { - let sorter = ExternalSorter::new() - .with_segment_size(100) - .with_parallel_sort(); - let data: Vec = (0..1000u32).collect(); - - let data_rev: Vec = data.iter().rev().cloned().collect(); - let sorted_iter = sorter.sort(data_rev.into_iter()).unwrap(); - assert_eq!(sorted_iter.segments_file.len(), 10); - - let sorted_data: Vec = sorted_iter.collect(); - assert_eq!(data, sorted_data); - } - - impl Sortable for u32 { - fn encode(&self, writer: &mut W) { - writer.write_u32::(*self).unwrap(); - } - - fn decode(reader: &mut R) -> Option { - reader.read_u32::().ok() - } - } -} diff --git a/src/uu/sort/src/sort.rs b/src/uu/sort/src/sort.rs index 07a8879b7..fab712978 100644 --- a/src/uu/sort/src/sort.rs +++ b/src/uu/sort/src/sort.rs @@ -15,14 +15,14 @@ #[macro_use] extern crate uucore; +mod ext_sorter; mod numeric_str_cmp; -pub mod ext_sorter; -pub use ext_sorter::{ExternalSorter, Sortable, SortedIterator}; use clap::{App, Arg}; use fnv::FnvHasher; use itertools::Itertools; use numeric_str_cmp::{numeric_str_cmp, NumInfo, NumInfoParseSettings}; +use ext_sorter::{ExternalSorter, Sortable}; use rand::distributions::Alphanumeric; use rand::{thread_rng, Rng}; use semver::Version; @@ -39,7 +39,7 @@ use std::io::{stdin, stdout, BufRead, BufReader, BufWriter, Lines, Read, Write}; use std::mem::replace; use std::ops::{Range, RangeInclusive}; use std::path::{Path, PathBuf}; -use uucore::fs::is_stdin_interactive; // for Iterator::dedup() +use uucore::fs::is_stdin_interactive; // for Iterator::dedup(); static NAME: &str = "sort"; static ABOUT: &str = "Display sorted concatenation of all FILE(s)."; @@ -90,7 +90,8 @@ static NEGATIVE: char = '-'; static POSITIVE: char = '+'; static DEFAULT_TMPDIR: &str = r"/tmp"; -static DEFAULT_BUF_SIZE: usize = 10000000usize; +// 16GB buffer for Vec before we dump to disk +static DEFAULT_BUF_SIZE: usize = 16000000000; #[derive(Eq, Ord, PartialEq, PartialOrd, Clone)] enum SortMode { @@ -281,7 +282,7 @@ impl Sortable for Line { selections_joined.append(&mut deserialized_line.selections); } Some(Line { - line: line_joined.strip_suffix("\n").unwrap().to_owned(), + line: line_joined.strip_suffix("\n").unwrap_or("").to_owned(), selections: selections_joined, }) }; @@ -881,7 +882,7 @@ pub fn uumain(args: impl uucore::Args) -> i32 { } if matches.is_present(OPT_BUF_SIZE) { - // 10K is the default extsort buffer, but that's too small, so we set at 10M + // 10K is the default extsort buffer, but that's too small, so we set at 100M // Although the "default" is never used unless extsort options are given settings.buffer_size = { let input = matches @@ -889,7 +890,11 @@ pub fn uumain(args: impl uucore::Args) -> i32 { .map(String::from) .unwrap_or(format!("{}", DEFAULT_BUF_SIZE)); - human_numeric_convert(&input) + if human_numeric_convert(&input) < 128000 { + panic!("sort will not operate with less than 128K of memory."); + } else { + human_numeric_convert(&input) + } } } @@ -1030,7 +1035,6 @@ fn exec(files: Vec, settings: &GlobalSettings) -> i32 { return exec_check_file(&lines, &settings); } - lines = sort_by(lines, &settings); if settings.merge { From dad7761be96e18bb66d3ff50642fab36fb242955 Mon Sep 17 00:00:00 2001 From: electricboogie <32370782+electricboogie@users.noreply.github.com> Date: Sun, 18 Apr 2021 13:43:41 -0500 Subject: [PATCH 16/55] Add test --- src/uu/sort/src/ext_sorter.rs | 72 +++++++++++++++++++++++++++++++++++ src/uu/sort/src/sort.rs | 6 +-- tests/by-util/test_sort.rs | 25 +++++------- 3 files changed, 83 insertions(+), 20 deletions(-) diff --git a/src/uu/sort/src/ext_sorter.rs b/src/uu/sort/src/ext_sorter.rs index d607cbd3e..00fe9b401 100644 --- a/src/uu/sort/src/ext_sorter.rs +++ b/src/uu/sort/src/ext_sorter.rs @@ -74,6 +74,18 @@ impl ExternalSorter { self } + /// Sorts a given iterator, returning a new iterator with items + pub fn sort( + &self, + iterator: I, + ) -> Result Ordering + Send + Sync>, Error> + where + T: Sortable + Ord, + I: Iterator, + { + self.sort_by(iterator, |a, b| a.cmp(b)) + } + /// Sorts a given iterator with a comparator function, returning a new iterator with items pub fn sort_by(&self, iterator: I, cmp: F) -> Result, Error> where @@ -255,3 +267,63 @@ impl Ordering> Iterator for SortedIterator { }) } } + +#[cfg(test)] +pub mod test { + use super::*; + + use byteorder::{ReadBytesExt, WriteBytesExt}; + + #[test] + fn test_smaller_than_segment() { + let sorter = ExternalSorter::new(); + let data: Vec = (0..100u32).collect(); + let data_rev: Vec = data.iter().rev().cloned().collect(); + + let sorted_iter = sorter.sort(data_rev.into_iter()).unwrap(); + + // should not have used any segments (all in memory) + assert_eq!(sorted_iter.segments_file.len(), 0); + let sorted_data: Vec = sorted_iter.collect(); + + assert_eq!(data, sorted_data); + } + + #[test] + fn test_multiple_segments() { + let sorter = ExternalSorter::new().with_segment_size(100); + let data: Vec = (0..1000u32).collect(); + + let data_rev: Vec = data.iter().rev().cloned().collect(); + let sorted_iter = sorter.sort(data_rev.into_iter()).unwrap(); + assert_eq!(sorted_iter.segments_file.len(), 10); + + let sorted_data: Vec = sorted_iter.collect(); + assert_eq!(data, sorted_data); + } + + #[test] + fn test_parallel() { + let sorter = ExternalSorter::new() + .with_segment_size(100) + .with_parallel_sort(); + let data: Vec = (0..1000u32).collect(); + + let data_rev: Vec = data.iter().rev().cloned().collect(); + let sorted_iter = sorter.sort(data_rev.into_iter()).unwrap(); + assert_eq!(sorted_iter.segments_file.len(), 10); + + let sorted_data: Vec = sorted_iter.collect(); + assert_eq!(data, sorted_data); + } + + impl Sortable for u32 { + fn encode(&self, writer: &mut W) { + writer.write_u32::(*self).unwrap(); + } + + fn decode(reader: &mut R) -> Option { + reader.read_u32::().ok() + } + } +} diff --git a/src/uu/sort/src/sort.rs b/src/uu/sort/src/sort.rs index fab712978..4854990e6 100644 --- a/src/uu/sort/src/sort.rs +++ b/src/uu/sort/src/sort.rs @@ -90,7 +90,7 @@ static NEGATIVE: char = '-'; static POSITIVE: char = '+'; static DEFAULT_TMPDIR: &str = r"/tmp"; -// 16GB buffer for Vec before we dump to disk +// 16GB buffer for Vec before we dump to disk static DEFAULT_BUF_SIZE: usize = 16000000000; #[derive(Eq, Ord, PartialEq, PartialOrd, Clone)] @@ -890,11 +890,7 @@ pub fn uumain(args: impl uucore::Args) -> i32 { .map(String::from) .unwrap_or(format!("{}", DEFAULT_BUF_SIZE)); - if human_numeric_convert(&input) < 128000 { - panic!("sort will not operate with less than 128K of memory."); - } else { human_numeric_convert(&input) - } } } diff --git a/tests/by-util/test_sort.rs b/tests/by-util/test_sort.rs index a4a9a383c..0ca917a86 100644 --- a/tests/by-util/test_sort.rs +++ b/tests/by-util/test_sort.rs @@ -8,6 +8,16 @@ fn test_helper(file_name: &str, args: &str) { .stdout_is_fixture(format!("{}.expected", file_name)); } +#[test] +fn test_larger_than_specified_segment() { + new_ucmd!() + .arg("-n") + .arg("-S 100") + .arg("numeric_unsorted_ints.txt") + .succeeds() + .stdout_is_fixture(format!("{}", "numeric_unsorted_ints.expected")); +} + #[test] fn test_months_whitespace() { test_helper("months-whitespace", "-M"); @@ -100,21 +110,6 @@ fn test_random_shuffle_two_runs_not_the_same() { assert_ne!(result, unexpected); } -#[test] -fn test_random_shuffle_contains_two_runs_not_the_same() { - // check to verify that two random shuffles are not equal; this has the - // potential to fail in the unlikely event that random order is the same - // as the starting order, or if both random sorts end up having the same order. - const FILE: &'static str = "default_unsorted_ints.expected"; - let (at, _ucmd) = at_and_ucmd!(); - let result = new_ucmd!().arg("-R").arg(FILE).run().stdout_move_str(); - let expected = at.read(FILE); - let unexpected = new_ucmd!().arg("-R").arg(FILE).run().stdout_move_str(); - - assert_ne!(result, expected); - assert_ne!(result, unexpected); -} - #[test] fn test_numeric_floats_and_ints() { test_helper("numeric_floats_and_ints", "-n"); From 42da444f40df869406e1d0138341b308a148001e Mon Sep 17 00:00:00 2001 From: electricboogie <32370782+electricboogie@users.noreply.github.com> Date: Sun, 18 Apr 2021 13:49:11 -0500 Subject: [PATCH 17/55] Remove unused deps --- Cargo.lock | 131 +---------------------------------------- src/uu/sort/Cargo.toml | 2 - 2 files changed, 3 insertions(+), 130 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 76f43d8b4..eb99af34b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -119,40 +119,12 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "560c32574a12a89ecd91f5e742165893f86e3ab98d21f8ea548658eb9eef5f40" -[[package]] -name = "bytecount" -version = "0.6.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72feb31ffc86498dacdbd0fcebb56138e7177a8cc5cea4516031d15ae85a742e" - [[package]] name = "byteorder" version = "1.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" -[[package]] -name = "cargo-platform" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0226944a63d1bf35a3b5f948dd7c59e263db83695c9e8bffc4037de02e30f1d7" -dependencies = [ - "serde", -] - -[[package]] -name = "cargo_metadata" -version = "0.12.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7714a157da7991e23d90686b9524b9e12e0407a108647f52e9328f4b3d51ac7f" -dependencies = [ - "cargo-platform", - "semver 0.11.0", - "semver-parser 0.10.2", - "serde", - "serde_json", -] - [[package]] name = "cast" version = "0.2.3" @@ -588,26 +560,6 @@ dependencies = [ "regex", ] -[[package]] -name = "error-chain" -version = "0.12.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2d2f06b9cac1506ece98fe3231e3cc9c4410ec3d5b1f24ae1c8946f0742cdefc" -dependencies = [ - "version_check", -] - -[[package]] -name = "extsort" -version = "0.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ffc5bb6fbca3c5ce6a51f6857eab8c35c898b2fbcb62ff1b728243dd19ec0c9f" -dependencies = [ - "rayon", - "skeptic", - "tempfile", -] - [[package]] name = "fake-simd" version = "0.1.2" @@ -992,15 +944,6 @@ dependencies = [ "proc-macro-hack", ] -[[package]] -name = "pest" -version = "2.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "10f4872ae94d7b90ae48754df22fd42ad52ce740b8f370b03da4835417403e53" -dependencies = [ - "ucd-trie", -] - [[package]] name = "pkg-config" version = "0.3.19" @@ -1066,17 +1009,6 @@ dependencies = [ "unicode-xid 0.2.1", ] -[[package]] -name = "pulldown-cmark" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ffade02495f22453cd593159ea2f59827aae7f53fa8323f756799b670881dcf8" -dependencies = [ - "bitflags", - "memchr 2.3.4", - "unicase", -] - [[package]] name = "quick-error" version = "1.2.3" @@ -1313,7 +1245,7 @@ version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a" dependencies = [ - "semver 0.9.0", + "semver", ] [[package]] @@ -1343,17 +1275,7 @@ version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403" dependencies = [ - "semver-parser 0.7.0", -] - -[[package]] -name = "semver" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f301af10236f6df4160f7c3f04eec6dbc70ace82d23326abad5edee88801c6b6" -dependencies = [ - "semver-parser 0.10.2", - "serde", + "semver-parser", ] [[package]] @@ -1362,15 +1284,6 @@ version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" -[[package]] -name = "semver-parser" -version = "0.10.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "00b0bef5b7f9e0df16536d3961cfb6e84331c065b4066afb39768d0e319411f7" -dependencies = [ - "pest", -] - [[package]] name = "serde" version = "1.0.125" @@ -1443,21 +1356,6 @@ dependencies = [ "generic-array", ] -[[package]] -name = "skeptic" -version = "0.13.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "188b810342d98f23f0bb875045299f34187b559370b041eb11520c905370a888" -dependencies = [ - "bytecount", - "cargo_metadata", - "error-chain", - "glob 0.3.0", - "pulldown-cmark", - "tempfile", - "walkdir", -] - [[package]] name = "smallvec" version = "0.6.14" @@ -1636,21 +1534,6 @@ version = "1.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "879f6906492a7cd215bfa4cf595b600146ccfac0c79bcbd1f3000162af5e8b06" -[[package]] -name = "ucd-trie" -version = "0.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "56dee185309b50d1f11bfedef0fe6d036842e3fb77413abef29f8f8d1c5d4c1c" - -[[package]] -name = "unicase" -version = "2.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "50f37be617794602aabbeee0be4f259dc1778fabe05e2d67ee8f79326d5cb4f6" -dependencies = [ - "version_check", -] - [[package]] name = "unicode-segmentation" version = "1.7.1" @@ -2413,14 +2296,12 @@ dependencies = [ name = "uu_sort" version = "0.0.6" dependencies = [ - "byteorder", "clap", - "extsort", "fnv", "itertools 0.10.0", "rand 0.7.3", "rayon", - "semver 0.9.0", + "semver", "serde", "serde_json", "smallvec 1.6.1", @@ -2733,12 +2614,6 @@ version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191" -[[package]] -name = "version_check" -version = "0.9.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5fecdca9a5291cc2b8dcf7dc02453fee791a280f3743cb0905f8822ae463b3fe" - [[package]] name = "void" version = "1.0.2" diff --git a/src/uu/sort/Cargo.toml b/src/uu/sort/Cargo.toml index e1e0d1b87..f29df6ab8 100644 --- a/src/uu/sort/Cargo.toml +++ b/src/uu/sort/Cargo.toml @@ -15,8 +15,6 @@ edition = "2018" path = "src/sort.rs" [dependencies] -byteorder = "1.4.3" -extsort = "0.4.2" serde_json = { version = "1.0.64", default-features = false, features = ["alloc"] } serde = { version = "1.0", features = ["derive"] } rayon = "1.5" From 0275a43c5bc414c52e40edbb1e2cd2d531255009 Mon Sep 17 00:00:00 2001 From: electricboogie <32370782+electricboogie@users.noreply.github.com> Date: Sun, 18 Apr 2021 14:05:27 -0500 Subject: [PATCH 18/55] Make modifications clearer per Apache license --- src/uu/sort/src/ext_sorter.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/uu/sort/src/ext_sorter.rs b/src/uu/sort/src/ext_sorter.rs index 00fe9b401..782e80429 100644 --- a/src/uu/sort/src/ext_sorter.rs +++ b/src/uu/sort/src/ext_sorter.rs @@ -1,5 +1,5 @@ // Copyright 2018 Andre-Philippe Paquet -// Copyright 2021 Robert Swinford +// Modifications copyright 2021 Robert Swinford // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -13,6 +13,8 @@ // See the License for the specific language governing permissions and // limitations under the License. +// This file has been modified for use in the uutils project. + use rayon::prelude::*; use std::{ cmp::Ordering, From e3e1ee30ebd378b8dfdf9502a271bcda71c80667 Mon Sep 17 00:00:00 2001 From: electricboogie <32370782+electricboogie@users.noreply.github.com> Date: Sun, 18 Apr 2021 14:37:16 -0500 Subject: [PATCH 19/55] Add additional notices --- src/uu/sort/src/APACHE_LICENSE_EXT_SORTER | 202 ++++++++++++++++++++++ src/uu/sort/src/NOTICE | 8 + src/uu/sort/src/ext_sorter.rs | 2 +- 3 files changed, 211 insertions(+), 1 deletion(-) create mode 100644 src/uu/sort/src/APACHE_LICENSE_EXT_SORTER create mode 100644 src/uu/sort/src/NOTICE diff --git a/src/uu/sort/src/APACHE_LICENSE_EXT_SORTER b/src/uu/sort/src/APACHE_LICENSE_EXT_SORTER new file mode 100644 index 000000000..7a4a3ea24 --- /dev/null +++ b/src/uu/sort/src/APACHE_LICENSE_EXT_SORTER @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. \ No newline at end of file diff --git a/src/uu/sort/src/NOTICE b/src/uu/sort/src/NOTICE new file mode 100644 index 000000000..3a20ec6e7 --- /dev/null +++ b/src/uu/sort/src/NOTICE @@ -0,0 +1,8 @@ +extsort +Copyright 2016 Andre-Philippe Paquet + +This project includes software developed by Andre-Philippe Paquet. + +The ext_sorter.rs file was copied and modified for use in the uutils' coreutils subproject, sort. + +Except as otherwise specified, all other contributions to sort are licensed according to the terms of the LICENSE file. \ No newline at end of file diff --git a/src/uu/sort/src/ext_sorter.rs b/src/uu/sort/src/ext_sorter.rs index 782e80429..026c6d8da 100644 --- a/src/uu/sort/src/ext_sorter.rs +++ b/src/uu/sort/src/ext_sorter.rs @@ -13,7 +13,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -// This file has been modified for use in the uutils project. +// This file has been modified for use in the uutils' coreutils subproject, sort. use rayon::prelude::*; use std::{ From 0151f30c4ed420962a77365044ae6a6151aaa51e Mon Sep 17 00:00:00 2001 From: electricboogie <32370782+electricboogie@users.noreply.github.com> Date: Sun, 18 Apr 2021 15:04:25 -0500 Subject: [PATCH 20/55] Change directory structure --- src/uu/sort/src/APACHE_LICENSE_EXT_SORTER | 202 ------------------ src/uu/sort/src/ext_sorter/LICENSE | 202 ++++++++++++++++++ src/uu/sort/src/{ => ext_sorter}/NOTICE | 0 .../src/{ext_sorter.rs => ext_sorter/mod.rs} | 62 +----- src/uu/sort/src/sort.rs | 4 +- 5 files changed, 205 insertions(+), 265 deletions(-) delete mode 100644 src/uu/sort/src/APACHE_LICENSE_EXT_SORTER create mode 100644 src/uu/sort/src/ext_sorter/LICENSE rename src/uu/sort/src/{ => ext_sorter}/NOTICE (100%) rename src/uu/sort/src/{ext_sorter.rs => ext_sorter/mod.rs} (82%) diff --git a/src/uu/sort/src/APACHE_LICENSE_EXT_SORTER b/src/uu/sort/src/APACHE_LICENSE_EXT_SORTER deleted file mode 100644 index 7a4a3ea24..000000000 --- a/src/uu/sort/src/APACHE_LICENSE_EXT_SORTER +++ /dev/null @@ -1,202 +0,0 @@ - - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright [yyyy] [name of copyright owner] - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. \ No newline at end of file diff --git a/src/uu/sort/src/ext_sorter/LICENSE b/src/uu/sort/src/ext_sorter/LICENSE new file mode 100644 index 000000000..fe647bd7f --- /dev/null +++ b/src/uu/sort/src/ext_sorter/LICENSE @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + +2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + +3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + +4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + +5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS + +APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + +Copyright [yyyy] [name of copyright owner] + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. \ No newline at end of file diff --git a/src/uu/sort/src/NOTICE b/src/uu/sort/src/ext_sorter/NOTICE similarity index 100% rename from src/uu/sort/src/NOTICE rename to src/uu/sort/src/ext_sorter/NOTICE diff --git a/src/uu/sort/src/ext_sorter.rs b/src/uu/sort/src/ext_sorter/mod.rs similarity index 82% rename from src/uu/sort/src/ext_sorter.rs rename to src/uu/sort/src/ext_sorter/mod.rs index 026c6d8da..c5d7e59e8 100644 --- a/src/uu/sort/src/ext_sorter.rs +++ b/src/uu/sort/src/ext_sorter/mod.rs @@ -12,7 +12,7 @@ // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. - +// // This file has been modified for use in the uutils' coreutils subproject, sort. use rayon::prelude::*; @@ -269,63 +269,3 @@ impl Ordering> Iterator for SortedIterator { }) } } - -#[cfg(test)] -pub mod test { - use super::*; - - use byteorder::{ReadBytesExt, WriteBytesExt}; - - #[test] - fn test_smaller_than_segment() { - let sorter = ExternalSorter::new(); - let data: Vec = (0..100u32).collect(); - let data_rev: Vec = data.iter().rev().cloned().collect(); - - let sorted_iter = sorter.sort(data_rev.into_iter()).unwrap(); - - // should not have used any segments (all in memory) - assert_eq!(sorted_iter.segments_file.len(), 0); - let sorted_data: Vec = sorted_iter.collect(); - - assert_eq!(data, sorted_data); - } - - #[test] - fn test_multiple_segments() { - let sorter = ExternalSorter::new().with_segment_size(100); - let data: Vec = (0..1000u32).collect(); - - let data_rev: Vec = data.iter().rev().cloned().collect(); - let sorted_iter = sorter.sort(data_rev.into_iter()).unwrap(); - assert_eq!(sorted_iter.segments_file.len(), 10); - - let sorted_data: Vec = sorted_iter.collect(); - assert_eq!(data, sorted_data); - } - - #[test] - fn test_parallel() { - let sorter = ExternalSorter::new() - .with_segment_size(100) - .with_parallel_sort(); - let data: Vec = (0..1000u32).collect(); - - let data_rev: Vec = data.iter().rev().cloned().collect(); - let sorted_iter = sorter.sort(data_rev.into_iter()).unwrap(); - assert_eq!(sorted_iter.segments_file.len(), 10); - - let sorted_data: Vec = sorted_iter.collect(); - assert_eq!(data, sorted_data); - } - - impl Sortable for u32 { - fn encode(&self, writer: &mut W) { - writer.write_u32::(*self).unwrap(); - } - - fn decode(reader: &mut R) -> Option { - reader.read_u32::().ok() - } - } -} diff --git a/src/uu/sort/src/sort.rs b/src/uu/sort/src/sort.rs index 4854990e6..7b19547ea 100644 --- a/src/uu/sort/src/sort.rs +++ b/src/uu/sort/src/sort.rs @@ -256,7 +256,7 @@ impl Sortable for Line { line: self.line.to_owned(), selections: self.selections.to_owned(), }; - let serialized = serde_json::ser::to_string(&line).unwrap(); + let serialized = serde_json::to_string(&line).unwrap(); // Each instance of valid JSON needs to be seperated by something, so here we use a newline write .write_all(format!("{}{}", serialized, "\n").as_bytes()) @@ -275,7 +275,7 @@ impl Sortable for Line { let mut selections_joined = SmallVec::new(); let p_iter = buf_reader.lines().peekable(); for line in p_iter { - let mut deserialized_line: Line = serde_json::de::from_str(&line.unwrap()).unwrap(); + let mut deserialized_line: Line = serde_json::from_str(&line.unwrap()).unwrap(); line_joined = format!("{}\n{}\n", line_joined, deserialized_line.line); // I think we've done our sorting already and these are irrelevant? // @miDeb what's your sense? Could we just return an empty vec? From 298e269531b8c1ba0f29e6d31d476c0a824c078e Mon Sep 17 00:00:00 2001 From: electricboogie <32370782+electricboogie@users.noreply.github.com> Date: Sun, 18 Apr 2021 15:08:42 -0500 Subject: [PATCH 21/55] Remove unsed code --- src/uu/sort/src/ext_sorter/mod.rs | 19 +------------------ 1 file changed, 1 insertion(+), 18 deletions(-) diff --git a/src/uu/sort/src/ext_sorter/mod.rs b/src/uu/sort/src/ext_sorter/mod.rs index c5d7e59e8..07ae3bb09 100644 --- a/src/uu/sort/src/ext_sorter/mod.rs +++ b/src/uu/sort/src/ext_sorter/mod.rs @@ -75,18 +75,6 @@ impl ExternalSorter { self.parallel = true; self } - - /// Sorts a given iterator, returning a new iterator with items - pub fn sort( - &self, - iterator: I, - ) -> Result Ordering + Send + Sync>, Error> - where - T: Sortable + Ord, - I: Iterator, - { - self.sort_by(iterator, |a, b| a.cmp(b)) - } /// Sorts a given iterator with a comparator function, returning a new iterator with items pub fn sort_by(&self, iterator: I, cmp: F) -> Result, Error> @@ -98,12 +86,10 @@ impl ExternalSorter { let mut tempdir: Option = None; let mut sort_dir: Option = None; - let mut count = 0; let mut segments_file: Vec = Vec::new(); let size_of_items = std::mem::size_of::(); let mut buffer: Vec = Vec::with_capacity(self.segment_size / size_of_items); for next_item in iterator { - count += 1; buffer.push(next_item); if buffer.len() > self.segment_size { let sort_dir = self.lazy_create_dir(&mut tempdir, &mut sort_dir)?; @@ -122,7 +108,7 @@ impl ExternalSorter { Some(VecDeque::from(buffer)) }; - SortedIterator::new(tempdir, pass_through_queue, segments_file, count, cmp) + SortedIterator::new(tempdir, pass_through_queue, segments_file, cmp) } /// We only want to create directory if it's needed (i.e. if the dataset @@ -199,7 +185,6 @@ pub struct SortedIterator { pass_through_queue: Option>, segments_file: Vec>, next_values: Vec>, - count: u64, cmp: F, } @@ -208,7 +193,6 @@ impl Ordering + Send + Sync> SortedIterator tempdir: Option, pass_through_queue: Option>, mut segments_file: Vec, - count: u64, cmp: F, ) -> Result, Error> { for segment in &mut segments_file { @@ -227,7 +211,6 @@ impl Ordering + Send + Sync> SortedIterator pass_through_queue, segments_file: segments_file_buffered, next_values, - count, cmp, }) } From 9170e7a5112f6ca437e564bfd6b2fbaac13ea6d6 Mon Sep 17 00:00:00 2001 From: electricboogie <32370782+electricboogie@users.noreply.github.com> Date: Sun, 18 Apr 2021 15:15:12 -0500 Subject: [PATCH 22/55] Modify NOTICE --- src/uu/sort/src/ext_sorter/NOTICE | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/uu/sort/src/ext_sorter/NOTICE b/src/uu/sort/src/ext_sorter/NOTICE index 3a20ec6e7..168d9d3b5 100644 --- a/src/uu/sort/src/ext_sorter/NOTICE +++ b/src/uu/sort/src/ext_sorter/NOTICE @@ -1,8 +1,8 @@ extsort Copyright 2016 Andre-Philippe Paquet -This project includes software developed by Andre-Philippe Paquet. +This ext_sorter module includes software developed by Andre-Philippe Paquet. -The ext_sorter.rs file was copied and modified for use in the uutils' coreutils subproject, sort. +The sorter.rs file was copied and modified for use in the uutils' coreutils subproject, sort. Except as otherwise specified, all other contributions to sort are licensed according to the terms of the LICENSE file. \ No newline at end of file From e841bb6a2414114669bd2d75a23260f54d2da505 Mon Sep 17 00:00:00 2001 From: electricboogie <32370782+electricboogie@users.noreply.github.com> Date: Sun, 18 Apr 2021 15:20:16 -0500 Subject: [PATCH 23/55] More license cleanup --- src/uu/sort/src/ext_sorter/{LICENSE => APACHE_LICENSE} | 0 src/uu/sort/src/ext_sorter/NOTICE | 4 ++-- 2 files changed, 2 insertions(+), 2 deletions(-) rename src/uu/sort/src/ext_sorter/{LICENSE => APACHE_LICENSE} (100%) diff --git a/src/uu/sort/src/ext_sorter/LICENSE b/src/uu/sort/src/ext_sorter/APACHE_LICENSE similarity index 100% rename from src/uu/sort/src/ext_sorter/LICENSE rename to src/uu/sort/src/ext_sorter/APACHE_LICENSE diff --git a/src/uu/sort/src/ext_sorter/NOTICE b/src/uu/sort/src/ext_sorter/NOTICE index 168d9d3b5..2964ac31d 100644 --- a/src/uu/sort/src/ext_sorter/NOTICE +++ b/src/uu/sort/src/ext_sorter/NOTICE @@ -1,8 +1,8 @@ extsort -Copyright 2016 Andre-Philippe Paquet +Copyright 2018 Andre-Philippe Paquet This ext_sorter module includes software developed by Andre-Philippe Paquet. The sorter.rs file was copied and modified for use in the uutils' coreutils subproject, sort. -Except as otherwise specified, all other contributions to sort are licensed according to the terms of the LICENSE file. \ No newline at end of file +Except as otherwise specified, all other contributions to sort are licensed according to the terms of the LICENSE file found in root directory of this project. \ No newline at end of file From fb19522ca05401ec95eed6f091c89f5fd0c94fbb Mon Sep 17 00:00:00 2001 From: electricboogie <32370782+electricboogie@users.noreply.github.com> Date: Sun, 18 Apr 2021 15:39:20 -0500 Subject: [PATCH 24/55] Bring back non-external sort as default --- src/uu/sort/src/sort.rs | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/src/uu/sort/src/sort.rs b/src/uu/sort/src/sort.rs index 7b19547ea..e04688e70 100644 --- a/src/uu/sort/src/sort.rs +++ b/src/uu/sort/src/sort.rs @@ -18,6 +18,7 @@ extern crate uucore; mod ext_sorter; mod numeric_str_cmp; +use rayon::prelude::*; use clap::{App, Arg}; use fnv::FnvHasher; use itertools::Itertools; @@ -1031,8 +1032,15 @@ fn exec(files: Vec, settings: &GlobalSettings) -> i32 { return exec_check_file(&lines, &settings); } - lines = sort_by(lines, &settings); - + // Only use ext_sorter when we need to. + // Probably faster that we don't create + // an owned value each run + if settings.buffer_size != DEFAULT_BUF_SIZE { + lines = ext_sort_by(lines, &settings); + } else { + sort_by(&mut lines, &settings); + } + if settings.merge { if settings.unique { print_sorted(file_merger.dedup(), &settings) @@ -1086,7 +1094,7 @@ fn exec_check_file(unwrapped_lines: &[Line], settings: &GlobalSettings) -> i32 { } } -fn sort_by(lines: Vec, settings: &GlobalSettings) -> Vec { +fn ext_sort_by(lines: Vec, settings: &GlobalSettings) -> Vec { let sorter = ExternalSorter::new() .with_segment_size(settings.buffer_size) .with_sort_dir(settings.tmp_dir.clone()) @@ -1098,6 +1106,10 @@ fn sort_by(lines: Vec, settings: &GlobalSettings) -> Vec { result } +fn sort_by(lines: &mut Vec, settings: &GlobalSettings) { + lines.par_sort_by(|a, b| compare_by(a, b, &settings)) +} + fn compare_by(a: &Line, b: &Line, global_settings: &GlobalSettings) -> Ordering { for (idx, selector) in global_settings.selectors.iter().enumerate() { let a_selection = &a.selections[idx]; From 559f4e81f607749c4af505c97211459c78854287 Mon Sep 17 00:00:00 2001 From: electricboogie <32370782+electricboogie@users.noreply.github.com> Date: Sun, 18 Apr 2021 15:47:05 -0500 Subject: [PATCH 25/55] More license cleanup --- src/uu/sort/src/ext_sorter/NOTICE | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/uu/sort/src/ext_sorter/NOTICE b/src/uu/sort/src/ext_sorter/NOTICE index 2964ac31d..d7c2199d1 100644 --- a/src/uu/sort/src/ext_sorter/NOTICE +++ b/src/uu/sort/src/ext_sorter/NOTICE @@ -1,8 +1,9 @@ -extsort +ext_sorter Copyright 2018 Andre-Philippe Paquet +Copyright 2021 Robert Swinford -This ext_sorter module includes software developed by Andre-Philippe Paquet. +This ext_sorter module includes software developed by Andre-Philippe Paquet as extsort. The sorter.rs file was copied and modified for use in the uutils' coreutils subproject, sort. -Except as otherwise specified, all other contributions to sort are licensed according to the terms of the LICENSE file found in root directory of this project. \ No newline at end of file +sort is licensed according to the term of the LICENSE file found in root directory of the uutils' coreutils project. \ No newline at end of file From deb94cef7aca462e0c1dc405ff1a13ec0fc23b0c Mon Sep 17 00:00:00 2001 From: electricboogie <32370782+electricboogie@users.noreply.github.com> Date: Sun, 18 Apr 2021 15:52:48 -0500 Subject: [PATCH 26/55] Cleanup --- src/uu/sort/src/sort.rs | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/src/uu/sort/src/sort.rs b/src/uu/sort/src/sort.rs index e04688e70..1df3b1bc9 100644 --- a/src/uu/sort/src/sort.rs +++ b/src/uu/sort/src/sort.rs @@ -264,11 +264,8 @@ impl Sortable for Line { .unwrap(); } - // This crate asks us to write one Line at a time, but returns multiple Lines to us(?). - // However, this crate also expects us to return a result of Option, - // so we concat the these lines into a single Option. So, it's possible this is broken, - // and/or needs to be tested more thoroughly. Perhaps we need to rethink our Line struct or rewrite a - // ext sorter ourselves. + // This crate asks us to write one Line struct at a time, but then returns multiple Lines to us at once. + // We concatanate them and return them as one big Line here. fn decode(read: &mut R) -> Option { let buf_reader = BufReader::new(read); let result = { @@ -278,7 +275,7 @@ impl Sortable for Line { for line in p_iter { let mut deserialized_line: Line = serde_json::from_str(&line.unwrap()).unwrap(); line_joined = format!("{}\n{}\n", line_joined, deserialized_line.line); - // I think we've done our sorting already and these are irrelevant? + // I think we've done our sorting already and these selctions are irrelevant? // @miDeb what's your sense? Could we just return an empty vec? selections_joined.append(&mut deserialized_line.selections); } From 8072e2092af919eab71cfffe144851ff943edf2c Mon Sep 17 00:00:00 2001 From: electricboogie <32370782+electricboogie@users.noreply.github.com> Date: Sun, 18 Apr 2021 16:33:18 -0500 Subject: [PATCH 27/55] Cleanup loop, run rustfmt --- src/uu/sort/src/ext_sorter/mod.rs | 2 +- src/uu/sort/src/sort.rs | 25 +++++++++++++++---------- 2 files changed, 16 insertions(+), 11 deletions(-) diff --git a/src/uu/sort/src/ext_sorter/mod.rs b/src/uu/sort/src/ext_sorter/mod.rs index 07ae3bb09..92b88637d 100644 --- a/src/uu/sort/src/ext_sorter/mod.rs +++ b/src/uu/sort/src/ext_sorter/mod.rs @@ -75,7 +75,7 @@ impl ExternalSorter { self.parallel = true; self } - + /// Sorts a given iterator with a comparator function, returning a new iterator with items pub fn sort_by(&self, iterator: I, cmp: F) -> Result, Error> where diff --git a/src/uu/sort/src/sort.rs b/src/uu/sort/src/sort.rs index 1df3b1bc9..ea41ce24f 100644 --- a/src/uu/sort/src/sort.rs +++ b/src/uu/sort/src/sort.rs @@ -18,14 +18,14 @@ extern crate uucore; mod ext_sorter; mod numeric_str_cmp; -use rayon::prelude::*; use clap::{App, Arg}; +use ext_sorter::{ExternalSorter, Sortable}; use fnv::FnvHasher; use itertools::Itertools; use numeric_str_cmp::{numeric_str_cmp, NumInfo, NumInfoParseSettings}; -use ext_sorter::{ExternalSorter, Sortable}; use rand::distributions::Alphanumeric; use rand::{thread_rng, Rng}; +use rayon::prelude::*; use semver::Version; use serde::{Deserialize, Serialize}; use smallvec::SmallVec; @@ -271,16 +271,21 @@ impl Sortable for Line { let result = { let mut line_joined = String::new(); let mut selections_joined = SmallVec::new(); - let p_iter = buf_reader.lines().peekable(); - for line in p_iter { - let mut deserialized_line: Line = serde_json::from_str(&line.unwrap()).unwrap(); - line_joined = format!("{}\n{}\n", line_joined, deserialized_line.line); + let mut p_iter = buf_reader.lines().peekable(); + while let Some(line) = p_iter.next() { + let mut deserialized_line: Line = + serde_json::from_str(&line.as_ref().unwrap()).unwrap(); + if let Some(_next_line) = p_iter.peek() { + line_joined = format!("{}\n{}\n", line_joined, deserialized_line.line) + } else { + line_joined = format!("{}\n{}", line_joined, deserialized_line.line) + } // I think we've done our sorting already and these selctions are irrelevant? // @miDeb what's your sense? Could we just return an empty vec? selections_joined.append(&mut deserialized_line.selections); } Some(Line { - line: line_joined.strip_suffix("\n").unwrap_or("").to_owned(), + line: line_joined, selections: selections_joined, }) }; @@ -888,7 +893,7 @@ pub fn uumain(args: impl uucore::Args) -> i32 { .map(String::from) .unwrap_or(format!("{}", DEFAULT_BUF_SIZE)); - human_numeric_convert(&input) + human_numeric_convert(&input) } } @@ -1030,14 +1035,14 @@ fn exec(files: Vec, settings: &GlobalSettings) -> i32 { } // Only use ext_sorter when we need to. - // Probably faster that we don't create + // Probably faster that we don't create // an owned value each run if settings.buffer_size != DEFAULT_BUF_SIZE { lines = ext_sort_by(lines, &settings); } else { sort_by(&mut lines, &settings); } - + if settings.merge { if settings.unique { print_sorted(file_merger.dedup(), &settings) From 258325491f341db66341e8133f72fc586b281af4 Mon Sep 17 00:00:00 2001 From: electricboogie <32370782+electricboogie@users.noreply.github.com> Date: Sun, 18 Apr 2021 17:39:42 -0500 Subject: [PATCH 28/55] Make human_numeric_convert a method --- src/uu/sort/src/sort.rs | 45 ++++++++++++++++++++++------------------- 1 file changed, 24 insertions(+), 21 deletions(-) diff --git a/src/uu/sort/src/sort.rs b/src/uu/sort/src/sort.rs index ea41ce24f..7da1c8cd7 100644 --- a/src/uu/sort/src/sort.rs +++ b/src/uu/sort/src/sort.rs @@ -127,6 +127,29 @@ struct GlobalSettings { tmp_dir: PathBuf, } +impl GlobalSettings { + // It's back to do conversions for command line opts! + // Probably want to do through numstrcmp somehow now? + fn human_numeric_convert(a: &str) -> usize { + let num_part = leading_num_common(a); + let (_, s) = a.split_at(num_part.len()); + let num_part = permissive_f64_parse(num_part); + let suffix = match s.parse().unwrap_or('\0') { + // SI Units + 'K' | 'k' => 1E3, + 'M' => 1E6, + 'G' => 1E9, + 'T' => 1E12, + 'P' => 1E15, + 'E' => 1E18, + 'Z' => 1E21, + 'Y' => 1E24, + _ => 1f64, + }; + num_part as usize * suffix as usize + } +} + impl Default for GlobalSettings { fn default() -> GlobalSettings { GlobalSettings { @@ -893,7 +916,7 @@ pub fn uumain(args: impl uucore::Args) -> i32 { .map(String::from) .unwrap_or(format!("{}", DEFAULT_BUF_SIZE)); - human_numeric_convert(&input) + GlobalSettings::human_numeric_convert(&input) } } @@ -1156,26 +1179,6 @@ fn compare_by(a: &Line, b: &Line, global_settings: &GlobalSettings) -> Ordering } } -// It's back to do conversions for command options! Probably want to do through numstrcmp somehow now -fn human_numeric_convert(a: &str) -> usize { - let num_part = leading_num_common(a); - let (_, s) = a.split_at(num_part.len()); - let num_part = permissive_f64_parse(num_part); - let suffix = match s.parse().unwrap_or('\0') { - // SI Units - 'K' | 'k' => 1E3, - 'M' => 1E6, - 'G' => 1E9, - 'T' => 1E12, - 'P' => 1E15, - 'E' => 1E18, - 'Z' => 1E21, - 'Y' => 1E24, - _ => 1f64, - }; - num_part as usize * suffix as usize -} - // Test output against BSDs and GNU with their locale // env var set to lc_ctype=utf-8 to enjoy the exact same output. #[inline(always)] From 72858dda423cd9df812425c53f1aa5ac29bdf951 Mon Sep 17 00:00:00 2001 From: electricboogie <32370782+electricboogie@users.noreply.github.com> Date: Sun, 18 Apr 2021 17:40:59 -0500 Subject: [PATCH 29/55] Ran rustfmt --- src/uu/sort/src/sort.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/uu/sort/src/sort.rs b/src/uu/sort/src/sort.rs index 7da1c8cd7..4938450f4 100644 --- a/src/uu/sort/src/sort.rs +++ b/src/uu/sort/src/sort.rs @@ -127,8 +127,8 @@ struct GlobalSettings { tmp_dir: PathBuf, } -impl GlobalSettings { - // It's back to do conversions for command line opts! +impl GlobalSettings { + // It's back to do conversions for command line opts! // Probably want to do through numstrcmp somehow now? fn human_numeric_convert(a: &str) -> usize { let num_part = leading_num_common(a); From 5efd67b5e2d969cb8a8e8d17e2cb4da216a1d016 Mon Sep 17 00:00:00 2001 From: electricboogie <32370782+electricboogie@users.noreply.github.com> Date: Sun, 18 Apr 2021 17:44:45 -0500 Subject: [PATCH 30/55] License cleanup --- src/uu/sort/src/ext_sorter/NOTICE | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/uu/sort/src/ext_sorter/NOTICE b/src/uu/sort/src/ext_sorter/NOTICE index d7c2199d1..fdfc6f04f 100644 --- a/src/uu/sort/src/ext_sorter/NOTICE +++ b/src/uu/sort/src/ext_sorter/NOTICE @@ -1,6 +1,6 @@ ext_sorter Copyright 2018 Andre-Philippe Paquet -Copyright 2021 Robert Swinford +Modifications copyright 2021 Robert Swinford This ext_sorter module includes software developed by Andre-Philippe Paquet as extsort. From fcebdbb7a737750b70b7bfd8883bee5b2acaaa04 Mon Sep 17 00:00:00 2001 From: electricboogie <32370782+electricboogie@users.noreply.github.com> Date: Sun, 18 Apr 2021 17:51:44 -0500 Subject: [PATCH 31/55] Cleanup comment --- src/uu/sort/src/sort.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/uu/sort/src/sort.rs b/src/uu/sort/src/sort.rs index 4938450f4..3b13e5bbb 100644 --- a/src/uu/sort/src/sort.rs +++ b/src/uu/sort/src/sort.rs @@ -908,7 +908,7 @@ pub fn uumain(args: impl uucore::Args) -> i32 { } if matches.is_present(OPT_BUF_SIZE) { - // 10K is the default extsort buffer, but that's too small, so we set at 100M + // 16G is the default in memory buffer. // Although the "default" is never used unless extsort options are given settings.buffer_size = { let input = matches From e7bcd5955815461873e9a4ed304afe796e42b6ab Mon Sep 17 00:00:00 2001 From: electricboogie <32370782+electricboogie@users.noreply.github.com> Date: Sun, 18 Apr 2021 18:22:30 -0500 Subject: [PATCH 32/55] Remove a clone --- src/uu/sort/src/sort.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/uu/sort/src/sort.rs b/src/uu/sort/src/sort.rs index 3b13e5bbb..d5a2ccbf4 100644 --- a/src/uu/sort/src/sort.rs +++ b/src/uu/sort/src/sort.rs @@ -1227,7 +1227,7 @@ fn get_leading_gen(a: &str) -> &str { let mut p_iter = raw_leading_num.chars().peekable(); let mut result = ""; // Cleanup raw stripped strings - for c in p_iter.to_owned() { + while let Some(c) = p_iter.next() { let next_char_numeric = p_iter.peek().unwrap_or(&'\0').is_numeric(); // Only general numeric recognizes e notation and, see block below, the '+' sign // Only GNU (non-general) numeric recognize thousands seperators, takes only leading # From b8d667c38393e17baf7fd10ef61eeeb717fb2cec Mon Sep 17 00:00:00 2001 From: electricboogie <32370782+electricboogie@users.noreply.github.com> Date: Mon, 19 Apr 2021 10:57:53 -0500 Subject: [PATCH 33/55] Clippy lints, more work on ext_sorter leads to 2 failing tests --- .../ext_sorter/{APACHE_LICENSE => LICENSE} | 0 src/uu/sort/src/ext_sorter/mod.rs | 28 ++++++++++++++++--- src/uu/sort/src/sort.rs | 14 ++++------ 3 files changed, 29 insertions(+), 13 deletions(-) rename src/uu/sort/src/ext_sorter/{APACHE_LICENSE => LICENSE} (100%) diff --git a/src/uu/sort/src/ext_sorter/APACHE_LICENSE b/src/uu/sort/src/ext_sorter/LICENSE similarity index 100% rename from src/uu/sort/src/ext_sorter/APACHE_LICENSE rename to src/uu/sort/src/ext_sorter/LICENSE diff --git a/src/uu/sort/src/ext_sorter/mod.rs b/src/uu/sort/src/ext_sorter/mod.rs index 92b88637d..eef7befe4 100644 --- a/src/uu/sort/src/ext_sorter/mod.rs +++ b/src/uu/sort/src/ext_sorter/mod.rs @@ -86,14 +86,24 @@ impl ExternalSorter { let mut tempdir: Option = None; let mut sort_dir: Option = None; + let mut count = 0; let mut segments_file: Vec = Vec::new(); + // FYI, the initialization size of struct Line is 96 bytes, but below works for all let size_of_items = std::mem::size_of::(); - let mut buffer: Vec = Vec::with_capacity(self.segment_size / size_of_items); + let initial_capacity = + if self.segment_size / size_of_items >= 2 { + self.segment_size / size_of_items + } else { 2 }; + let mut buffer: Vec = Vec::with_capacity(initial_capacity); for next_item in iterator { + count += 1; buffer.push(next_item); - if buffer.len() > self.segment_size { + // if after push, number of elements in vector > initial capacity + if buffer.len() > initial_capacity { let sort_dir = self.lazy_create_dir(&mut tempdir, &mut sort_dir)?; self.sort_and_write_segment(sort_dir, &mut segments_file, &mut buffer, &cmp)?; + // Resize buffer after write out + // buffer.shrink_to_fit(); } } @@ -108,7 +118,7 @@ impl ExternalSorter { Some(VecDeque::from(buffer)) }; - SortedIterator::new(tempdir, pass_through_queue, segments_file, cmp) + SortedIterator::new(tempdir, pass_through_queue, segments_file, count, cmp) } /// We only want to create directory if it's needed (i.e. if the dataset @@ -158,7 +168,10 @@ impl ExternalSorter { .open(&segment_path)?; let mut buf_writer = BufWriter::new(segment_file); - for item in buffer.drain(0..) { + // Possible panic here. + // Why use drain here, if we want to dump the entire buffer? + // Was "buffer.drain(0..)" + for item in buffer { item.encode(&mut buf_writer); } @@ -185,6 +198,7 @@ pub struct SortedIterator { pass_through_queue: Option>, segments_file: Vec>, next_values: Vec>, + count: u64, cmp: F, } @@ -193,6 +207,7 @@ impl Ordering + Send + Sync> SortedIterator tempdir: Option, pass_through_queue: Option>, mut segments_file: Vec, + count: u64, cmp: F, ) -> Result, Error> { for segment in &mut segments_file { @@ -211,9 +226,14 @@ impl Ordering + Send + Sync> SortedIterator pass_through_queue, segments_file: segments_file_buffered, next_values, + count, cmp, }) } + + pub fn sorted_count(&self) -> u64 { + self.count + } } impl Ordering> Iterator for SortedIterator { diff --git a/src/uu/sort/src/sort.rs b/src/uu/sort/src/sort.rs index d5a2ccbf4..2bbc02e78 100644 --- a/src/uu/sort/src/sort.rs +++ b/src/uu/sort/src/sort.rs @@ -924,15 +924,12 @@ pub fn uumain(args: impl uucore::Args) -> i32 { let result = matches .value_of(OPT_TMP_DIR) .map(String::from) - .unwrap_or(DEFAULT_TMPDIR.to_owned()); - settings.tmp_dir = PathBuf::from(format!(r"{}", result)); + .unwrap_or_else(|| DEFAULT_TMPDIR.to_owned()); + settings.tmp_dir = PathBuf::from(result); } else { for (key, value) in env::vars_os() { if key == OsString::from("TMPDIR") { - settings.tmp_dir = PathBuf::from(format!( - r"{}", - value.into_string().unwrap_or("/tmp".to_owned()) - )); + settings.tmp_dir = PathBuf::from(value); break; } settings.tmp_dir = PathBuf::from(DEFAULT_TMPDIR); @@ -1124,11 +1121,10 @@ fn ext_sort_by(lines: Vec, settings: &GlobalSettings) -> Vec { .with_segment_size(settings.buffer_size) .with_sort_dir(settings.tmp_dir.clone()) .with_parallel_sort(); - let result = sorter + sorter .sort_by(lines.into_iter(), |a, b| compare_by(a, b, &settings)) .unwrap() - .collect(); - result + .collect() } fn sort_by(lines: &mut Vec, settings: &GlobalSettings) { From 25021f31ebd927e61ff2d2815b4d3cf169ce1c00 Mon Sep 17 00:00:00 2001 From: electricboogie <32370782+electricboogie@users.noreply.github.com> Date: Mon, 19 Apr 2021 21:24:52 -0500 Subject: [PATCH 34/55] Incorporate overhead of Line struct --- src/uu/sort/src/ext_sorter/mod.rs | 17 +- src/uu/sort/src/sort.rs | 11 +- tests/by-util/test_sort.rs | 31 +- tests/fixtures/sort/ext_sort.expected | 20000 ++++++++++++++++++++++++ tests/fixtures/sort/ext_sort.txt | 20000 ++++++++++++++++++++++++ 5 files changed, 40033 insertions(+), 26 deletions(-) create mode 100644 tests/fixtures/sort/ext_sort.expected create mode 100644 tests/fixtures/sort/ext_sort.txt diff --git a/src/uu/sort/src/ext_sorter/mod.rs b/src/uu/sort/src/ext_sorter/mod.rs index eef7befe4..a90be6bb0 100644 --- a/src/uu/sort/src/ext_sorter/mod.rs +++ b/src/uu/sort/src/ext_sorter/mod.rs @@ -41,6 +41,8 @@ pub struct ExternalSorter { impl ExternalSorter { pub fn new() -> ExternalSorter { ExternalSorter { + // Default is 16G - But we never use it, + // because we always set or ignore segment_size: 16000000000, sort_dir: None, parallel: false, @@ -88,13 +90,14 @@ impl ExternalSorter { let mut count = 0; let mut segments_file: Vec = Vec::new(); - // FYI, the initialization size of struct Line is 96 bytes, but below works for all + let size_of_items = std::mem::size_of::(); - let initial_capacity = - if self.segment_size / size_of_items >= 2 { - self.segment_size / size_of_items - } else { 2 }; + // Get size of iterator + let (_, upper_bound) = iterator.size_hint(); + // Buffer size specified + minimum overhead of struct / size of items + let initial_capacity = (self.segment_size + (upper_bound.unwrap() * size_of_items)) / size_of_items; let mut buffer: Vec = Vec::with_capacity(initial_capacity); + for next_item in iterator { count += 1; buffer.push(next_item); @@ -102,8 +105,8 @@ impl ExternalSorter { if buffer.len() > initial_capacity { let sort_dir = self.lazy_create_dir(&mut tempdir, &mut sort_dir)?; self.sort_and_write_segment(sort_dir, &mut segments_file, &mut buffer, &cmp)?; - // Resize buffer after write out - // buffer.shrink_to_fit(); + // Truncate buffer back to initial capacity + buffer.truncate(initial_capacity); } } diff --git a/src/uu/sort/src/sort.rs b/src/uu/sort/src/sort.rs index 2bbc02e78..571541fc6 100644 --- a/src/uu/sort/src/sort.rs +++ b/src/uu/sort/src/sort.rs @@ -293,10 +293,11 @@ impl Sortable for Line { let buf_reader = BufReader::new(read); let result = { let mut line_joined = String::new(); - let mut selections_joined = SmallVec::new(); + // Return an empty vec for selections + let selections_joined = SmallVec::new(); let mut p_iter = buf_reader.lines().peekable(); while let Some(line) = p_iter.next() { - let mut deserialized_line: Line = + let deserialized_line: Line = serde_json::from_str(&line.as_ref().unwrap()).unwrap(); if let Some(_next_line) = p_iter.peek() { line_joined = format!("{}\n{}\n", line_joined, deserialized_line.line) @@ -305,7 +306,7 @@ impl Sortable for Line { } // I think we've done our sorting already and these selctions are irrelevant? // @miDeb what's your sense? Could we just return an empty vec? - selections_joined.append(&mut deserialized_line.selections); + //selections_joined.append(&mut deserialized_line.selections); } Some(Line { line: line_joined, @@ -909,13 +910,13 @@ pub fn uumain(args: impl uucore::Args) -> i32 { if matches.is_present(OPT_BUF_SIZE) { // 16G is the default in memory buffer. - // Although the "default" is never used unless extsort options are given + // Although the "default" is never used settings.buffer_size = { let input = matches .value_of(OPT_BUF_SIZE) .map(String::from) .unwrap_or(format!("{}", DEFAULT_BUF_SIZE)); - + GlobalSettings::human_numeric_convert(&input) } } diff --git a/tests/by-util/test_sort.rs b/tests/by-util/test_sort.rs index 0ca917a86..c76ab219a 100644 --- a/tests/by-util/test_sort.rs +++ b/tests/by-util/test_sort.rs @@ -8,14 +8,28 @@ fn test_helper(file_name: &str, args: &str) { .stdout_is_fixture(format!("{}.expected", file_name)); } +// FYI, the initialization size of our Line struct is 96 bytes. +// +// At very small buffer sizes, with that overhead we are certainly going +// to overrun our buffer way, way, way too quickly because of these excess +// bytes for the struct. +// +// For instance, seq 0..20000 > ...text = 108894 bytes +// But overhead is 1920000 + 108894 = 2028894 bytes +// +// Or kjvbible-random.txt = 4332506 bytes, but minimum size of its +// 99817 lines in memory * 96 bytes = 9582432 bytes +// +// Here, we test 108894 bytes with a 50K buffer +// #[test] fn test_larger_than_specified_segment() { new_ucmd!() .arg("-n") - .arg("-S 100") - .arg("numeric_unsorted_ints.txt") + .arg("-S 50K") + .arg("ext_sort.txt") .succeeds() - .stdout_is_fixture(format!("{}", "numeric_unsorted_ints.expected")); + .stdout_is_fixture(format!("{}", "ext_sort.expected")); } #[test] @@ -202,17 +216,6 @@ fn test_non_printing_chars() { } } -#[test] -fn test_exponents_positive_general_fixed() { - for exponents_positive_general_param in vec!["-g"] { - new_ucmd!() - .pipe_in("100E6\n\n50e10\n+100000\n\n10000K78\n10E\n\n\n1000EDKLD\n\n\n100E6\n\n50e10\n+100000\n\n") - .arg(exponents_positive_general_param) - .succeeds() - .stdout_only("\n\n\n\n\n\n\n\n10000K78\n1000EDKLD\n10E\n+100000\n+100000\n100E6\n100E6\n50e10\n50e10\n"); - } -} - #[test] fn test_exponents_positive_numeric() { test_helper("exponents-positive-numeric", "-n"); diff --git a/tests/fixtures/sort/ext_sort.expected b/tests/fixtures/sort/ext_sort.expected new file mode 100644 index 000000000..7599e0c96 --- /dev/null +++ b/tests/fixtures/sort/ext_sort.expected @@ -0,0 +1,20000 @@ +1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 +45 +46 +47 +48 +49 +50 +51 +52 +53 +54 +55 +56 +57 +58 +59 +60 +61 +62 +63 +64 +65 +66 +67 +68 +69 +70 +71 +72 +73 +74 +75 +76 +77 +78 +79 +80 +81 +82 +83 +84 +85 +86 +87 +88 +89 +90 +91 +92 +93 +94 +95 +96 +97 +98 +99 +100 +101 +102 +103 +104 +105 +106 +107 +108 +109 +110 +111 +112 +113 +114 +115 +116 +117 +118 +119 +120 +121 +122 +123 +124 +125 +126 +127 +128 +129 +130 +131 +132 +133 +134 +135 +136 +137 +138 +139 +140 +141 +142 +143 +144 +145 +146 +147 +148 +149 +150 +151 +152 +153 +154 +155 +156 +157 +158 +159 +160 +161 +162 +163 +164 +165 +166 +167 +168 +169 +170 +171 +172 +173 +174 +175 +176 +177 +178 +179 +180 +181 +182 +183 +184 +185 +186 +187 +188 +189 +190 +191 +192 +193 +194 +195 +196 +197 +198 +199 +200 +201 +202 +203 +204 +205 +206 +207 +208 +209 +210 +211 +212 +213 +214 +215 +216 +217 +218 +219 +220 +221 +222 +223 +224 +225 +226 +227 +228 +229 +230 +231 +232 +233 +234 +235 +236 +237 +238 +239 +240 +241 +242 +243 +244 +245 +246 +247 +248 +249 +250 +251 +252 +253 +254 +255 +256 +257 +258 +259 +260 +261 +262 +263 +264 +265 +266 +267 +268 +269 +270 +271 +272 +273 +274 +275 +276 +277 +278 +279 +280 +281 +282 +283 +284 +285 +286 +287 +288 +289 +290 +291 +292 +293 +294 +295 +296 +297 +298 +299 +300 +301 +302 +303 +304 +305 +306 +307 +308 +309 +310 +311 +312 +313 +314 +315 +316 +317 +318 +319 +320 +321 +322 +323 +324 +325 +326 +327 +328 +329 +330 +331 +332 +333 +334 +335 +336 +337 +338 +339 +340 +341 +342 +343 +344 +345 +346 +347 +348 +349 +350 +351 +352 +353 +354 +355 +356 +357 +358 +359 +360 +361 +362 +363 +364 +365 +366 +367 +368 +369 +370 +371 +372 +373 +374 +375 +376 +377 +378 +379 +380 +381 +382 +383 +384 +385 +386 +387 +388 +389 +390 +391 +392 +393 +394 +395 +396 +397 +398 +399 +400 +401 +402 +403 +404 +405 +406 +407 +408 +409 +410 +411 +412 +413 +414 +415 +416 +417 +418 +419 +420 +421 +422 +423 +424 +425 +426 +427 +428 +429 +430 +431 +432 +433 +434 +435 +436 +437 +438 +439 +440 +441 +442 +443 +444 +445 +446 +447 +448 +449 +450 +451 +452 +453 +454 +455 +456 +457 +458 +459 +460 +461 +462 +463 +464 +465 +466 +467 +468 +469 +470 +471 +472 +473 +474 +475 +476 +477 +478 +479 +480 +481 +482 +483 +484 +485 +486 +487 +488 +489 +490 +491 +492 +493 +494 +495 +496 +497 +498 +499 +500 +501 +502 +503 +504 +505 +506 +507 +508 +509 +510 +511 +512 +513 +514 +515 +516 +517 +518 +519 +520 +521 +522 +523 +524 +525 +526 +527 +528 +529 +530 +531 +532 +533 +534 +535 +536 +537 +538 +539 +540 +541 +542 +543 +544 +545 +546 +547 +548 +549 +550 +551 +552 +553 +554 +555 +556 +557 +558 +559 +560 +561 +562 +563 +564 +565 +566 +567 +568 +569 +570 +571 +572 +573 +574 +575 +576 +577 +578 +579 +580 +581 +582 +583 +584 +585 +586 +587 +588 +589 +590 +591 +592 +593 +594 +595 +596 +597 +598 +599 +600 +601 +602 +603 +604 +605 +606 +607 +608 +609 +610 +611 +612 +613 +614 +615 +616 +617 +618 +619 +620 +621 +622 +623 +624 +625 +626 +627 +628 +629 +630 +631 +632 +633 +634 +635 +636 +637 +638 +639 +640 +641 +642 +643 +644 +645 +646 +647 +648 +649 +650 +651 +652 +653 +654 +655 +656 +657 +658 +659 +660 +661 +662 +663 +664 +665 +666 +667 +668 +669 +670 +671 +672 +673 +674 +675 +676 +677 +678 +679 +680 +681 +682 +683 +684 +685 +686 +687 +688 +689 +690 +691 +692 +693 +694 +695 +696 +697 +698 +699 +700 +701 +702 +703 +704 +705 +706 +707 +708 +709 +710 +711 +712 +713 +714 +715 +716 +717 +718 +719 +720 +721 +722 +723 +724 +725 +726 +727 +728 +729 +730 +731 +732 +733 +734 +735 +736 +737 +738 +739 +740 +741 +742 +743 +744 +745 +746 +747 +748 +749 +750 +751 +752 +753 +754 +755 +756 +757 +758 +759 +760 +761 +762 +763 +764 +765 +766 +767 +768 +769 +770 +771 +772 +773 +774 +775 +776 +777 +778 +779 +780 +781 +782 +783 +784 +785 +786 +787 +788 +789 +790 +791 +792 +793 +794 +795 +796 +797 +798 +799 +800 +801 +802 +803 +804 +805 +806 +807 +808 +809 +810 +811 +812 +813 +814 +815 +816 +817 +818 +819 +820 +821 +822 +823 +824 +825 +826 +827 +828 +829 +830 +831 +832 +833 +834 +835 +836 +837 +838 +839 +840 +841 +842 +843 +844 +845 +846 +847 +848 +849 +850 +851 +852 +853 +854 +855 +856 +857 +858 +859 +860 +861 +862 +863 +864 +865 +866 +867 +868 +869 +870 +871 +872 +873 +874 +875 +876 +877 +878 +879 +880 +881 +882 +883 +884 +885 +886 +887 +888 +889 +890 +891 +892 +893 +894 +895 +896 +897 +898 +899 +900 +901 +902 +903 +904 +905 +906 +907 +908 +909 +910 +911 +912 +913 +914 +915 +916 +917 +918 +919 +920 +921 +922 +923 +924 +925 +926 +927 +928 +929 +930 +931 +932 +933 +934 +935 +936 +937 +938 +939 +940 +941 +942 +943 +944 +945 +946 +947 +948 +949 +950 +951 +952 +953 +954 +955 +956 +957 +958 +959 +960 +961 +962 +963 +964 +965 +966 +967 +968 +969 +970 +971 +972 +973 +974 +975 +976 +977 +978 +979 +980 +981 +982 +983 +984 +985 +986 +987 +988 +989 +990 +991 +992 +993 +994 +995 +996 +997 +998 +999 +1000 +1001 +1002 +1003 +1004 +1005 +1006 +1007 +1008 +1009 +1010 +1011 +1012 +1013 +1014 +1015 +1016 +1017 +1018 +1019 +1020 +1021 +1022 +1023 +1024 +1025 +1026 +1027 +1028 +1029 +1030 +1031 +1032 +1033 +1034 +1035 +1036 +1037 +1038 +1039 +1040 +1041 +1042 +1043 +1044 +1045 +1046 +1047 +1048 +1049 +1050 +1051 +1052 +1053 +1054 +1055 +1056 +1057 +1058 +1059 +1060 +1061 +1062 +1063 +1064 +1065 +1066 +1067 +1068 +1069 +1070 +1071 +1072 +1073 +1074 +1075 +1076 +1077 +1078 +1079 +1080 +1081 +1082 +1083 +1084 +1085 +1086 +1087 +1088 +1089 +1090 +1091 +1092 +1093 +1094 +1095 +1096 +1097 +1098 +1099 +1100 +1101 +1102 +1103 +1104 +1105 +1106 +1107 +1108 +1109 +1110 +1111 +1112 +1113 +1114 +1115 +1116 +1117 +1118 +1119 +1120 +1121 +1122 +1123 +1124 +1125 +1126 +1127 +1128 +1129 +1130 +1131 +1132 +1133 +1134 +1135 +1136 +1137 +1138 +1139 +1140 +1141 +1142 +1143 +1144 +1145 +1146 +1147 +1148 +1149 +1150 +1151 +1152 +1153 +1154 +1155 +1156 +1157 +1158 +1159 +1160 +1161 +1162 +1163 +1164 +1165 +1166 +1167 +1168 +1169 +1170 +1171 +1172 +1173 +1174 +1175 +1176 +1177 +1178 +1179 +1180 +1181 +1182 +1183 +1184 +1185 +1186 +1187 +1188 +1189 +1190 +1191 +1192 +1193 +1194 +1195 +1196 +1197 +1198 +1199 +1200 +1201 +1202 +1203 +1204 +1205 +1206 +1207 +1208 +1209 +1210 +1211 +1212 +1213 +1214 +1215 +1216 +1217 +1218 +1219 +1220 +1221 +1222 +1223 +1224 +1225 +1226 +1227 +1228 +1229 +1230 +1231 +1232 +1233 +1234 +1235 +1236 +1237 +1238 +1239 +1240 +1241 +1242 +1243 +1244 +1245 +1246 +1247 +1248 +1249 +1250 +1251 +1252 +1253 +1254 +1255 +1256 +1257 +1258 +1259 +1260 +1261 +1262 +1263 +1264 +1265 +1266 +1267 +1268 +1269 +1270 +1271 +1272 +1273 +1274 +1275 +1276 +1277 +1278 +1279 +1280 +1281 +1282 +1283 +1284 +1285 +1286 +1287 +1288 +1289 +1290 +1291 +1292 +1293 +1294 +1295 +1296 +1297 +1298 +1299 +1300 +1301 +1302 +1303 +1304 +1305 +1306 +1307 +1308 +1309 +1310 +1311 +1312 +1313 +1314 +1315 +1316 +1317 +1318 +1319 +1320 +1321 +1322 +1323 +1324 +1325 +1326 +1327 +1328 +1329 +1330 +1331 +1332 +1333 +1334 +1335 +1336 +1337 +1338 +1339 +1340 +1341 +1342 +1343 +1344 +1345 +1346 +1347 +1348 +1349 +1350 +1351 +1352 +1353 +1354 +1355 +1356 +1357 +1358 +1359 +1360 +1361 +1362 +1363 +1364 +1365 +1366 +1367 +1368 +1369 +1370 +1371 +1372 +1373 +1374 +1375 +1376 +1377 +1378 +1379 +1380 +1381 +1382 +1383 +1384 +1385 +1386 +1387 +1388 +1389 +1390 +1391 +1392 +1393 +1394 +1395 +1396 +1397 +1398 +1399 +1400 +1401 +1402 +1403 +1404 +1405 +1406 +1407 +1408 +1409 +1410 +1411 +1412 +1413 +1414 +1415 +1416 +1417 +1418 +1419 +1420 +1421 +1422 +1423 +1424 +1425 +1426 +1427 +1428 +1429 +1430 +1431 +1432 +1433 +1434 +1435 +1436 +1437 +1438 +1439 +1440 +1441 +1442 +1443 +1444 +1445 +1446 +1447 +1448 +1449 +1450 +1451 +1452 +1453 +1454 +1455 +1456 +1457 +1458 +1459 +1460 +1461 +1462 +1463 +1464 +1465 +1466 +1467 +1468 +1469 +1470 +1471 +1472 +1473 +1474 +1475 +1476 +1477 +1478 +1479 +1480 +1481 +1482 +1483 +1484 +1485 +1486 +1487 +1488 +1489 +1490 +1491 +1492 +1493 +1494 +1495 +1496 +1497 +1498 +1499 +1500 +1501 +1502 +1503 +1504 +1505 +1506 +1507 +1508 +1509 +1510 +1511 +1512 +1513 +1514 +1515 +1516 +1517 +1518 +1519 +1520 +1521 +1522 +1523 +1524 +1525 +1526 +1527 +1528 +1529 +1530 +1531 +1532 +1533 +1534 +1535 +1536 +1537 +1538 +1539 +1540 +1541 +1542 +1543 +1544 +1545 +1546 +1547 +1548 +1549 +1550 +1551 +1552 +1553 +1554 +1555 +1556 +1557 +1558 +1559 +1560 +1561 +1562 +1563 +1564 +1565 +1566 +1567 +1568 +1569 +1570 +1571 +1572 +1573 +1574 +1575 +1576 +1577 +1578 +1579 +1580 +1581 +1582 +1583 +1584 +1585 +1586 +1587 +1588 +1589 +1590 +1591 +1592 +1593 +1594 +1595 +1596 +1597 +1598 +1599 +1600 +1601 +1602 +1603 +1604 +1605 +1606 +1607 +1608 +1609 +1610 +1611 +1612 +1613 +1614 +1615 +1616 +1617 +1618 +1619 +1620 +1621 +1622 +1623 +1624 +1625 +1626 +1627 +1628 +1629 +1630 +1631 +1632 +1633 +1634 +1635 +1636 +1637 +1638 +1639 +1640 +1641 +1642 +1643 +1644 +1645 +1646 +1647 +1648 +1649 +1650 +1651 +1652 +1653 +1654 +1655 +1656 +1657 +1658 +1659 +1660 +1661 +1662 +1663 +1664 +1665 +1666 +1667 +1668 +1669 +1670 +1671 +1672 +1673 +1674 +1675 +1676 +1677 +1678 +1679 +1680 +1681 +1682 +1683 +1684 +1685 +1686 +1687 +1688 +1689 +1690 +1691 +1692 +1693 +1694 +1695 +1696 +1697 +1698 +1699 +1700 +1701 +1702 +1703 +1704 +1705 +1706 +1707 +1708 +1709 +1710 +1711 +1712 +1713 +1714 +1715 +1716 +1717 +1718 +1719 +1720 +1721 +1722 +1723 +1724 +1725 +1726 +1727 +1728 +1729 +1730 +1731 +1732 +1733 +1734 +1735 +1736 +1737 +1738 +1739 +1740 +1741 +1742 +1743 +1744 +1745 +1746 +1747 +1748 +1749 +1750 +1751 +1752 +1753 +1754 +1755 +1756 +1757 +1758 +1759 +1760 +1761 +1762 +1763 +1764 +1765 +1766 +1767 +1768 +1769 +1770 +1771 +1772 +1773 +1774 +1775 +1776 +1777 +1778 +1779 +1780 +1781 +1782 +1783 +1784 +1785 +1786 +1787 +1788 +1789 +1790 +1791 +1792 +1793 +1794 +1795 +1796 +1797 +1798 +1799 +1800 +1801 +1802 +1803 +1804 +1805 +1806 +1807 +1808 +1809 +1810 +1811 +1812 +1813 +1814 +1815 +1816 +1817 +1818 +1819 +1820 +1821 +1822 +1823 +1824 +1825 +1826 +1827 +1828 +1829 +1830 +1831 +1832 +1833 +1834 +1835 +1836 +1837 +1838 +1839 +1840 +1841 +1842 +1843 +1844 +1845 +1846 +1847 +1848 +1849 +1850 +1851 +1852 +1853 +1854 +1855 +1856 +1857 +1858 +1859 +1860 +1861 +1862 +1863 +1864 +1865 +1866 +1867 +1868 +1869 +1870 +1871 +1872 +1873 +1874 +1875 +1876 +1877 +1878 +1879 +1880 +1881 +1882 +1883 +1884 +1885 +1886 +1887 +1888 +1889 +1890 +1891 +1892 +1893 +1894 +1895 +1896 +1897 +1898 +1899 +1900 +1901 +1902 +1903 +1904 +1905 +1906 +1907 +1908 +1909 +1910 +1911 +1912 +1913 +1914 +1915 +1916 +1917 +1918 +1919 +1920 +1921 +1922 +1923 +1924 +1925 +1926 +1927 +1928 +1929 +1930 +1931 +1932 +1933 +1934 +1935 +1936 +1937 +1938 +1939 +1940 +1941 +1942 +1943 +1944 +1945 +1946 +1947 +1948 +1949 +1950 +1951 +1952 +1953 +1954 +1955 +1956 +1957 +1958 +1959 +1960 +1961 +1962 +1963 +1964 +1965 +1966 +1967 +1968 +1969 +1970 +1971 +1972 +1973 +1974 +1975 +1976 +1977 +1978 +1979 +1980 +1981 +1982 +1983 +1984 +1985 +1986 +1987 +1988 +1989 +1990 +1991 +1992 +1993 +1994 +1995 +1996 +1997 +1998 +1999 +2000 +2001 +2002 +2003 +2004 +2005 +2006 +2007 +2008 +2009 +2010 +2011 +2012 +2013 +2014 +2015 +2016 +2017 +2018 +2019 +2020 +2021 +2022 +2023 +2024 +2025 +2026 +2027 +2028 +2029 +2030 +2031 +2032 +2033 +2034 +2035 +2036 +2037 +2038 +2039 +2040 +2041 +2042 +2043 +2044 +2045 +2046 +2047 +2048 +2049 +2050 +2051 +2052 +2053 +2054 +2055 +2056 +2057 +2058 +2059 +2060 +2061 +2062 +2063 +2064 +2065 +2066 +2067 +2068 +2069 +2070 +2071 +2072 +2073 +2074 +2075 +2076 +2077 +2078 +2079 +2080 +2081 +2082 +2083 +2084 +2085 +2086 +2087 +2088 +2089 +2090 +2091 +2092 +2093 +2094 +2095 +2096 +2097 +2098 +2099 +2100 +2101 +2102 +2103 +2104 +2105 +2106 +2107 +2108 +2109 +2110 +2111 +2112 +2113 +2114 +2115 +2116 +2117 +2118 +2119 +2120 +2121 +2122 +2123 +2124 +2125 +2126 +2127 +2128 +2129 +2130 +2131 +2132 +2133 +2134 +2135 +2136 +2137 +2138 +2139 +2140 +2141 +2142 +2143 +2144 +2145 +2146 +2147 +2148 +2149 +2150 +2151 +2152 +2153 +2154 +2155 +2156 +2157 +2158 +2159 +2160 +2161 +2162 +2163 +2164 +2165 +2166 +2167 +2168 +2169 +2170 +2171 +2172 +2173 +2174 +2175 +2176 +2177 +2178 +2179 +2180 +2181 +2182 +2183 +2184 +2185 +2186 +2187 +2188 +2189 +2190 +2191 +2192 +2193 +2194 +2195 +2196 +2197 +2198 +2199 +2200 +2201 +2202 +2203 +2204 +2205 +2206 +2207 +2208 +2209 +2210 +2211 +2212 +2213 +2214 +2215 +2216 +2217 +2218 +2219 +2220 +2221 +2222 +2223 +2224 +2225 +2226 +2227 +2228 +2229 +2230 +2231 +2232 +2233 +2234 +2235 +2236 +2237 +2238 +2239 +2240 +2241 +2242 +2243 +2244 +2245 +2246 +2247 +2248 +2249 +2250 +2251 +2252 +2253 +2254 +2255 +2256 +2257 +2258 +2259 +2260 +2261 +2262 +2263 +2264 +2265 +2266 +2267 +2268 +2269 +2270 +2271 +2272 +2273 +2274 +2275 +2276 +2277 +2278 +2279 +2280 +2281 +2282 +2283 +2284 +2285 +2286 +2287 +2288 +2289 +2290 +2291 +2292 +2293 +2294 +2295 +2296 +2297 +2298 +2299 +2300 +2301 +2302 +2303 +2304 +2305 +2306 +2307 +2308 +2309 +2310 +2311 +2312 +2313 +2314 +2315 +2316 +2317 +2318 +2319 +2320 +2321 +2322 +2323 +2324 +2325 +2326 +2327 +2328 +2329 +2330 +2331 +2332 +2333 +2334 +2335 +2336 +2337 +2338 +2339 +2340 +2341 +2342 +2343 +2344 +2345 +2346 +2347 +2348 +2349 +2350 +2351 +2352 +2353 +2354 +2355 +2356 +2357 +2358 +2359 +2360 +2361 +2362 +2363 +2364 +2365 +2366 +2367 +2368 +2369 +2370 +2371 +2372 +2373 +2374 +2375 +2376 +2377 +2378 +2379 +2380 +2381 +2382 +2383 +2384 +2385 +2386 +2387 +2388 +2389 +2390 +2391 +2392 +2393 +2394 +2395 +2396 +2397 +2398 +2399 +2400 +2401 +2402 +2403 +2404 +2405 +2406 +2407 +2408 +2409 +2410 +2411 +2412 +2413 +2414 +2415 +2416 +2417 +2418 +2419 +2420 +2421 +2422 +2423 +2424 +2425 +2426 +2427 +2428 +2429 +2430 +2431 +2432 +2433 +2434 +2435 +2436 +2437 +2438 +2439 +2440 +2441 +2442 +2443 +2444 +2445 +2446 +2447 +2448 +2449 +2450 +2451 +2452 +2453 +2454 +2455 +2456 +2457 +2458 +2459 +2460 +2461 +2462 +2463 +2464 +2465 +2466 +2467 +2468 +2469 +2470 +2471 +2472 +2473 +2474 +2475 +2476 +2477 +2478 +2479 +2480 +2481 +2482 +2483 +2484 +2485 +2486 +2487 +2488 +2489 +2490 +2491 +2492 +2493 +2494 +2495 +2496 +2497 +2498 +2499 +2500 +2501 +2502 +2503 +2504 +2505 +2506 +2507 +2508 +2509 +2510 +2511 +2512 +2513 +2514 +2515 +2516 +2517 +2518 +2519 +2520 +2521 +2522 +2523 +2524 +2525 +2526 +2527 +2528 +2529 +2530 +2531 +2532 +2533 +2534 +2535 +2536 +2537 +2538 +2539 +2540 +2541 +2542 +2543 +2544 +2545 +2546 +2547 +2548 +2549 +2550 +2551 +2552 +2553 +2554 +2555 +2556 +2557 +2558 +2559 +2560 +2561 +2562 +2563 +2564 +2565 +2566 +2567 +2568 +2569 +2570 +2571 +2572 +2573 +2574 +2575 +2576 +2577 +2578 +2579 +2580 +2581 +2582 +2583 +2584 +2585 +2586 +2587 +2588 +2589 +2590 +2591 +2592 +2593 +2594 +2595 +2596 +2597 +2598 +2599 +2600 +2601 +2602 +2603 +2604 +2605 +2606 +2607 +2608 +2609 +2610 +2611 +2612 +2613 +2614 +2615 +2616 +2617 +2618 +2619 +2620 +2621 +2622 +2623 +2624 +2625 +2626 +2627 +2628 +2629 +2630 +2631 +2632 +2633 +2634 +2635 +2636 +2637 +2638 +2639 +2640 +2641 +2642 +2643 +2644 +2645 +2646 +2647 +2648 +2649 +2650 +2651 +2652 +2653 +2654 +2655 +2656 +2657 +2658 +2659 +2660 +2661 +2662 +2663 +2664 +2665 +2666 +2667 +2668 +2669 +2670 +2671 +2672 +2673 +2674 +2675 +2676 +2677 +2678 +2679 +2680 +2681 +2682 +2683 +2684 +2685 +2686 +2687 +2688 +2689 +2690 +2691 +2692 +2693 +2694 +2695 +2696 +2697 +2698 +2699 +2700 +2701 +2702 +2703 +2704 +2705 +2706 +2707 +2708 +2709 +2710 +2711 +2712 +2713 +2714 +2715 +2716 +2717 +2718 +2719 +2720 +2721 +2722 +2723 +2724 +2725 +2726 +2727 +2728 +2729 +2730 +2731 +2732 +2733 +2734 +2735 +2736 +2737 +2738 +2739 +2740 +2741 +2742 +2743 +2744 +2745 +2746 +2747 +2748 +2749 +2750 +2751 +2752 +2753 +2754 +2755 +2756 +2757 +2758 +2759 +2760 +2761 +2762 +2763 +2764 +2765 +2766 +2767 +2768 +2769 +2770 +2771 +2772 +2773 +2774 +2775 +2776 +2777 +2778 +2779 +2780 +2781 +2782 +2783 +2784 +2785 +2786 +2787 +2788 +2789 +2790 +2791 +2792 +2793 +2794 +2795 +2796 +2797 +2798 +2799 +2800 +2801 +2802 +2803 +2804 +2805 +2806 +2807 +2808 +2809 +2810 +2811 +2812 +2813 +2814 +2815 +2816 +2817 +2818 +2819 +2820 +2821 +2822 +2823 +2824 +2825 +2826 +2827 +2828 +2829 +2830 +2831 +2832 +2833 +2834 +2835 +2836 +2837 +2838 +2839 +2840 +2841 +2842 +2843 +2844 +2845 +2846 +2847 +2848 +2849 +2850 +2851 +2852 +2853 +2854 +2855 +2856 +2857 +2858 +2859 +2860 +2861 +2862 +2863 +2864 +2865 +2866 +2867 +2868 +2869 +2870 +2871 +2872 +2873 +2874 +2875 +2876 +2877 +2878 +2879 +2880 +2881 +2882 +2883 +2884 +2885 +2886 +2887 +2888 +2889 +2890 +2891 +2892 +2893 +2894 +2895 +2896 +2897 +2898 +2899 +2900 +2901 +2902 +2903 +2904 +2905 +2906 +2907 +2908 +2909 +2910 +2911 +2912 +2913 +2914 +2915 +2916 +2917 +2918 +2919 +2920 +2921 +2922 +2923 +2924 +2925 +2926 +2927 +2928 +2929 +2930 +2931 +2932 +2933 +2934 +2935 +2936 +2937 +2938 +2939 +2940 +2941 +2942 +2943 +2944 +2945 +2946 +2947 +2948 +2949 +2950 +2951 +2952 +2953 +2954 +2955 +2956 +2957 +2958 +2959 +2960 +2961 +2962 +2963 +2964 +2965 +2966 +2967 +2968 +2969 +2970 +2971 +2972 +2973 +2974 +2975 +2976 +2977 +2978 +2979 +2980 +2981 +2982 +2983 +2984 +2985 +2986 +2987 +2988 +2989 +2990 +2991 +2992 +2993 +2994 +2995 +2996 +2997 +2998 +2999 +3000 +3001 +3002 +3003 +3004 +3005 +3006 +3007 +3008 +3009 +3010 +3011 +3012 +3013 +3014 +3015 +3016 +3017 +3018 +3019 +3020 +3021 +3022 +3023 +3024 +3025 +3026 +3027 +3028 +3029 +3030 +3031 +3032 +3033 +3034 +3035 +3036 +3037 +3038 +3039 +3040 +3041 +3042 +3043 +3044 +3045 +3046 +3047 +3048 +3049 +3050 +3051 +3052 +3053 +3054 +3055 +3056 +3057 +3058 +3059 +3060 +3061 +3062 +3063 +3064 +3065 +3066 +3067 +3068 +3069 +3070 +3071 +3072 +3073 +3074 +3075 +3076 +3077 +3078 +3079 +3080 +3081 +3082 +3083 +3084 +3085 +3086 +3087 +3088 +3089 +3090 +3091 +3092 +3093 +3094 +3095 +3096 +3097 +3098 +3099 +3100 +3101 +3102 +3103 +3104 +3105 +3106 +3107 +3108 +3109 +3110 +3111 +3112 +3113 +3114 +3115 +3116 +3117 +3118 +3119 +3120 +3121 +3122 +3123 +3124 +3125 +3126 +3127 +3128 +3129 +3130 +3131 +3132 +3133 +3134 +3135 +3136 +3137 +3138 +3139 +3140 +3141 +3142 +3143 +3144 +3145 +3146 +3147 +3148 +3149 +3150 +3151 +3152 +3153 +3154 +3155 +3156 +3157 +3158 +3159 +3160 +3161 +3162 +3163 +3164 +3165 +3166 +3167 +3168 +3169 +3170 +3171 +3172 +3173 +3174 +3175 +3176 +3177 +3178 +3179 +3180 +3181 +3182 +3183 +3184 +3185 +3186 +3187 +3188 +3189 +3190 +3191 +3192 +3193 +3194 +3195 +3196 +3197 +3198 +3199 +3200 +3201 +3202 +3203 +3204 +3205 +3206 +3207 +3208 +3209 +3210 +3211 +3212 +3213 +3214 +3215 +3216 +3217 +3218 +3219 +3220 +3221 +3222 +3223 +3224 +3225 +3226 +3227 +3228 +3229 +3230 +3231 +3232 +3233 +3234 +3235 +3236 +3237 +3238 +3239 +3240 +3241 +3242 +3243 +3244 +3245 +3246 +3247 +3248 +3249 +3250 +3251 +3252 +3253 +3254 +3255 +3256 +3257 +3258 +3259 +3260 +3261 +3262 +3263 +3264 +3265 +3266 +3267 +3268 +3269 +3270 +3271 +3272 +3273 +3274 +3275 +3276 +3277 +3278 +3279 +3280 +3281 +3282 +3283 +3284 +3285 +3286 +3287 +3288 +3289 +3290 +3291 +3292 +3293 +3294 +3295 +3296 +3297 +3298 +3299 +3300 +3301 +3302 +3303 +3304 +3305 +3306 +3307 +3308 +3309 +3310 +3311 +3312 +3313 +3314 +3315 +3316 +3317 +3318 +3319 +3320 +3321 +3322 +3323 +3324 +3325 +3326 +3327 +3328 +3329 +3330 +3331 +3332 +3333 +3334 +3335 +3336 +3337 +3338 +3339 +3340 +3341 +3342 +3343 +3344 +3345 +3346 +3347 +3348 +3349 +3350 +3351 +3352 +3353 +3354 +3355 +3356 +3357 +3358 +3359 +3360 +3361 +3362 +3363 +3364 +3365 +3366 +3367 +3368 +3369 +3370 +3371 +3372 +3373 +3374 +3375 +3376 +3377 +3378 +3379 +3380 +3381 +3382 +3383 +3384 +3385 +3386 +3387 +3388 +3389 +3390 +3391 +3392 +3393 +3394 +3395 +3396 +3397 +3398 +3399 +3400 +3401 +3402 +3403 +3404 +3405 +3406 +3407 +3408 +3409 +3410 +3411 +3412 +3413 +3414 +3415 +3416 +3417 +3418 +3419 +3420 +3421 +3422 +3423 +3424 +3425 +3426 +3427 +3428 +3429 +3430 +3431 +3432 +3433 +3434 +3435 +3436 +3437 +3438 +3439 +3440 +3441 +3442 +3443 +3444 +3445 +3446 +3447 +3448 +3449 +3450 +3451 +3452 +3453 +3454 +3455 +3456 +3457 +3458 +3459 +3460 +3461 +3462 +3463 +3464 +3465 +3466 +3467 +3468 +3469 +3470 +3471 +3472 +3473 +3474 +3475 +3476 +3477 +3478 +3479 +3480 +3481 +3482 +3483 +3484 +3485 +3486 +3487 +3488 +3489 +3490 +3491 +3492 +3493 +3494 +3495 +3496 +3497 +3498 +3499 +3500 +3501 +3502 +3503 +3504 +3505 +3506 +3507 +3508 +3509 +3510 +3511 +3512 +3513 +3514 +3515 +3516 +3517 +3518 +3519 +3520 +3521 +3522 +3523 +3524 +3525 +3526 +3527 +3528 +3529 +3530 +3531 +3532 +3533 +3534 +3535 +3536 +3537 +3538 +3539 +3540 +3541 +3542 +3543 +3544 +3545 +3546 +3547 +3548 +3549 +3550 +3551 +3552 +3553 +3554 +3555 +3556 +3557 +3558 +3559 +3560 +3561 +3562 +3563 +3564 +3565 +3566 +3567 +3568 +3569 +3570 +3571 +3572 +3573 +3574 +3575 +3576 +3577 +3578 +3579 +3580 +3581 +3582 +3583 +3584 +3585 +3586 +3587 +3588 +3589 +3590 +3591 +3592 +3593 +3594 +3595 +3596 +3597 +3598 +3599 +3600 +3601 +3602 +3603 +3604 +3605 +3606 +3607 +3608 +3609 +3610 +3611 +3612 +3613 +3614 +3615 +3616 +3617 +3618 +3619 +3620 +3621 +3622 +3623 +3624 +3625 +3626 +3627 +3628 +3629 +3630 +3631 +3632 +3633 +3634 +3635 +3636 +3637 +3638 +3639 +3640 +3641 +3642 +3643 +3644 +3645 +3646 +3647 +3648 +3649 +3650 +3651 +3652 +3653 +3654 +3655 +3656 +3657 +3658 +3659 +3660 +3661 +3662 +3663 +3664 +3665 +3666 +3667 +3668 +3669 +3670 +3671 +3672 +3673 +3674 +3675 +3676 +3677 +3678 +3679 +3680 +3681 +3682 +3683 +3684 +3685 +3686 +3687 +3688 +3689 +3690 +3691 +3692 +3693 +3694 +3695 +3696 +3697 +3698 +3699 +3700 +3701 +3702 +3703 +3704 +3705 +3706 +3707 +3708 +3709 +3710 +3711 +3712 +3713 +3714 +3715 +3716 +3717 +3718 +3719 +3720 +3721 +3722 +3723 +3724 +3725 +3726 +3727 +3728 +3729 +3730 +3731 +3732 +3733 +3734 +3735 +3736 +3737 +3738 +3739 +3740 +3741 +3742 +3743 +3744 +3745 +3746 +3747 +3748 +3749 +3750 +3751 +3752 +3753 +3754 +3755 +3756 +3757 +3758 +3759 +3760 +3761 +3762 +3763 +3764 +3765 +3766 +3767 +3768 +3769 +3770 +3771 +3772 +3773 +3774 +3775 +3776 +3777 +3778 +3779 +3780 +3781 +3782 +3783 +3784 +3785 +3786 +3787 +3788 +3789 +3790 +3791 +3792 +3793 +3794 +3795 +3796 +3797 +3798 +3799 +3800 +3801 +3802 +3803 +3804 +3805 +3806 +3807 +3808 +3809 +3810 +3811 +3812 +3813 +3814 +3815 +3816 +3817 +3818 +3819 +3820 +3821 +3822 +3823 +3824 +3825 +3826 +3827 +3828 +3829 +3830 +3831 +3832 +3833 +3834 +3835 +3836 +3837 +3838 +3839 +3840 +3841 +3842 +3843 +3844 +3845 +3846 +3847 +3848 +3849 +3850 +3851 +3852 +3853 +3854 +3855 +3856 +3857 +3858 +3859 +3860 +3861 +3862 +3863 +3864 +3865 +3866 +3867 +3868 +3869 +3870 +3871 +3872 +3873 +3874 +3875 +3876 +3877 +3878 +3879 +3880 +3881 +3882 +3883 +3884 +3885 +3886 +3887 +3888 +3889 +3890 +3891 +3892 +3893 +3894 +3895 +3896 +3897 +3898 +3899 +3900 +3901 +3902 +3903 +3904 +3905 +3906 +3907 +3908 +3909 +3910 +3911 +3912 +3913 +3914 +3915 +3916 +3917 +3918 +3919 +3920 +3921 +3922 +3923 +3924 +3925 +3926 +3927 +3928 +3929 +3930 +3931 +3932 +3933 +3934 +3935 +3936 +3937 +3938 +3939 +3940 +3941 +3942 +3943 +3944 +3945 +3946 +3947 +3948 +3949 +3950 +3951 +3952 +3953 +3954 +3955 +3956 +3957 +3958 +3959 +3960 +3961 +3962 +3963 +3964 +3965 +3966 +3967 +3968 +3969 +3970 +3971 +3972 +3973 +3974 +3975 +3976 +3977 +3978 +3979 +3980 +3981 +3982 +3983 +3984 +3985 +3986 +3987 +3988 +3989 +3990 +3991 +3992 +3993 +3994 +3995 +3996 +3997 +3998 +3999 +4000 +4001 +4002 +4003 +4004 +4005 +4006 +4007 +4008 +4009 +4010 +4011 +4012 +4013 +4014 +4015 +4016 +4017 +4018 +4019 +4020 +4021 +4022 +4023 +4024 +4025 +4026 +4027 +4028 +4029 +4030 +4031 +4032 +4033 +4034 +4035 +4036 +4037 +4038 +4039 +4040 +4041 +4042 +4043 +4044 +4045 +4046 +4047 +4048 +4049 +4050 +4051 +4052 +4053 +4054 +4055 +4056 +4057 +4058 +4059 +4060 +4061 +4062 +4063 +4064 +4065 +4066 +4067 +4068 +4069 +4070 +4071 +4072 +4073 +4074 +4075 +4076 +4077 +4078 +4079 +4080 +4081 +4082 +4083 +4084 +4085 +4086 +4087 +4088 +4089 +4090 +4091 +4092 +4093 +4094 +4095 +4096 +4097 +4098 +4099 +4100 +4101 +4102 +4103 +4104 +4105 +4106 +4107 +4108 +4109 +4110 +4111 +4112 +4113 +4114 +4115 +4116 +4117 +4118 +4119 +4120 +4121 +4122 +4123 +4124 +4125 +4126 +4127 +4128 +4129 +4130 +4131 +4132 +4133 +4134 +4135 +4136 +4137 +4138 +4139 +4140 +4141 +4142 +4143 +4144 +4145 +4146 +4147 +4148 +4149 +4150 +4151 +4152 +4153 +4154 +4155 +4156 +4157 +4158 +4159 +4160 +4161 +4162 +4163 +4164 +4165 +4166 +4167 +4168 +4169 +4170 +4171 +4172 +4173 +4174 +4175 +4176 +4177 +4178 +4179 +4180 +4181 +4182 +4183 +4184 +4185 +4186 +4187 +4188 +4189 +4190 +4191 +4192 +4193 +4194 +4195 +4196 +4197 +4198 +4199 +4200 +4201 +4202 +4203 +4204 +4205 +4206 +4207 +4208 +4209 +4210 +4211 +4212 +4213 +4214 +4215 +4216 +4217 +4218 +4219 +4220 +4221 +4222 +4223 +4224 +4225 +4226 +4227 +4228 +4229 +4230 +4231 +4232 +4233 +4234 +4235 +4236 +4237 +4238 +4239 +4240 +4241 +4242 +4243 +4244 +4245 +4246 +4247 +4248 +4249 +4250 +4251 +4252 +4253 +4254 +4255 +4256 +4257 +4258 +4259 +4260 +4261 +4262 +4263 +4264 +4265 +4266 +4267 +4268 +4269 +4270 +4271 +4272 +4273 +4274 +4275 +4276 +4277 +4278 +4279 +4280 +4281 +4282 +4283 +4284 +4285 +4286 +4287 +4288 +4289 +4290 +4291 +4292 +4293 +4294 +4295 +4296 +4297 +4298 +4299 +4300 +4301 +4302 +4303 +4304 +4305 +4306 +4307 +4308 +4309 +4310 +4311 +4312 +4313 +4314 +4315 +4316 +4317 +4318 +4319 +4320 +4321 +4322 +4323 +4324 +4325 +4326 +4327 +4328 +4329 +4330 +4331 +4332 +4333 +4334 +4335 +4336 +4337 +4338 +4339 +4340 +4341 +4342 +4343 +4344 +4345 +4346 +4347 +4348 +4349 +4350 +4351 +4352 +4353 +4354 +4355 +4356 +4357 +4358 +4359 +4360 +4361 +4362 +4363 +4364 +4365 +4366 +4367 +4368 +4369 +4370 +4371 +4372 +4373 +4374 +4375 +4376 +4377 +4378 +4379 +4380 +4381 +4382 +4383 +4384 +4385 +4386 +4387 +4388 +4389 +4390 +4391 +4392 +4393 +4394 +4395 +4396 +4397 +4398 +4399 +4400 +4401 +4402 +4403 +4404 +4405 +4406 +4407 +4408 +4409 +4410 +4411 +4412 +4413 +4414 +4415 +4416 +4417 +4418 +4419 +4420 +4421 +4422 +4423 +4424 +4425 +4426 +4427 +4428 +4429 +4430 +4431 +4432 +4433 +4434 +4435 +4436 +4437 +4438 +4439 +4440 +4441 +4442 +4443 +4444 +4445 +4446 +4447 +4448 +4449 +4450 +4451 +4452 +4453 +4454 +4455 +4456 +4457 +4458 +4459 +4460 +4461 +4462 +4463 +4464 +4465 +4466 +4467 +4468 +4469 +4470 +4471 +4472 +4473 +4474 +4475 +4476 +4477 +4478 +4479 +4480 +4481 +4482 +4483 +4484 +4485 +4486 +4487 +4488 +4489 +4490 +4491 +4492 +4493 +4494 +4495 +4496 +4497 +4498 +4499 +4500 +4501 +4502 +4503 +4504 +4505 +4506 +4507 +4508 +4509 +4510 +4511 +4512 +4513 +4514 +4515 +4516 +4517 +4518 +4519 +4520 +4521 +4522 +4523 +4524 +4525 +4526 +4527 +4528 +4529 +4530 +4531 +4532 +4533 +4534 +4535 +4536 +4537 +4538 +4539 +4540 +4541 +4542 +4543 +4544 +4545 +4546 +4547 +4548 +4549 +4550 +4551 +4552 +4553 +4554 +4555 +4556 +4557 +4558 +4559 +4560 +4561 +4562 +4563 +4564 +4565 +4566 +4567 +4568 +4569 +4570 +4571 +4572 +4573 +4574 +4575 +4576 +4577 +4578 +4579 +4580 +4581 +4582 +4583 +4584 +4585 +4586 +4587 +4588 +4589 +4590 +4591 +4592 +4593 +4594 +4595 +4596 +4597 +4598 +4599 +4600 +4601 +4602 +4603 +4604 +4605 +4606 +4607 +4608 +4609 +4610 +4611 +4612 +4613 +4614 +4615 +4616 +4617 +4618 +4619 +4620 +4621 +4622 +4623 +4624 +4625 +4626 +4627 +4628 +4629 +4630 +4631 +4632 +4633 +4634 +4635 +4636 +4637 +4638 +4639 +4640 +4641 +4642 +4643 +4644 +4645 +4646 +4647 +4648 +4649 +4650 +4651 +4652 +4653 +4654 +4655 +4656 +4657 +4658 +4659 +4660 +4661 +4662 +4663 +4664 +4665 +4666 +4667 +4668 +4669 +4670 +4671 +4672 +4673 +4674 +4675 +4676 +4677 +4678 +4679 +4680 +4681 +4682 +4683 +4684 +4685 +4686 +4687 +4688 +4689 +4690 +4691 +4692 +4693 +4694 +4695 +4696 +4697 +4698 +4699 +4700 +4701 +4702 +4703 +4704 +4705 +4706 +4707 +4708 +4709 +4710 +4711 +4712 +4713 +4714 +4715 +4716 +4717 +4718 +4719 +4720 +4721 +4722 +4723 +4724 +4725 +4726 +4727 +4728 +4729 +4730 +4731 +4732 +4733 +4734 +4735 +4736 +4737 +4738 +4739 +4740 +4741 +4742 +4743 +4744 +4745 +4746 +4747 +4748 +4749 +4750 +4751 +4752 +4753 +4754 +4755 +4756 +4757 +4758 +4759 +4760 +4761 +4762 +4763 +4764 +4765 +4766 +4767 +4768 +4769 +4770 +4771 +4772 +4773 +4774 +4775 +4776 +4777 +4778 +4779 +4780 +4781 +4782 +4783 +4784 +4785 +4786 +4787 +4788 +4789 +4790 +4791 +4792 +4793 +4794 +4795 +4796 +4797 +4798 +4799 +4800 +4801 +4802 +4803 +4804 +4805 +4806 +4807 +4808 +4809 +4810 +4811 +4812 +4813 +4814 +4815 +4816 +4817 +4818 +4819 +4820 +4821 +4822 +4823 +4824 +4825 +4826 +4827 +4828 +4829 +4830 +4831 +4832 +4833 +4834 +4835 +4836 +4837 +4838 +4839 +4840 +4841 +4842 +4843 +4844 +4845 +4846 +4847 +4848 +4849 +4850 +4851 +4852 +4853 +4854 +4855 +4856 +4857 +4858 +4859 +4860 +4861 +4862 +4863 +4864 +4865 +4866 +4867 +4868 +4869 +4870 +4871 +4872 +4873 +4874 +4875 +4876 +4877 +4878 +4879 +4880 +4881 +4882 +4883 +4884 +4885 +4886 +4887 +4888 +4889 +4890 +4891 +4892 +4893 +4894 +4895 +4896 +4897 +4898 +4899 +4900 +4901 +4902 +4903 +4904 +4905 +4906 +4907 +4908 +4909 +4910 +4911 +4912 +4913 +4914 +4915 +4916 +4917 +4918 +4919 +4920 +4921 +4922 +4923 +4924 +4925 +4926 +4927 +4928 +4929 +4930 +4931 +4932 +4933 +4934 +4935 +4936 +4937 +4938 +4939 +4940 +4941 +4942 +4943 +4944 +4945 +4946 +4947 +4948 +4949 +4950 +4951 +4952 +4953 +4954 +4955 +4956 +4957 +4958 +4959 +4960 +4961 +4962 +4963 +4964 +4965 +4966 +4967 +4968 +4969 +4970 +4971 +4972 +4973 +4974 +4975 +4976 +4977 +4978 +4979 +4980 +4981 +4982 +4983 +4984 +4985 +4986 +4987 +4988 +4989 +4990 +4991 +4992 +4993 +4994 +4995 +4996 +4997 +4998 +4999 +5000 +5001 +5002 +5003 +5004 +5005 +5006 +5007 +5008 +5009 +5010 +5011 +5012 +5013 +5014 +5015 +5016 +5017 +5018 +5019 +5020 +5021 +5022 +5023 +5024 +5025 +5026 +5027 +5028 +5029 +5030 +5031 +5032 +5033 +5034 +5035 +5036 +5037 +5038 +5039 +5040 +5041 +5042 +5043 +5044 +5045 +5046 +5047 +5048 +5049 +5050 +5051 +5052 +5053 +5054 +5055 +5056 +5057 +5058 +5059 +5060 +5061 +5062 +5063 +5064 +5065 +5066 +5067 +5068 +5069 +5070 +5071 +5072 +5073 +5074 +5075 +5076 +5077 +5078 +5079 +5080 +5081 +5082 +5083 +5084 +5085 +5086 +5087 +5088 +5089 +5090 +5091 +5092 +5093 +5094 +5095 +5096 +5097 +5098 +5099 +5100 +5101 +5102 +5103 +5104 +5105 +5106 +5107 +5108 +5109 +5110 +5111 +5112 +5113 +5114 +5115 +5116 +5117 +5118 +5119 +5120 +5121 +5122 +5123 +5124 +5125 +5126 +5127 +5128 +5129 +5130 +5131 +5132 +5133 +5134 +5135 +5136 +5137 +5138 +5139 +5140 +5141 +5142 +5143 +5144 +5145 +5146 +5147 +5148 +5149 +5150 +5151 +5152 +5153 +5154 +5155 +5156 +5157 +5158 +5159 +5160 +5161 +5162 +5163 +5164 +5165 +5166 +5167 +5168 +5169 +5170 +5171 +5172 +5173 +5174 +5175 +5176 +5177 +5178 +5179 +5180 +5181 +5182 +5183 +5184 +5185 +5186 +5187 +5188 +5189 +5190 +5191 +5192 +5193 +5194 +5195 +5196 +5197 +5198 +5199 +5200 +5201 +5202 +5203 +5204 +5205 +5206 +5207 +5208 +5209 +5210 +5211 +5212 +5213 +5214 +5215 +5216 +5217 +5218 +5219 +5220 +5221 +5222 +5223 +5224 +5225 +5226 +5227 +5228 +5229 +5230 +5231 +5232 +5233 +5234 +5235 +5236 +5237 +5238 +5239 +5240 +5241 +5242 +5243 +5244 +5245 +5246 +5247 +5248 +5249 +5250 +5251 +5252 +5253 +5254 +5255 +5256 +5257 +5258 +5259 +5260 +5261 +5262 +5263 +5264 +5265 +5266 +5267 +5268 +5269 +5270 +5271 +5272 +5273 +5274 +5275 +5276 +5277 +5278 +5279 +5280 +5281 +5282 +5283 +5284 +5285 +5286 +5287 +5288 +5289 +5290 +5291 +5292 +5293 +5294 +5295 +5296 +5297 +5298 +5299 +5300 +5301 +5302 +5303 +5304 +5305 +5306 +5307 +5308 +5309 +5310 +5311 +5312 +5313 +5314 +5315 +5316 +5317 +5318 +5319 +5320 +5321 +5322 +5323 +5324 +5325 +5326 +5327 +5328 +5329 +5330 +5331 +5332 +5333 +5334 +5335 +5336 +5337 +5338 +5339 +5340 +5341 +5342 +5343 +5344 +5345 +5346 +5347 +5348 +5349 +5350 +5351 +5352 +5353 +5354 +5355 +5356 +5357 +5358 +5359 +5360 +5361 +5362 +5363 +5364 +5365 +5366 +5367 +5368 +5369 +5370 +5371 +5372 +5373 +5374 +5375 +5376 +5377 +5378 +5379 +5380 +5381 +5382 +5383 +5384 +5385 +5386 +5387 +5388 +5389 +5390 +5391 +5392 +5393 +5394 +5395 +5396 +5397 +5398 +5399 +5400 +5401 +5402 +5403 +5404 +5405 +5406 +5407 +5408 +5409 +5410 +5411 +5412 +5413 +5414 +5415 +5416 +5417 +5418 +5419 +5420 +5421 +5422 +5423 +5424 +5425 +5426 +5427 +5428 +5429 +5430 +5431 +5432 +5433 +5434 +5435 +5436 +5437 +5438 +5439 +5440 +5441 +5442 +5443 +5444 +5445 +5446 +5447 +5448 +5449 +5450 +5451 +5452 +5453 +5454 +5455 +5456 +5457 +5458 +5459 +5460 +5461 +5462 +5463 +5464 +5465 +5466 +5467 +5468 +5469 +5470 +5471 +5472 +5473 +5474 +5475 +5476 +5477 +5478 +5479 +5480 +5481 +5482 +5483 +5484 +5485 +5486 +5487 +5488 +5489 +5490 +5491 +5492 +5493 +5494 +5495 +5496 +5497 +5498 +5499 +5500 +5501 +5502 +5503 +5504 +5505 +5506 +5507 +5508 +5509 +5510 +5511 +5512 +5513 +5514 +5515 +5516 +5517 +5518 +5519 +5520 +5521 +5522 +5523 +5524 +5525 +5526 +5527 +5528 +5529 +5530 +5531 +5532 +5533 +5534 +5535 +5536 +5537 +5538 +5539 +5540 +5541 +5542 +5543 +5544 +5545 +5546 +5547 +5548 +5549 +5550 +5551 +5552 +5553 +5554 +5555 +5556 +5557 +5558 +5559 +5560 +5561 +5562 +5563 +5564 +5565 +5566 +5567 +5568 +5569 +5570 +5571 +5572 +5573 +5574 +5575 +5576 +5577 +5578 +5579 +5580 +5581 +5582 +5583 +5584 +5585 +5586 +5587 +5588 +5589 +5590 +5591 +5592 +5593 +5594 +5595 +5596 +5597 +5598 +5599 +5600 +5601 +5602 +5603 +5604 +5605 +5606 +5607 +5608 +5609 +5610 +5611 +5612 +5613 +5614 +5615 +5616 +5617 +5618 +5619 +5620 +5621 +5622 +5623 +5624 +5625 +5626 +5627 +5628 +5629 +5630 +5631 +5632 +5633 +5634 +5635 +5636 +5637 +5638 +5639 +5640 +5641 +5642 +5643 +5644 +5645 +5646 +5647 +5648 +5649 +5650 +5651 +5652 +5653 +5654 +5655 +5656 +5657 +5658 +5659 +5660 +5661 +5662 +5663 +5664 +5665 +5666 +5667 +5668 +5669 +5670 +5671 +5672 +5673 +5674 +5675 +5676 +5677 +5678 +5679 +5680 +5681 +5682 +5683 +5684 +5685 +5686 +5687 +5688 +5689 +5690 +5691 +5692 +5693 +5694 +5695 +5696 +5697 +5698 +5699 +5700 +5701 +5702 +5703 +5704 +5705 +5706 +5707 +5708 +5709 +5710 +5711 +5712 +5713 +5714 +5715 +5716 +5717 +5718 +5719 +5720 +5721 +5722 +5723 +5724 +5725 +5726 +5727 +5728 +5729 +5730 +5731 +5732 +5733 +5734 +5735 +5736 +5737 +5738 +5739 +5740 +5741 +5742 +5743 +5744 +5745 +5746 +5747 +5748 +5749 +5750 +5751 +5752 +5753 +5754 +5755 +5756 +5757 +5758 +5759 +5760 +5761 +5762 +5763 +5764 +5765 +5766 +5767 +5768 +5769 +5770 +5771 +5772 +5773 +5774 +5775 +5776 +5777 +5778 +5779 +5780 +5781 +5782 +5783 +5784 +5785 +5786 +5787 +5788 +5789 +5790 +5791 +5792 +5793 +5794 +5795 +5796 +5797 +5798 +5799 +5800 +5801 +5802 +5803 +5804 +5805 +5806 +5807 +5808 +5809 +5810 +5811 +5812 +5813 +5814 +5815 +5816 +5817 +5818 +5819 +5820 +5821 +5822 +5823 +5824 +5825 +5826 +5827 +5828 +5829 +5830 +5831 +5832 +5833 +5834 +5835 +5836 +5837 +5838 +5839 +5840 +5841 +5842 +5843 +5844 +5845 +5846 +5847 +5848 +5849 +5850 +5851 +5852 +5853 +5854 +5855 +5856 +5857 +5858 +5859 +5860 +5861 +5862 +5863 +5864 +5865 +5866 +5867 +5868 +5869 +5870 +5871 +5872 +5873 +5874 +5875 +5876 +5877 +5878 +5879 +5880 +5881 +5882 +5883 +5884 +5885 +5886 +5887 +5888 +5889 +5890 +5891 +5892 +5893 +5894 +5895 +5896 +5897 +5898 +5899 +5900 +5901 +5902 +5903 +5904 +5905 +5906 +5907 +5908 +5909 +5910 +5911 +5912 +5913 +5914 +5915 +5916 +5917 +5918 +5919 +5920 +5921 +5922 +5923 +5924 +5925 +5926 +5927 +5928 +5929 +5930 +5931 +5932 +5933 +5934 +5935 +5936 +5937 +5938 +5939 +5940 +5941 +5942 +5943 +5944 +5945 +5946 +5947 +5948 +5949 +5950 +5951 +5952 +5953 +5954 +5955 +5956 +5957 +5958 +5959 +5960 +5961 +5962 +5963 +5964 +5965 +5966 +5967 +5968 +5969 +5970 +5971 +5972 +5973 +5974 +5975 +5976 +5977 +5978 +5979 +5980 +5981 +5982 +5983 +5984 +5985 +5986 +5987 +5988 +5989 +5990 +5991 +5992 +5993 +5994 +5995 +5996 +5997 +5998 +5999 +6000 +6001 +6002 +6003 +6004 +6005 +6006 +6007 +6008 +6009 +6010 +6011 +6012 +6013 +6014 +6015 +6016 +6017 +6018 +6019 +6020 +6021 +6022 +6023 +6024 +6025 +6026 +6027 +6028 +6029 +6030 +6031 +6032 +6033 +6034 +6035 +6036 +6037 +6038 +6039 +6040 +6041 +6042 +6043 +6044 +6045 +6046 +6047 +6048 +6049 +6050 +6051 +6052 +6053 +6054 +6055 +6056 +6057 +6058 +6059 +6060 +6061 +6062 +6063 +6064 +6065 +6066 +6067 +6068 +6069 +6070 +6071 +6072 +6073 +6074 +6075 +6076 +6077 +6078 +6079 +6080 +6081 +6082 +6083 +6084 +6085 +6086 +6087 +6088 +6089 +6090 +6091 +6092 +6093 +6094 +6095 +6096 +6097 +6098 +6099 +6100 +6101 +6102 +6103 +6104 +6105 +6106 +6107 +6108 +6109 +6110 +6111 +6112 +6113 +6114 +6115 +6116 +6117 +6118 +6119 +6120 +6121 +6122 +6123 +6124 +6125 +6126 +6127 +6128 +6129 +6130 +6131 +6132 +6133 +6134 +6135 +6136 +6137 +6138 +6139 +6140 +6141 +6142 +6143 +6144 +6145 +6146 +6147 +6148 +6149 +6150 +6151 +6152 +6153 +6154 +6155 +6156 +6157 +6158 +6159 +6160 +6161 +6162 +6163 +6164 +6165 +6166 +6167 +6168 +6169 +6170 +6171 +6172 +6173 +6174 +6175 +6176 +6177 +6178 +6179 +6180 +6181 +6182 +6183 +6184 +6185 +6186 +6187 +6188 +6189 +6190 +6191 +6192 +6193 +6194 +6195 +6196 +6197 +6198 +6199 +6200 +6201 +6202 +6203 +6204 +6205 +6206 +6207 +6208 +6209 +6210 +6211 +6212 +6213 +6214 +6215 +6216 +6217 +6218 +6219 +6220 +6221 +6222 +6223 +6224 +6225 +6226 +6227 +6228 +6229 +6230 +6231 +6232 +6233 +6234 +6235 +6236 +6237 +6238 +6239 +6240 +6241 +6242 +6243 +6244 +6245 +6246 +6247 +6248 +6249 +6250 +6251 +6252 +6253 +6254 +6255 +6256 +6257 +6258 +6259 +6260 +6261 +6262 +6263 +6264 +6265 +6266 +6267 +6268 +6269 +6270 +6271 +6272 +6273 +6274 +6275 +6276 +6277 +6278 +6279 +6280 +6281 +6282 +6283 +6284 +6285 +6286 +6287 +6288 +6289 +6290 +6291 +6292 +6293 +6294 +6295 +6296 +6297 +6298 +6299 +6300 +6301 +6302 +6303 +6304 +6305 +6306 +6307 +6308 +6309 +6310 +6311 +6312 +6313 +6314 +6315 +6316 +6317 +6318 +6319 +6320 +6321 +6322 +6323 +6324 +6325 +6326 +6327 +6328 +6329 +6330 +6331 +6332 +6333 +6334 +6335 +6336 +6337 +6338 +6339 +6340 +6341 +6342 +6343 +6344 +6345 +6346 +6347 +6348 +6349 +6350 +6351 +6352 +6353 +6354 +6355 +6356 +6357 +6358 +6359 +6360 +6361 +6362 +6363 +6364 +6365 +6366 +6367 +6368 +6369 +6370 +6371 +6372 +6373 +6374 +6375 +6376 +6377 +6378 +6379 +6380 +6381 +6382 +6383 +6384 +6385 +6386 +6387 +6388 +6389 +6390 +6391 +6392 +6393 +6394 +6395 +6396 +6397 +6398 +6399 +6400 +6401 +6402 +6403 +6404 +6405 +6406 +6407 +6408 +6409 +6410 +6411 +6412 +6413 +6414 +6415 +6416 +6417 +6418 +6419 +6420 +6421 +6422 +6423 +6424 +6425 +6426 +6427 +6428 +6429 +6430 +6431 +6432 +6433 +6434 +6435 +6436 +6437 +6438 +6439 +6440 +6441 +6442 +6443 +6444 +6445 +6446 +6447 +6448 +6449 +6450 +6451 +6452 +6453 +6454 +6455 +6456 +6457 +6458 +6459 +6460 +6461 +6462 +6463 +6464 +6465 +6466 +6467 +6468 +6469 +6470 +6471 +6472 +6473 +6474 +6475 +6476 +6477 +6478 +6479 +6480 +6481 +6482 +6483 +6484 +6485 +6486 +6487 +6488 +6489 +6490 +6491 +6492 +6493 +6494 +6495 +6496 +6497 +6498 +6499 +6500 +6501 +6502 +6503 +6504 +6505 +6506 +6507 +6508 +6509 +6510 +6511 +6512 +6513 +6514 +6515 +6516 +6517 +6518 +6519 +6520 +6521 +6522 +6523 +6524 +6525 +6526 +6527 +6528 +6529 +6530 +6531 +6532 +6533 +6534 +6535 +6536 +6537 +6538 +6539 +6540 +6541 +6542 +6543 +6544 +6545 +6546 +6547 +6548 +6549 +6550 +6551 +6552 +6553 +6554 +6555 +6556 +6557 +6558 +6559 +6560 +6561 +6562 +6563 +6564 +6565 +6566 +6567 +6568 +6569 +6570 +6571 +6572 +6573 +6574 +6575 +6576 +6577 +6578 +6579 +6580 +6581 +6582 +6583 +6584 +6585 +6586 +6587 +6588 +6589 +6590 +6591 +6592 +6593 +6594 +6595 +6596 +6597 +6598 +6599 +6600 +6601 +6602 +6603 +6604 +6605 +6606 +6607 +6608 +6609 +6610 +6611 +6612 +6613 +6614 +6615 +6616 +6617 +6618 +6619 +6620 +6621 +6622 +6623 +6624 +6625 +6626 +6627 +6628 +6629 +6630 +6631 +6632 +6633 +6634 +6635 +6636 +6637 +6638 +6639 +6640 +6641 +6642 +6643 +6644 +6645 +6646 +6647 +6648 +6649 +6650 +6651 +6652 +6653 +6654 +6655 +6656 +6657 +6658 +6659 +6660 +6661 +6662 +6663 +6664 +6665 +6666 +6667 +6668 +6669 +6670 +6671 +6672 +6673 +6674 +6675 +6676 +6677 +6678 +6679 +6680 +6681 +6682 +6683 +6684 +6685 +6686 +6687 +6688 +6689 +6690 +6691 +6692 +6693 +6694 +6695 +6696 +6697 +6698 +6699 +6700 +6701 +6702 +6703 +6704 +6705 +6706 +6707 +6708 +6709 +6710 +6711 +6712 +6713 +6714 +6715 +6716 +6717 +6718 +6719 +6720 +6721 +6722 +6723 +6724 +6725 +6726 +6727 +6728 +6729 +6730 +6731 +6732 +6733 +6734 +6735 +6736 +6737 +6738 +6739 +6740 +6741 +6742 +6743 +6744 +6745 +6746 +6747 +6748 +6749 +6750 +6751 +6752 +6753 +6754 +6755 +6756 +6757 +6758 +6759 +6760 +6761 +6762 +6763 +6764 +6765 +6766 +6767 +6768 +6769 +6770 +6771 +6772 +6773 +6774 +6775 +6776 +6777 +6778 +6779 +6780 +6781 +6782 +6783 +6784 +6785 +6786 +6787 +6788 +6789 +6790 +6791 +6792 +6793 +6794 +6795 +6796 +6797 +6798 +6799 +6800 +6801 +6802 +6803 +6804 +6805 +6806 +6807 +6808 +6809 +6810 +6811 +6812 +6813 +6814 +6815 +6816 +6817 +6818 +6819 +6820 +6821 +6822 +6823 +6824 +6825 +6826 +6827 +6828 +6829 +6830 +6831 +6832 +6833 +6834 +6835 +6836 +6837 +6838 +6839 +6840 +6841 +6842 +6843 +6844 +6845 +6846 +6847 +6848 +6849 +6850 +6851 +6852 +6853 +6854 +6855 +6856 +6857 +6858 +6859 +6860 +6861 +6862 +6863 +6864 +6865 +6866 +6867 +6868 +6869 +6870 +6871 +6872 +6873 +6874 +6875 +6876 +6877 +6878 +6879 +6880 +6881 +6882 +6883 +6884 +6885 +6886 +6887 +6888 +6889 +6890 +6891 +6892 +6893 +6894 +6895 +6896 +6897 +6898 +6899 +6900 +6901 +6902 +6903 +6904 +6905 +6906 +6907 +6908 +6909 +6910 +6911 +6912 +6913 +6914 +6915 +6916 +6917 +6918 +6919 +6920 +6921 +6922 +6923 +6924 +6925 +6926 +6927 +6928 +6929 +6930 +6931 +6932 +6933 +6934 +6935 +6936 +6937 +6938 +6939 +6940 +6941 +6942 +6943 +6944 +6945 +6946 +6947 +6948 +6949 +6950 +6951 +6952 +6953 +6954 +6955 +6956 +6957 +6958 +6959 +6960 +6961 +6962 +6963 +6964 +6965 +6966 +6967 +6968 +6969 +6970 +6971 +6972 +6973 +6974 +6975 +6976 +6977 +6978 +6979 +6980 +6981 +6982 +6983 +6984 +6985 +6986 +6987 +6988 +6989 +6990 +6991 +6992 +6993 +6994 +6995 +6996 +6997 +6998 +6999 +7000 +7001 +7002 +7003 +7004 +7005 +7006 +7007 +7008 +7009 +7010 +7011 +7012 +7013 +7014 +7015 +7016 +7017 +7018 +7019 +7020 +7021 +7022 +7023 +7024 +7025 +7026 +7027 +7028 +7029 +7030 +7031 +7032 +7033 +7034 +7035 +7036 +7037 +7038 +7039 +7040 +7041 +7042 +7043 +7044 +7045 +7046 +7047 +7048 +7049 +7050 +7051 +7052 +7053 +7054 +7055 +7056 +7057 +7058 +7059 +7060 +7061 +7062 +7063 +7064 +7065 +7066 +7067 +7068 +7069 +7070 +7071 +7072 +7073 +7074 +7075 +7076 +7077 +7078 +7079 +7080 +7081 +7082 +7083 +7084 +7085 +7086 +7087 +7088 +7089 +7090 +7091 +7092 +7093 +7094 +7095 +7096 +7097 +7098 +7099 +7100 +7101 +7102 +7103 +7104 +7105 +7106 +7107 +7108 +7109 +7110 +7111 +7112 +7113 +7114 +7115 +7116 +7117 +7118 +7119 +7120 +7121 +7122 +7123 +7124 +7125 +7126 +7127 +7128 +7129 +7130 +7131 +7132 +7133 +7134 +7135 +7136 +7137 +7138 +7139 +7140 +7141 +7142 +7143 +7144 +7145 +7146 +7147 +7148 +7149 +7150 +7151 +7152 +7153 +7154 +7155 +7156 +7157 +7158 +7159 +7160 +7161 +7162 +7163 +7164 +7165 +7166 +7167 +7168 +7169 +7170 +7171 +7172 +7173 +7174 +7175 +7176 +7177 +7178 +7179 +7180 +7181 +7182 +7183 +7184 +7185 +7186 +7187 +7188 +7189 +7190 +7191 +7192 +7193 +7194 +7195 +7196 +7197 +7198 +7199 +7200 +7201 +7202 +7203 +7204 +7205 +7206 +7207 +7208 +7209 +7210 +7211 +7212 +7213 +7214 +7215 +7216 +7217 +7218 +7219 +7220 +7221 +7222 +7223 +7224 +7225 +7226 +7227 +7228 +7229 +7230 +7231 +7232 +7233 +7234 +7235 +7236 +7237 +7238 +7239 +7240 +7241 +7242 +7243 +7244 +7245 +7246 +7247 +7248 +7249 +7250 +7251 +7252 +7253 +7254 +7255 +7256 +7257 +7258 +7259 +7260 +7261 +7262 +7263 +7264 +7265 +7266 +7267 +7268 +7269 +7270 +7271 +7272 +7273 +7274 +7275 +7276 +7277 +7278 +7279 +7280 +7281 +7282 +7283 +7284 +7285 +7286 +7287 +7288 +7289 +7290 +7291 +7292 +7293 +7294 +7295 +7296 +7297 +7298 +7299 +7300 +7301 +7302 +7303 +7304 +7305 +7306 +7307 +7308 +7309 +7310 +7311 +7312 +7313 +7314 +7315 +7316 +7317 +7318 +7319 +7320 +7321 +7322 +7323 +7324 +7325 +7326 +7327 +7328 +7329 +7330 +7331 +7332 +7333 +7334 +7335 +7336 +7337 +7338 +7339 +7340 +7341 +7342 +7343 +7344 +7345 +7346 +7347 +7348 +7349 +7350 +7351 +7352 +7353 +7354 +7355 +7356 +7357 +7358 +7359 +7360 +7361 +7362 +7363 +7364 +7365 +7366 +7367 +7368 +7369 +7370 +7371 +7372 +7373 +7374 +7375 +7376 +7377 +7378 +7379 +7380 +7381 +7382 +7383 +7384 +7385 +7386 +7387 +7388 +7389 +7390 +7391 +7392 +7393 +7394 +7395 +7396 +7397 +7398 +7399 +7400 +7401 +7402 +7403 +7404 +7405 +7406 +7407 +7408 +7409 +7410 +7411 +7412 +7413 +7414 +7415 +7416 +7417 +7418 +7419 +7420 +7421 +7422 +7423 +7424 +7425 +7426 +7427 +7428 +7429 +7430 +7431 +7432 +7433 +7434 +7435 +7436 +7437 +7438 +7439 +7440 +7441 +7442 +7443 +7444 +7445 +7446 +7447 +7448 +7449 +7450 +7451 +7452 +7453 +7454 +7455 +7456 +7457 +7458 +7459 +7460 +7461 +7462 +7463 +7464 +7465 +7466 +7467 +7468 +7469 +7470 +7471 +7472 +7473 +7474 +7475 +7476 +7477 +7478 +7479 +7480 +7481 +7482 +7483 +7484 +7485 +7486 +7487 +7488 +7489 +7490 +7491 +7492 +7493 +7494 +7495 +7496 +7497 +7498 +7499 +7500 +7501 +7502 +7503 +7504 +7505 +7506 +7507 +7508 +7509 +7510 +7511 +7512 +7513 +7514 +7515 +7516 +7517 +7518 +7519 +7520 +7521 +7522 +7523 +7524 +7525 +7526 +7527 +7528 +7529 +7530 +7531 +7532 +7533 +7534 +7535 +7536 +7537 +7538 +7539 +7540 +7541 +7542 +7543 +7544 +7545 +7546 +7547 +7548 +7549 +7550 +7551 +7552 +7553 +7554 +7555 +7556 +7557 +7558 +7559 +7560 +7561 +7562 +7563 +7564 +7565 +7566 +7567 +7568 +7569 +7570 +7571 +7572 +7573 +7574 +7575 +7576 +7577 +7578 +7579 +7580 +7581 +7582 +7583 +7584 +7585 +7586 +7587 +7588 +7589 +7590 +7591 +7592 +7593 +7594 +7595 +7596 +7597 +7598 +7599 +7600 +7601 +7602 +7603 +7604 +7605 +7606 +7607 +7608 +7609 +7610 +7611 +7612 +7613 +7614 +7615 +7616 +7617 +7618 +7619 +7620 +7621 +7622 +7623 +7624 +7625 +7626 +7627 +7628 +7629 +7630 +7631 +7632 +7633 +7634 +7635 +7636 +7637 +7638 +7639 +7640 +7641 +7642 +7643 +7644 +7645 +7646 +7647 +7648 +7649 +7650 +7651 +7652 +7653 +7654 +7655 +7656 +7657 +7658 +7659 +7660 +7661 +7662 +7663 +7664 +7665 +7666 +7667 +7668 +7669 +7670 +7671 +7672 +7673 +7674 +7675 +7676 +7677 +7678 +7679 +7680 +7681 +7682 +7683 +7684 +7685 +7686 +7687 +7688 +7689 +7690 +7691 +7692 +7693 +7694 +7695 +7696 +7697 +7698 +7699 +7700 +7701 +7702 +7703 +7704 +7705 +7706 +7707 +7708 +7709 +7710 +7711 +7712 +7713 +7714 +7715 +7716 +7717 +7718 +7719 +7720 +7721 +7722 +7723 +7724 +7725 +7726 +7727 +7728 +7729 +7730 +7731 +7732 +7733 +7734 +7735 +7736 +7737 +7738 +7739 +7740 +7741 +7742 +7743 +7744 +7745 +7746 +7747 +7748 +7749 +7750 +7751 +7752 +7753 +7754 +7755 +7756 +7757 +7758 +7759 +7760 +7761 +7762 +7763 +7764 +7765 +7766 +7767 +7768 +7769 +7770 +7771 +7772 +7773 +7774 +7775 +7776 +7777 +7778 +7779 +7780 +7781 +7782 +7783 +7784 +7785 +7786 +7787 +7788 +7789 +7790 +7791 +7792 +7793 +7794 +7795 +7796 +7797 +7798 +7799 +7800 +7801 +7802 +7803 +7804 +7805 +7806 +7807 +7808 +7809 +7810 +7811 +7812 +7813 +7814 +7815 +7816 +7817 +7818 +7819 +7820 +7821 +7822 +7823 +7824 +7825 +7826 +7827 +7828 +7829 +7830 +7831 +7832 +7833 +7834 +7835 +7836 +7837 +7838 +7839 +7840 +7841 +7842 +7843 +7844 +7845 +7846 +7847 +7848 +7849 +7850 +7851 +7852 +7853 +7854 +7855 +7856 +7857 +7858 +7859 +7860 +7861 +7862 +7863 +7864 +7865 +7866 +7867 +7868 +7869 +7870 +7871 +7872 +7873 +7874 +7875 +7876 +7877 +7878 +7879 +7880 +7881 +7882 +7883 +7884 +7885 +7886 +7887 +7888 +7889 +7890 +7891 +7892 +7893 +7894 +7895 +7896 +7897 +7898 +7899 +7900 +7901 +7902 +7903 +7904 +7905 +7906 +7907 +7908 +7909 +7910 +7911 +7912 +7913 +7914 +7915 +7916 +7917 +7918 +7919 +7920 +7921 +7922 +7923 +7924 +7925 +7926 +7927 +7928 +7929 +7930 +7931 +7932 +7933 +7934 +7935 +7936 +7937 +7938 +7939 +7940 +7941 +7942 +7943 +7944 +7945 +7946 +7947 +7948 +7949 +7950 +7951 +7952 +7953 +7954 +7955 +7956 +7957 +7958 +7959 +7960 +7961 +7962 +7963 +7964 +7965 +7966 +7967 +7968 +7969 +7970 +7971 +7972 +7973 +7974 +7975 +7976 +7977 +7978 +7979 +7980 +7981 +7982 +7983 +7984 +7985 +7986 +7987 +7988 +7989 +7990 +7991 +7992 +7993 +7994 +7995 +7996 +7997 +7998 +7999 +8000 +8001 +8002 +8003 +8004 +8005 +8006 +8007 +8008 +8009 +8010 +8011 +8012 +8013 +8014 +8015 +8016 +8017 +8018 +8019 +8020 +8021 +8022 +8023 +8024 +8025 +8026 +8027 +8028 +8029 +8030 +8031 +8032 +8033 +8034 +8035 +8036 +8037 +8038 +8039 +8040 +8041 +8042 +8043 +8044 +8045 +8046 +8047 +8048 +8049 +8050 +8051 +8052 +8053 +8054 +8055 +8056 +8057 +8058 +8059 +8060 +8061 +8062 +8063 +8064 +8065 +8066 +8067 +8068 +8069 +8070 +8071 +8072 +8073 +8074 +8075 +8076 +8077 +8078 +8079 +8080 +8081 +8082 +8083 +8084 +8085 +8086 +8087 +8088 +8089 +8090 +8091 +8092 +8093 +8094 +8095 +8096 +8097 +8098 +8099 +8100 +8101 +8102 +8103 +8104 +8105 +8106 +8107 +8108 +8109 +8110 +8111 +8112 +8113 +8114 +8115 +8116 +8117 +8118 +8119 +8120 +8121 +8122 +8123 +8124 +8125 +8126 +8127 +8128 +8129 +8130 +8131 +8132 +8133 +8134 +8135 +8136 +8137 +8138 +8139 +8140 +8141 +8142 +8143 +8144 +8145 +8146 +8147 +8148 +8149 +8150 +8151 +8152 +8153 +8154 +8155 +8156 +8157 +8158 +8159 +8160 +8161 +8162 +8163 +8164 +8165 +8166 +8167 +8168 +8169 +8170 +8171 +8172 +8173 +8174 +8175 +8176 +8177 +8178 +8179 +8180 +8181 +8182 +8183 +8184 +8185 +8186 +8187 +8188 +8189 +8190 +8191 +8192 +8193 +8194 +8195 +8196 +8197 +8198 +8199 +8200 +8201 +8202 +8203 +8204 +8205 +8206 +8207 +8208 +8209 +8210 +8211 +8212 +8213 +8214 +8215 +8216 +8217 +8218 +8219 +8220 +8221 +8222 +8223 +8224 +8225 +8226 +8227 +8228 +8229 +8230 +8231 +8232 +8233 +8234 +8235 +8236 +8237 +8238 +8239 +8240 +8241 +8242 +8243 +8244 +8245 +8246 +8247 +8248 +8249 +8250 +8251 +8252 +8253 +8254 +8255 +8256 +8257 +8258 +8259 +8260 +8261 +8262 +8263 +8264 +8265 +8266 +8267 +8268 +8269 +8270 +8271 +8272 +8273 +8274 +8275 +8276 +8277 +8278 +8279 +8280 +8281 +8282 +8283 +8284 +8285 +8286 +8287 +8288 +8289 +8290 +8291 +8292 +8293 +8294 +8295 +8296 +8297 +8298 +8299 +8300 +8301 +8302 +8303 +8304 +8305 +8306 +8307 +8308 +8309 +8310 +8311 +8312 +8313 +8314 +8315 +8316 +8317 +8318 +8319 +8320 +8321 +8322 +8323 +8324 +8325 +8326 +8327 +8328 +8329 +8330 +8331 +8332 +8333 +8334 +8335 +8336 +8337 +8338 +8339 +8340 +8341 +8342 +8343 +8344 +8345 +8346 +8347 +8348 +8349 +8350 +8351 +8352 +8353 +8354 +8355 +8356 +8357 +8358 +8359 +8360 +8361 +8362 +8363 +8364 +8365 +8366 +8367 +8368 +8369 +8370 +8371 +8372 +8373 +8374 +8375 +8376 +8377 +8378 +8379 +8380 +8381 +8382 +8383 +8384 +8385 +8386 +8387 +8388 +8389 +8390 +8391 +8392 +8393 +8394 +8395 +8396 +8397 +8398 +8399 +8400 +8401 +8402 +8403 +8404 +8405 +8406 +8407 +8408 +8409 +8410 +8411 +8412 +8413 +8414 +8415 +8416 +8417 +8418 +8419 +8420 +8421 +8422 +8423 +8424 +8425 +8426 +8427 +8428 +8429 +8430 +8431 +8432 +8433 +8434 +8435 +8436 +8437 +8438 +8439 +8440 +8441 +8442 +8443 +8444 +8445 +8446 +8447 +8448 +8449 +8450 +8451 +8452 +8453 +8454 +8455 +8456 +8457 +8458 +8459 +8460 +8461 +8462 +8463 +8464 +8465 +8466 +8467 +8468 +8469 +8470 +8471 +8472 +8473 +8474 +8475 +8476 +8477 +8478 +8479 +8480 +8481 +8482 +8483 +8484 +8485 +8486 +8487 +8488 +8489 +8490 +8491 +8492 +8493 +8494 +8495 +8496 +8497 +8498 +8499 +8500 +8501 +8502 +8503 +8504 +8505 +8506 +8507 +8508 +8509 +8510 +8511 +8512 +8513 +8514 +8515 +8516 +8517 +8518 +8519 +8520 +8521 +8522 +8523 +8524 +8525 +8526 +8527 +8528 +8529 +8530 +8531 +8532 +8533 +8534 +8535 +8536 +8537 +8538 +8539 +8540 +8541 +8542 +8543 +8544 +8545 +8546 +8547 +8548 +8549 +8550 +8551 +8552 +8553 +8554 +8555 +8556 +8557 +8558 +8559 +8560 +8561 +8562 +8563 +8564 +8565 +8566 +8567 +8568 +8569 +8570 +8571 +8572 +8573 +8574 +8575 +8576 +8577 +8578 +8579 +8580 +8581 +8582 +8583 +8584 +8585 +8586 +8587 +8588 +8589 +8590 +8591 +8592 +8593 +8594 +8595 +8596 +8597 +8598 +8599 +8600 +8601 +8602 +8603 +8604 +8605 +8606 +8607 +8608 +8609 +8610 +8611 +8612 +8613 +8614 +8615 +8616 +8617 +8618 +8619 +8620 +8621 +8622 +8623 +8624 +8625 +8626 +8627 +8628 +8629 +8630 +8631 +8632 +8633 +8634 +8635 +8636 +8637 +8638 +8639 +8640 +8641 +8642 +8643 +8644 +8645 +8646 +8647 +8648 +8649 +8650 +8651 +8652 +8653 +8654 +8655 +8656 +8657 +8658 +8659 +8660 +8661 +8662 +8663 +8664 +8665 +8666 +8667 +8668 +8669 +8670 +8671 +8672 +8673 +8674 +8675 +8676 +8677 +8678 +8679 +8680 +8681 +8682 +8683 +8684 +8685 +8686 +8687 +8688 +8689 +8690 +8691 +8692 +8693 +8694 +8695 +8696 +8697 +8698 +8699 +8700 +8701 +8702 +8703 +8704 +8705 +8706 +8707 +8708 +8709 +8710 +8711 +8712 +8713 +8714 +8715 +8716 +8717 +8718 +8719 +8720 +8721 +8722 +8723 +8724 +8725 +8726 +8727 +8728 +8729 +8730 +8731 +8732 +8733 +8734 +8735 +8736 +8737 +8738 +8739 +8740 +8741 +8742 +8743 +8744 +8745 +8746 +8747 +8748 +8749 +8750 +8751 +8752 +8753 +8754 +8755 +8756 +8757 +8758 +8759 +8760 +8761 +8762 +8763 +8764 +8765 +8766 +8767 +8768 +8769 +8770 +8771 +8772 +8773 +8774 +8775 +8776 +8777 +8778 +8779 +8780 +8781 +8782 +8783 +8784 +8785 +8786 +8787 +8788 +8789 +8790 +8791 +8792 +8793 +8794 +8795 +8796 +8797 +8798 +8799 +8800 +8801 +8802 +8803 +8804 +8805 +8806 +8807 +8808 +8809 +8810 +8811 +8812 +8813 +8814 +8815 +8816 +8817 +8818 +8819 +8820 +8821 +8822 +8823 +8824 +8825 +8826 +8827 +8828 +8829 +8830 +8831 +8832 +8833 +8834 +8835 +8836 +8837 +8838 +8839 +8840 +8841 +8842 +8843 +8844 +8845 +8846 +8847 +8848 +8849 +8850 +8851 +8852 +8853 +8854 +8855 +8856 +8857 +8858 +8859 +8860 +8861 +8862 +8863 +8864 +8865 +8866 +8867 +8868 +8869 +8870 +8871 +8872 +8873 +8874 +8875 +8876 +8877 +8878 +8879 +8880 +8881 +8882 +8883 +8884 +8885 +8886 +8887 +8888 +8889 +8890 +8891 +8892 +8893 +8894 +8895 +8896 +8897 +8898 +8899 +8900 +8901 +8902 +8903 +8904 +8905 +8906 +8907 +8908 +8909 +8910 +8911 +8912 +8913 +8914 +8915 +8916 +8917 +8918 +8919 +8920 +8921 +8922 +8923 +8924 +8925 +8926 +8927 +8928 +8929 +8930 +8931 +8932 +8933 +8934 +8935 +8936 +8937 +8938 +8939 +8940 +8941 +8942 +8943 +8944 +8945 +8946 +8947 +8948 +8949 +8950 +8951 +8952 +8953 +8954 +8955 +8956 +8957 +8958 +8959 +8960 +8961 +8962 +8963 +8964 +8965 +8966 +8967 +8968 +8969 +8970 +8971 +8972 +8973 +8974 +8975 +8976 +8977 +8978 +8979 +8980 +8981 +8982 +8983 +8984 +8985 +8986 +8987 +8988 +8989 +8990 +8991 +8992 +8993 +8994 +8995 +8996 +8997 +8998 +8999 +9000 +9001 +9002 +9003 +9004 +9005 +9006 +9007 +9008 +9009 +9010 +9011 +9012 +9013 +9014 +9015 +9016 +9017 +9018 +9019 +9020 +9021 +9022 +9023 +9024 +9025 +9026 +9027 +9028 +9029 +9030 +9031 +9032 +9033 +9034 +9035 +9036 +9037 +9038 +9039 +9040 +9041 +9042 +9043 +9044 +9045 +9046 +9047 +9048 +9049 +9050 +9051 +9052 +9053 +9054 +9055 +9056 +9057 +9058 +9059 +9060 +9061 +9062 +9063 +9064 +9065 +9066 +9067 +9068 +9069 +9070 +9071 +9072 +9073 +9074 +9075 +9076 +9077 +9078 +9079 +9080 +9081 +9082 +9083 +9084 +9085 +9086 +9087 +9088 +9089 +9090 +9091 +9092 +9093 +9094 +9095 +9096 +9097 +9098 +9099 +9100 +9101 +9102 +9103 +9104 +9105 +9106 +9107 +9108 +9109 +9110 +9111 +9112 +9113 +9114 +9115 +9116 +9117 +9118 +9119 +9120 +9121 +9122 +9123 +9124 +9125 +9126 +9127 +9128 +9129 +9130 +9131 +9132 +9133 +9134 +9135 +9136 +9137 +9138 +9139 +9140 +9141 +9142 +9143 +9144 +9145 +9146 +9147 +9148 +9149 +9150 +9151 +9152 +9153 +9154 +9155 +9156 +9157 +9158 +9159 +9160 +9161 +9162 +9163 +9164 +9165 +9166 +9167 +9168 +9169 +9170 +9171 +9172 +9173 +9174 +9175 +9176 +9177 +9178 +9179 +9180 +9181 +9182 +9183 +9184 +9185 +9186 +9187 +9188 +9189 +9190 +9191 +9192 +9193 +9194 +9195 +9196 +9197 +9198 +9199 +9200 +9201 +9202 +9203 +9204 +9205 +9206 +9207 +9208 +9209 +9210 +9211 +9212 +9213 +9214 +9215 +9216 +9217 +9218 +9219 +9220 +9221 +9222 +9223 +9224 +9225 +9226 +9227 +9228 +9229 +9230 +9231 +9232 +9233 +9234 +9235 +9236 +9237 +9238 +9239 +9240 +9241 +9242 +9243 +9244 +9245 +9246 +9247 +9248 +9249 +9250 +9251 +9252 +9253 +9254 +9255 +9256 +9257 +9258 +9259 +9260 +9261 +9262 +9263 +9264 +9265 +9266 +9267 +9268 +9269 +9270 +9271 +9272 +9273 +9274 +9275 +9276 +9277 +9278 +9279 +9280 +9281 +9282 +9283 +9284 +9285 +9286 +9287 +9288 +9289 +9290 +9291 +9292 +9293 +9294 +9295 +9296 +9297 +9298 +9299 +9300 +9301 +9302 +9303 +9304 +9305 +9306 +9307 +9308 +9309 +9310 +9311 +9312 +9313 +9314 +9315 +9316 +9317 +9318 +9319 +9320 +9321 +9322 +9323 +9324 +9325 +9326 +9327 +9328 +9329 +9330 +9331 +9332 +9333 +9334 +9335 +9336 +9337 +9338 +9339 +9340 +9341 +9342 +9343 +9344 +9345 +9346 +9347 +9348 +9349 +9350 +9351 +9352 +9353 +9354 +9355 +9356 +9357 +9358 +9359 +9360 +9361 +9362 +9363 +9364 +9365 +9366 +9367 +9368 +9369 +9370 +9371 +9372 +9373 +9374 +9375 +9376 +9377 +9378 +9379 +9380 +9381 +9382 +9383 +9384 +9385 +9386 +9387 +9388 +9389 +9390 +9391 +9392 +9393 +9394 +9395 +9396 +9397 +9398 +9399 +9400 +9401 +9402 +9403 +9404 +9405 +9406 +9407 +9408 +9409 +9410 +9411 +9412 +9413 +9414 +9415 +9416 +9417 +9418 +9419 +9420 +9421 +9422 +9423 +9424 +9425 +9426 +9427 +9428 +9429 +9430 +9431 +9432 +9433 +9434 +9435 +9436 +9437 +9438 +9439 +9440 +9441 +9442 +9443 +9444 +9445 +9446 +9447 +9448 +9449 +9450 +9451 +9452 +9453 +9454 +9455 +9456 +9457 +9458 +9459 +9460 +9461 +9462 +9463 +9464 +9465 +9466 +9467 +9468 +9469 +9470 +9471 +9472 +9473 +9474 +9475 +9476 +9477 +9478 +9479 +9480 +9481 +9482 +9483 +9484 +9485 +9486 +9487 +9488 +9489 +9490 +9491 +9492 +9493 +9494 +9495 +9496 +9497 +9498 +9499 +9500 +9501 +9502 +9503 +9504 +9505 +9506 +9507 +9508 +9509 +9510 +9511 +9512 +9513 +9514 +9515 +9516 +9517 +9518 +9519 +9520 +9521 +9522 +9523 +9524 +9525 +9526 +9527 +9528 +9529 +9530 +9531 +9532 +9533 +9534 +9535 +9536 +9537 +9538 +9539 +9540 +9541 +9542 +9543 +9544 +9545 +9546 +9547 +9548 +9549 +9550 +9551 +9552 +9553 +9554 +9555 +9556 +9557 +9558 +9559 +9560 +9561 +9562 +9563 +9564 +9565 +9566 +9567 +9568 +9569 +9570 +9571 +9572 +9573 +9574 +9575 +9576 +9577 +9578 +9579 +9580 +9581 +9582 +9583 +9584 +9585 +9586 +9587 +9588 +9589 +9590 +9591 +9592 +9593 +9594 +9595 +9596 +9597 +9598 +9599 +9600 +9601 +9602 +9603 +9604 +9605 +9606 +9607 +9608 +9609 +9610 +9611 +9612 +9613 +9614 +9615 +9616 +9617 +9618 +9619 +9620 +9621 +9622 +9623 +9624 +9625 +9626 +9627 +9628 +9629 +9630 +9631 +9632 +9633 +9634 +9635 +9636 +9637 +9638 +9639 +9640 +9641 +9642 +9643 +9644 +9645 +9646 +9647 +9648 +9649 +9650 +9651 +9652 +9653 +9654 +9655 +9656 +9657 +9658 +9659 +9660 +9661 +9662 +9663 +9664 +9665 +9666 +9667 +9668 +9669 +9670 +9671 +9672 +9673 +9674 +9675 +9676 +9677 +9678 +9679 +9680 +9681 +9682 +9683 +9684 +9685 +9686 +9687 +9688 +9689 +9690 +9691 +9692 +9693 +9694 +9695 +9696 +9697 +9698 +9699 +9700 +9701 +9702 +9703 +9704 +9705 +9706 +9707 +9708 +9709 +9710 +9711 +9712 +9713 +9714 +9715 +9716 +9717 +9718 +9719 +9720 +9721 +9722 +9723 +9724 +9725 +9726 +9727 +9728 +9729 +9730 +9731 +9732 +9733 +9734 +9735 +9736 +9737 +9738 +9739 +9740 +9741 +9742 +9743 +9744 +9745 +9746 +9747 +9748 +9749 +9750 +9751 +9752 +9753 +9754 +9755 +9756 +9757 +9758 +9759 +9760 +9761 +9762 +9763 +9764 +9765 +9766 +9767 +9768 +9769 +9770 +9771 +9772 +9773 +9774 +9775 +9776 +9777 +9778 +9779 +9780 +9781 +9782 +9783 +9784 +9785 +9786 +9787 +9788 +9789 +9790 +9791 +9792 +9793 +9794 +9795 +9796 +9797 +9798 +9799 +9800 +9801 +9802 +9803 +9804 +9805 +9806 +9807 +9808 +9809 +9810 +9811 +9812 +9813 +9814 +9815 +9816 +9817 +9818 +9819 +9820 +9821 +9822 +9823 +9824 +9825 +9826 +9827 +9828 +9829 +9830 +9831 +9832 +9833 +9834 +9835 +9836 +9837 +9838 +9839 +9840 +9841 +9842 +9843 +9844 +9845 +9846 +9847 +9848 +9849 +9850 +9851 +9852 +9853 +9854 +9855 +9856 +9857 +9858 +9859 +9860 +9861 +9862 +9863 +9864 +9865 +9866 +9867 +9868 +9869 +9870 +9871 +9872 +9873 +9874 +9875 +9876 +9877 +9878 +9879 +9880 +9881 +9882 +9883 +9884 +9885 +9886 +9887 +9888 +9889 +9890 +9891 +9892 +9893 +9894 +9895 +9896 +9897 +9898 +9899 +9900 +9901 +9902 +9903 +9904 +9905 +9906 +9907 +9908 +9909 +9910 +9911 +9912 +9913 +9914 +9915 +9916 +9917 +9918 +9919 +9920 +9921 +9922 +9923 +9924 +9925 +9926 +9927 +9928 +9929 +9930 +9931 +9932 +9933 +9934 +9935 +9936 +9937 +9938 +9939 +9940 +9941 +9942 +9943 +9944 +9945 +9946 +9947 +9948 +9949 +9950 +9951 +9952 +9953 +9954 +9955 +9956 +9957 +9958 +9959 +9960 +9961 +9962 +9963 +9964 +9965 +9966 +9967 +9968 +9969 +9970 +9971 +9972 +9973 +9974 +9975 +9976 +9977 +9978 +9979 +9980 +9981 +9982 +9983 +9984 +9985 +9986 +9987 +9988 +9989 +9990 +9991 +9992 +9993 +9994 +9995 +9996 +9997 +9998 +9999 +10000 +10001 +10002 +10003 +10004 +10005 +10006 +10007 +10008 +10009 +10010 +10011 +10012 +10013 +10014 +10015 +10016 +10017 +10018 +10019 +10020 +10021 +10022 +10023 +10024 +10025 +10026 +10027 +10028 +10029 +10030 +10031 +10032 +10033 +10034 +10035 +10036 +10037 +10038 +10039 +10040 +10041 +10042 +10043 +10044 +10045 +10046 +10047 +10048 +10049 +10050 +10051 +10052 +10053 +10054 +10055 +10056 +10057 +10058 +10059 +10060 +10061 +10062 +10063 +10064 +10065 +10066 +10067 +10068 +10069 +10070 +10071 +10072 +10073 +10074 +10075 +10076 +10077 +10078 +10079 +10080 +10081 +10082 +10083 +10084 +10085 +10086 +10087 +10088 +10089 +10090 +10091 +10092 +10093 +10094 +10095 +10096 +10097 +10098 +10099 +10100 +10101 +10102 +10103 +10104 +10105 +10106 +10107 +10108 +10109 +10110 +10111 +10112 +10113 +10114 +10115 +10116 +10117 +10118 +10119 +10120 +10121 +10122 +10123 +10124 +10125 +10126 +10127 +10128 +10129 +10130 +10131 +10132 +10133 +10134 +10135 +10136 +10137 +10138 +10139 +10140 +10141 +10142 +10143 +10144 +10145 +10146 +10147 +10148 +10149 +10150 +10151 +10152 +10153 +10154 +10155 +10156 +10157 +10158 +10159 +10160 +10161 +10162 +10163 +10164 +10165 +10166 +10167 +10168 +10169 +10170 +10171 +10172 +10173 +10174 +10175 +10176 +10177 +10178 +10179 +10180 +10181 +10182 +10183 +10184 +10185 +10186 +10187 +10188 +10189 +10190 +10191 +10192 +10193 +10194 +10195 +10196 +10197 +10198 +10199 +10200 +10201 +10202 +10203 +10204 +10205 +10206 +10207 +10208 +10209 +10210 +10211 +10212 +10213 +10214 +10215 +10216 +10217 +10218 +10219 +10220 +10221 +10222 +10223 +10224 +10225 +10226 +10227 +10228 +10229 +10230 +10231 +10232 +10233 +10234 +10235 +10236 +10237 +10238 +10239 +10240 +10241 +10242 +10243 +10244 +10245 +10246 +10247 +10248 +10249 +10250 +10251 +10252 +10253 +10254 +10255 +10256 +10257 +10258 +10259 +10260 +10261 +10262 +10263 +10264 +10265 +10266 +10267 +10268 +10269 +10270 +10271 +10272 +10273 +10274 +10275 +10276 +10277 +10278 +10279 +10280 +10281 +10282 +10283 +10284 +10285 +10286 +10287 +10288 +10289 +10290 +10291 +10292 +10293 +10294 +10295 +10296 +10297 +10298 +10299 +10300 +10301 +10302 +10303 +10304 +10305 +10306 +10307 +10308 +10309 +10310 +10311 +10312 +10313 +10314 +10315 +10316 +10317 +10318 +10319 +10320 +10321 +10322 +10323 +10324 +10325 +10326 +10327 +10328 +10329 +10330 +10331 +10332 +10333 +10334 +10335 +10336 +10337 +10338 +10339 +10340 +10341 +10342 +10343 +10344 +10345 +10346 +10347 +10348 +10349 +10350 +10351 +10352 +10353 +10354 +10355 +10356 +10357 +10358 +10359 +10360 +10361 +10362 +10363 +10364 +10365 +10366 +10367 +10368 +10369 +10370 +10371 +10372 +10373 +10374 +10375 +10376 +10377 +10378 +10379 +10380 +10381 +10382 +10383 +10384 +10385 +10386 +10387 +10388 +10389 +10390 +10391 +10392 +10393 +10394 +10395 +10396 +10397 +10398 +10399 +10400 +10401 +10402 +10403 +10404 +10405 +10406 +10407 +10408 +10409 +10410 +10411 +10412 +10413 +10414 +10415 +10416 +10417 +10418 +10419 +10420 +10421 +10422 +10423 +10424 +10425 +10426 +10427 +10428 +10429 +10430 +10431 +10432 +10433 +10434 +10435 +10436 +10437 +10438 +10439 +10440 +10441 +10442 +10443 +10444 +10445 +10446 +10447 +10448 +10449 +10450 +10451 +10452 +10453 +10454 +10455 +10456 +10457 +10458 +10459 +10460 +10461 +10462 +10463 +10464 +10465 +10466 +10467 +10468 +10469 +10470 +10471 +10472 +10473 +10474 +10475 +10476 +10477 +10478 +10479 +10480 +10481 +10482 +10483 +10484 +10485 +10486 +10487 +10488 +10489 +10490 +10491 +10492 +10493 +10494 +10495 +10496 +10497 +10498 +10499 +10500 +10501 +10502 +10503 +10504 +10505 +10506 +10507 +10508 +10509 +10510 +10511 +10512 +10513 +10514 +10515 +10516 +10517 +10518 +10519 +10520 +10521 +10522 +10523 +10524 +10525 +10526 +10527 +10528 +10529 +10530 +10531 +10532 +10533 +10534 +10535 +10536 +10537 +10538 +10539 +10540 +10541 +10542 +10543 +10544 +10545 +10546 +10547 +10548 +10549 +10550 +10551 +10552 +10553 +10554 +10555 +10556 +10557 +10558 +10559 +10560 +10561 +10562 +10563 +10564 +10565 +10566 +10567 +10568 +10569 +10570 +10571 +10572 +10573 +10574 +10575 +10576 +10577 +10578 +10579 +10580 +10581 +10582 +10583 +10584 +10585 +10586 +10587 +10588 +10589 +10590 +10591 +10592 +10593 +10594 +10595 +10596 +10597 +10598 +10599 +10600 +10601 +10602 +10603 +10604 +10605 +10606 +10607 +10608 +10609 +10610 +10611 +10612 +10613 +10614 +10615 +10616 +10617 +10618 +10619 +10620 +10621 +10622 +10623 +10624 +10625 +10626 +10627 +10628 +10629 +10630 +10631 +10632 +10633 +10634 +10635 +10636 +10637 +10638 +10639 +10640 +10641 +10642 +10643 +10644 +10645 +10646 +10647 +10648 +10649 +10650 +10651 +10652 +10653 +10654 +10655 +10656 +10657 +10658 +10659 +10660 +10661 +10662 +10663 +10664 +10665 +10666 +10667 +10668 +10669 +10670 +10671 +10672 +10673 +10674 +10675 +10676 +10677 +10678 +10679 +10680 +10681 +10682 +10683 +10684 +10685 +10686 +10687 +10688 +10689 +10690 +10691 +10692 +10693 +10694 +10695 +10696 +10697 +10698 +10699 +10700 +10701 +10702 +10703 +10704 +10705 +10706 +10707 +10708 +10709 +10710 +10711 +10712 +10713 +10714 +10715 +10716 +10717 +10718 +10719 +10720 +10721 +10722 +10723 +10724 +10725 +10726 +10727 +10728 +10729 +10730 +10731 +10732 +10733 +10734 +10735 +10736 +10737 +10738 +10739 +10740 +10741 +10742 +10743 +10744 +10745 +10746 +10747 +10748 +10749 +10750 +10751 +10752 +10753 +10754 +10755 +10756 +10757 +10758 +10759 +10760 +10761 +10762 +10763 +10764 +10765 +10766 +10767 +10768 +10769 +10770 +10771 +10772 +10773 +10774 +10775 +10776 +10777 +10778 +10779 +10780 +10781 +10782 +10783 +10784 +10785 +10786 +10787 +10788 +10789 +10790 +10791 +10792 +10793 +10794 +10795 +10796 +10797 +10798 +10799 +10800 +10801 +10802 +10803 +10804 +10805 +10806 +10807 +10808 +10809 +10810 +10811 +10812 +10813 +10814 +10815 +10816 +10817 +10818 +10819 +10820 +10821 +10822 +10823 +10824 +10825 +10826 +10827 +10828 +10829 +10830 +10831 +10832 +10833 +10834 +10835 +10836 +10837 +10838 +10839 +10840 +10841 +10842 +10843 +10844 +10845 +10846 +10847 +10848 +10849 +10850 +10851 +10852 +10853 +10854 +10855 +10856 +10857 +10858 +10859 +10860 +10861 +10862 +10863 +10864 +10865 +10866 +10867 +10868 +10869 +10870 +10871 +10872 +10873 +10874 +10875 +10876 +10877 +10878 +10879 +10880 +10881 +10882 +10883 +10884 +10885 +10886 +10887 +10888 +10889 +10890 +10891 +10892 +10893 +10894 +10895 +10896 +10897 +10898 +10899 +10900 +10901 +10902 +10903 +10904 +10905 +10906 +10907 +10908 +10909 +10910 +10911 +10912 +10913 +10914 +10915 +10916 +10917 +10918 +10919 +10920 +10921 +10922 +10923 +10924 +10925 +10926 +10927 +10928 +10929 +10930 +10931 +10932 +10933 +10934 +10935 +10936 +10937 +10938 +10939 +10940 +10941 +10942 +10943 +10944 +10945 +10946 +10947 +10948 +10949 +10950 +10951 +10952 +10953 +10954 +10955 +10956 +10957 +10958 +10959 +10960 +10961 +10962 +10963 +10964 +10965 +10966 +10967 +10968 +10969 +10970 +10971 +10972 +10973 +10974 +10975 +10976 +10977 +10978 +10979 +10980 +10981 +10982 +10983 +10984 +10985 +10986 +10987 +10988 +10989 +10990 +10991 +10992 +10993 +10994 +10995 +10996 +10997 +10998 +10999 +11000 +11001 +11002 +11003 +11004 +11005 +11006 +11007 +11008 +11009 +11010 +11011 +11012 +11013 +11014 +11015 +11016 +11017 +11018 +11019 +11020 +11021 +11022 +11023 +11024 +11025 +11026 +11027 +11028 +11029 +11030 +11031 +11032 +11033 +11034 +11035 +11036 +11037 +11038 +11039 +11040 +11041 +11042 +11043 +11044 +11045 +11046 +11047 +11048 +11049 +11050 +11051 +11052 +11053 +11054 +11055 +11056 +11057 +11058 +11059 +11060 +11061 +11062 +11063 +11064 +11065 +11066 +11067 +11068 +11069 +11070 +11071 +11072 +11073 +11074 +11075 +11076 +11077 +11078 +11079 +11080 +11081 +11082 +11083 +11084 +11085 +11086 +11087 +11088 +11089 +11090 +11091 +11092 +11093 +11094 +11095 +11096 +11097 +11098 +11099 +11100 +11101 +11102 +11103 +11104 +11105 +11106 +11107 +11108 +11109 +11110 +11111 +11112 +11113 +11114 +11115 +11116 +11117 +11118 +11119 +11120 +11121 +11122 +11123 +11124 +11125 +11126 +11127 +11128 +11129 +11130 +11131 +11132 +11133 +11134 +11135 +11136 +11137 +11138 +11139 +11140 +11141 +11142 +11143 +11144 +11145 +11146 +11147 +11148 +11149 +11150 +11151 +11152 +11153 +11154 +11155 +11156 +11157 +11158 +11159 +11160 +11161 +11162 +11163 +11164 +11165 +11166 +11167 +11168 +11169 +11170 +11171 +11172 +11173 +11174 +11175 +11176 +11177 +11178 +11179 +11180 +11181 +11182 +11183 +11184 +11185 +11186 +11187 +11188 +11189 +11190 +11191 +11192 +11193 +11194 +11195 +11196 +11197 +11198 +11199 +11200 +11201 +11202 +11203 +11204 +11205 +11206 +11207 +11208 +11209 +11210 +11211 +11212 +11213 +11214 +11215 +11216 +11217 +11218 +11219 +11220 +11221 +11222 +11223 +11224 +11225 +11226 +11227 +11228 +11229 +11230 +11231 +11232 +11233 +11234 +11235 +11236 +11237 +11238 +11239 +11240 +11241 +11242 +11243 +11244 +11245 +11246 +11247 +11248 +11249 +11250 +11251 +11252 +11253 +11254 +11255 +11256 +11257 +11258 +11259 +11260 +11261 +11262 +11263 +11264 +11265 +11266 +11267 +11268 +11269 +11270 +11271 +11272 +11273 +11274 +11275 +11276 +11277 +11278 +11279 +11280 +11281 +11282 +11283 +11284 +11285 +11286 +11287 +11288 +11289 +11290 +11291 +11292 +11293 +11294 +11295 +11296 +11297 +11298 +11299 +11300 +11301 +11302 +11303 +11304 +11305 +11306 +11307 +11308 +11309 +11310 +11311 +11312 +11313 +11314 +11315 +11316 +11317 +11318 +11319 +11320 +11321 +11322 +11323 +11324 +11325 +11326 +11327 +11328 +11329 +11330 +11331 +11332 +11333 +11334 +11335 +11336 +11337 +11338 +11339 +11340 +11341 +11342 +11343 +11344 +11345 +11346 +11347 +11348 +11349 +11350 +11351 +11352 +11353 +11354 +11355 +11356 +11357 +11358 +11359 +11360 +11361 +11362 +11363 +11364 +11365 +11366 +11367 +11368 +11369 +11370 +11371 +11372 +11373 +11374 +11375 +11376 +11377 +11378 +11379 +11380 +11381 +11382 +11383 +11384 +11385 +11386 +11387 +11388 +11389 +11390 +11391 +11392 +11393 +11394 +11395 +11396 +11397 +11398 +11399 +11400 +11401 +11402 +11403 +11404 +11405 +11406 +11407 +11408 +11409 +11410 +11411 +11412 +11413 +11414 +11415 +11416 +11417 +11418 +11419 +11420 +11421 +11422 +11423 +11424 +11425 +11426 +11427 +11428 +11429 +11430 +11431 +11432 +11433 +11434 +11435 +11436 +11437 +11438 +11439 +11440 +11441 +11442 +11443 +11444 +11445 +11446 +11447 +11448 +11449 +11450 +11451 +11452 +11453 +11454 +11455 +11456 +11457 +11458 +11459 +11460 +11461 +11462 +11463 +11464 +11465 +11466 +11467 +11468 +11469 +11470 +11471 +11472 +11473 +11474 +11475 +11476 +11477 +11478 +11479 +11480 +11481 +11482 +11483 +11484 +11485 +11486 +11487 +11488 +11489 +11490 +11491 +11492 +11493 +11494 +11495 +11496 +11497 +11498 +11499 +11500 +11501 +11502 +11503 +11504 +11505 +11506 +11507 +11508 +11509 +11510 +11511 +11512 +11513 +11514 +11515 +11516 +11517 +11518 +11519 +11520 +11521 +11522 +11523 +11524 +11525 +11526 +11527 +11528 +11529 +11530 +11531 +11532 +11533 +11534 +11535 +11536 +11537 +11538 +11539 +11540 +11541 +11542 +11543 +11544 +11545 +11546 +11547 +11548 +11549 +11550 +11551 +11552 +11553 +11554 +11555 +11556 +11557 +11558 +11559 +11560 +11561 +11562 +11563 +11564 +11565 +11566 +11567 +11568 +11569 +11570 +11571 +11572 +11573 +11574 +11575 +11576 +11577 +11578 +11579 +11580 +11581 +11582 +11583 +11584 +11585 +11586 +11587 +11588 +11589 +11590 +11591 +11592 +11593 +11594 +11595 +11596 +11597 +11598 +11599 +11600 +11601 +11602 +11603 +11604 +11605 +11606 +11607 +11608 +11609 +11610 +11611 +11612 +11613 +11614 +11615 +11616 +11617 +11618 +11619 +11620 +11621 +11622 +11623 +11624 +11625 +11626 +11627 +11628 +11629 +11630 +11631 +11632 +11633 +11634 +11635 +11636 +11637 +11638 +11639 +11640 +11641 +11642 +11643 +11644 +11645 +11646 +11647 +11648 +11649 +11650 +11651 +11652 +11653 +11654 +11655 +11656 +11657 +11658 +11659 +11660 +11661 +11662 +11663 +11664 +11665 +11666 +11667 +11668 +11669 +11670 +11671 +11672 +11673 +11674 +11675 +11676 +11677 +11678 +11679 +11680 +11681 +11682 +11683 +11684 +11685 +11686 +11687 +11688 +11689 +11690 +11691 +11692 +11693 +11694 +11695 +11696 +11697 +11698 +11699 +11700 +11701 +11702 +11703 +11704 +11705 +11706 +11707 +11708 +11709 +11710 +11711 +11712 +11713 +11714 +11715 +11716 +11717 +11718 +11719 +11720 +11721 +11722 +11723 +11724 +11725 +11726 +11727 +11728 +11729 +11730 +11731 +11732 +11733 +11734 +11735 +11736 +11737 +11738 +11739 +11740 +11741 +11742 +11743 +11744 +11745 +11746 +11747 +11748 +11749 +11750 +11751 +11752 +11753 +11754 +11755 +11756 +11757 +11758 +11759 +11760 +11761 +11762 +11763 +11764 +11765 +11766 +11767 +11768 +11769 +11770 +11771 +11772 +11773 +11774 +11775 +11776 +11777 +11778 +11779 +11780 +11781 +11782 +11783 +11784 +11785 +11786 +11787 +11788 +11789 +11790 +11791 +11792 +11793 +11794 +11795 +11796 +11797 +11798 +11799 +11800 +11801 +11802 +11803 +11804 +11805 +11806 +11807 +11808 +11809 +11810 +11811 +11812 +11813 +11814 +11815 +11816 +11817 +11818 +11819 +11820 +11821 +11822 +11823 +11824 +11825 +11826 +11827 +11828 +11829 +11830 +11831 +11832 +11833 +11834 +11835 +11836 +11837 +11838 +11839 +11840 +11841 +11842 +11843 +11844 +11845 +11846 +11847 +11848 +11849 +11850 +11851 +11852 +11853 +11854 +11855 +11856 +11857 +11858 +11859 +11860 +11861 +11862 +11863 +11864 +11865 +11866 +11867 +11868 +11869 +11870 +11871 +11872 +11873 +11874 +11875 +11876 +11877 +11878 +11879 +11880 +11881 +11882 +11883 +11884 +11885 +11886 +11887 +11888 +11889 +11890 +11891 +11892 +11893 +11894 +11895 +11896 +11897 +11898 +11899 +11900 +11901 +11902 +11903 +11904 +11905 +11906 +11907 +11908 +11909 +11910 +11911 +11912 +11913 +11914 +11915 +11916 +11917 +11918 +11919 +11920 +11921 +11922 +11923 +11924 +11925 +11926 +11927 +11928 +11929 +11930 +11931 +11932 +11933 +11934 +11935 +11936 +11937 +11938 +11939 +11940 +11941 +11942 +11943 +11944 +11945 +11946 +11947 +11948 +11949 +11950 +11951 +11952 +11953 +11954 +11955 +11956 +11957 +11958 +11959 +11960 +11961 +11962 +11963 +11964 +11965 +11966 +11967 +11968 +11969 +11970 +11971 +11972 +11973 +11974 +11975 +11976 +11977 +11978 +11979 +11980 +11981 +11982 +11983 +11984 +11985 +11986 +11987 +11988 +11989 +11990 +11991 +11992 +11993 +11994 +11995 +11996 +11997 +11998 +11999 +12000 +12001 +12002 +12003 +12004 +12005 +12006 +12007 +12008 +12009 +12010 +12011 +12012 +12013 +12014 +12015 +12016 +12017 +12018 +12019 +12020 +12021 +12022 +12023 +12024 +12025 +12026 +12027 +12028 +12029 +12030 +12031 +12032 +12033 +12034 +12035 +12036 +12037 +12038 +12039 +12040 +12041 +12042 +12043 +12044 +12045 +12046 +12047 +12048 +12049 +12050 +12051 +12052 +12053 +12054 +12055 +12056 +12057 +12058 +12059 +12060 +12061 +12062 +12063 +12064 +12065 +12066 +12067 +12068 +12069 +12070 +12071 +12072 +12073 +12074 +12075 +12076 +12077 +12078 +12079 +12080 +12081 +12082 +12083 +12084 +12085 +12086 +12087 +12088 +12089 +12090 +12091 +12092 +12093 +12094 +12095 +12096 +12097 +12098 +12099 +12100 +12101 +12102 +12103 +12104 +12105 +12106 +12107 +12108 +12109 +12110 +12111 +12112 +12113 +12114 +12115 +12116 +12117 +12118 +12119 +12120 +12121 +12122 +12123 +12124 +12125 +12126 +12127 +12128 +12129 +12130 +12131 +12132 +12133 +12134 +12135 +12136 +12137 +12138 +12139 +12140 +12141 +12142 +12143 +12144 +12145 +12146 +12147 +12148 +12149 +12150 +12151 +12152 +12153 +12154 +12155 +12156 +12157 +12158 +12159 +12160 +12161 +12162 +12163 +12164 +12165 +12166 +12167 +12168 +12169 +12170 +12171 +12172 +12173 +12174 +12175 +12176 +12177 +12178 +12179 +12180 +12181 +12182 +12183 +12184 +12185 +12186 +12187 +12188 +12189 +12190 +12191 +12192 +12193 +12194 +12195 +12196 +12197 +12198 +12199 +12200 +12201 +12202 +12203 +12204 +12205 +12206 +12207 +12208 +12209 +12210 +12211 +12212 +12213 +12214 +12215 +12216 +12217 +12218 +12219 +12220 +12221 +12222 +12223 +12224 +12225 +12226 +12227 +12228 +12229 +12230 +12231 +12232 +12233 +12234 +12235 +12236 +12237 +12238 +12239 +12240 +12241 +12242 +12243 +12244 +12245 +12246 +12247 +12248 +12249 +12250 +12251 +12252 +12253 +12254 +12255 +12256 +12257 +12258 +12259 +12260 +12261 +12262 +12263 +12264 +12265 +12266 +12267 +12268 +12269 +12270 +12271 +12272 +12273 +12274 +12275 +12276 +12277 +12278 +12279 +12280 +12281 +12282 +12283 +12284 +12285 +12286 +12287 +12288 +12289 +12290 +12291 +12292 +12293 +12294 +12295 +12296 +12297 +12298 +12299 +12300 +12301 +12302 +12303 +12304 +12305 +12306 +12307 +12308 +12309 +12310 +12311 +12312 +12313 +12314 +12315 +12316 +12317 +12318 +12319 +12320 +12321 +12322 +12323 +12324 +12325 +12326 +12327 +12328 +12329 +12330 +12331 +12332 +12333 +12334 +12335 +12336 +12337 +12338 +12339 +12340 +12341 +12342 +12343 +12344 +12345 +12346 +12347 +12348 +12349 +12350 +12351 +12352 +12353 +12354 +12355 +12356 +12357 +12358 +12359 +12360 +12361 +12362 +12363 +12364 +12365 +12366 +12367 +12368 +12369 +12370 +12371 +12372 +12373 +12374 +12375 +12376 +12377 +12378 +12379 +12380 +12381 +12382 +12383 +12384 +12385 +12386 +12387 +12388 +12389 +12390 +12391 +12392 +12393 +12394 +12395 +12396 +12397 +12398 +12399 +12400 +12401 +12402 +12403 +12404 +12405 +12406 +12407 +12408 +12409 +12410 +12411 +12412 +12413 +12414 +12415 +12416 +12417 +12418 +12419 +12420 +12421 +12422 +12423 +12424 +12425 +12426 +12427 +12428 +12429 +12430 +12431 +12432 +12433 +12434 +12435 +12436 +12437 +12438 +12439 +12440 +12441 +12442 +12443 +12444 +12445 +12446 +12447 +12448 +12449 +12450 +12451 +12452 +12453 +12454 +12455 +12456 +12457 +12458 +12459 +12460 +12461 +12462 +12463 +12464 +12465 +12466 +12467 +12468 +12469 +12470 +12471 +12472 +12473 +12474 +12475 +12476 +12477 +12478 +12479 +12480 +12481 +12482 +12483 +12484 +12485 +12486 +12487 +12488 +12489 +12490 +12491 +12492 +12493 +12494 +12495 +12496 +12497 +12498 +12499 +12500 +12501 +12502 +12503 +12504 +12505 +12506 +12507 +12508 +12509 +12510 +12511 +12512 +12513 +12514 +12515 +12516 +12517 +12518 +12519 +12520 +12521 +12522 +12523 +12524 +12525 +12526 +12527 +12528 +12529 +12530 +12531 +12532 +12533 +12534 +12535 +12536 +12537 +12538 +12539 +12540 +12541 +12542 +12543 +12544 +12545 +12546 +12547 +12548 +12549 +12550 +12551 +12552 +12553 +12554 +12555 +12556 +12557 +12558 +12559 +12560 +12561 +12562 +12563 +12564 +12565 +12566 +12567 +12568 +12569 +12570 +12571 +12572 +12573 +12574 +12575 +12576 +12577 +12578 +12579 +12580 +12581 +12582 +12583 +12584 +12585 +12586 +12587 +12588 +12589 +12590 +12591 +12592 +12593 +12594 +12595 +12596 +12597 +12598 +12599 +12600 +12601 +12602 +12603 +12604 +12605 +12606 +12607 +12608 +12609 +12610 +12611 +12612 +12613 +12614 +12615 +12616 +12617 +12618 +12619 +12620 +12621 +12622 +12623 +12624 +12625 +12626 +12627 +12628 +12629 +12630 +12631 +12632 +12633 +12634 +12635 +12636 +12637 +12638 +12639 +12640 +12641 +12642 +12643 +12644 +12645 +12646 +12647 +12648 +12649 +12650 +12651 +12652 +12653 +12654 +12655 +12656 +12657 +12658 +12659 +12660 +12661 +12662 +12663 +12664 +12665 +12666 +12667 +12668 +12669 +12670 +12671 +12672 +12673 +12674 +12675 +12676 +12677 +12678 +12679 +12680 +12681 +12682 +12683 +12684 +12685 +12686 +12687 +12688 +12689 +12690 +12691 +12692 +12693 +12694 +12695 +12696 +12697 +12698 +12699 +12700 +12701 +12702 +12703 +12704 +12705 +12706 +12707 +12708 +12709 +12710 +12711 +12712 +12713 +12714 +12715 +12716 +12717 +12718 +12719 +12720 +12721 +12722 +12723 +12724 +12725 +12726 +12727 +12728 +12729 +12730 +12731 +12732 +12733 +12734 +12735 +12736 +12737 +12738 +12739 +12740 +12741 +12742 +12743 +12744 +12745 +12746 +12747 +12748 +12749 +12750 +12751 +12752 +12753 +12754 +12755 +12756 +12757 +12758 +12759 +12760 +12761 +12762 +12763 +12764 +12765 +12766 +12767 +12768 +12769 +12770 +12771 +12772 +12773 +12774 +12775 +12776 +12777 +12778 +12779 +12780 +12781 +12782 +12783 +12784 +12785 +12786 +12787 +12788 +12789 +12790 +12791 +12792 +12793 +12794 +12795 +12796 +12797 +12798 +12799 +12800 +12801 +12802 +12803 +12804 +12805 +12806 +12807 +12808 +12809 +12810 +12811 +12812 +12813 +12814 +12815 +12816 +12817 +12818 +12819 +12820 +12821 +12822 +12823 +12824 +12825 +12826 +12827 +12828 +12829 +12830 +12831 +12832 +12833 +12834 +12835 +12836 +12837 +12838 +12839 +12840 +12841 +12842 +12843 +12844 +12845 +12846 +12847 +12848 +12849 +12850 +12851 +12852 +12853 +12854 +12855 +12856 +12857 +12858 +12859 +12860 +12861 +12862 +12863 +12864 +12865 +12866 +12867 +12868 +12869 +12870 +12871 +12872 +12873 +12874 +12875 +12876 +12877 +12878 +12879 +12880 +12881 +12882 +12883 +12884 +12885 +12886 +12887 +12888 +12889 +12890 +12891 +12892 +12893 +12894 +12895 +12896 +12897 +12898 +12899 +12900 +12901 +12902 +12903 +12904 +12905 +12906 +12907 +12908 +12909 +12910 +12911 +12912 +12913 +12914 +12915 +12916 +12917 +12918 +12919 +12920 +12921 +12922 +12923 +12924 +12925 +12926 +12927 +12928 +12929 +12930 +12931 +12932 +12933 +12934 +12935 +12936 +12937 +12938 +12939 +12940 +12941 +12942 +12943 +12944 +12945 +12946 +12947 +12948 +12949 +12950 +12951 +12952 +12953 +12954 +12955 +12956 +12957 +12958 +12959 +12960 +12961 +12962 +12963 +12964 +12965 +12966 +12967 +12968 +12969 +12970 +12971 +12972 +12973 +12974 +12975 +12976 +12977 +12978 +12979 +12980 +12981 +12982 +12983 +12984 +12985 +12986 +12987 +12988 +12989 +12990 +12991 +12992 +12993 +12994 +12995 +12996 +12997 +12998 +12999 +13000 +13001 +13002 +13003 +13004 +13005 +13006 +13007 +13008 +13009 +13010 +13011 +13012 +13013 +13014 +13015 +13016 +13017 +13018 +13019 +13020 +13021 +13022 +13023 +13024 +13025 +13026 +13027 +13028 +13029 +13030 +13031 +13032 +13033 +13034 +13035 +13036 +13037 +13038 +13039 +13040 +13041 +13042 +13043 +13044 +13045 +13046 +13047 +13048 +13049 +13050 +13051 +13052 +13053 +13054 +13055 +13056 +13057 +13058 +13059 +13060 +13061 +13062 +13063 +13064 +13065 +13066 +13067 +13068 +13069 +13070 +13071 +13072 +13073 +13074 +13075 +13076 +13077 +13078 +13079 +13080 +13081 +13082 +13083 +13084 +13085 +13086 +13087 +13088 +13089 +13090 +13091 +13092 +13093 +13094 +13095 +13096 +13097 +13098 +13099 +13100 +13101 +13102 +13103 +13104 +13105 +13106 +13107 +13108 +13109 +13110 +13111 +13112 +13113 +13114 +13115 +13116 +13117 +13118 +13119 +13120 +13121 +13122 +13123 +13124 +13125 +13126 +13127 +13128 +13129 +13130 +13131 +13132 +13133 +13134 +13135 +13136 +13137 +13138 +13139 +13140 +13141 +13142 +13143 +13144 +13145 +13146 +13147 +13148 +13149 +13150 +13151 +13152 +13153 +13154 +13155 +13156 +13157 +13158 +13159 +13160 +13161 +13162 +13163 +13164 +13165 +13166 +13167 +13168 +13169 +13170 +13171 +13172 +13173 +13174 +13175 +13176 +13177 +13178 +13179 +13180 +13181 +13182 +13183 +13184 +13185 +13186 +13187 +13188 +13189 +13190 +13191 +13192 +13193 +13194 +13195 +13196 +13197 +13198 +13199 +13200 +13201 +13202 +13203 +13204 +13205 +13206 +13207 +13208 +13209 +13210 +13211 +13212 +13213 +13214 +13215 +13216 +13217 +13218 +13219 +13220 +13221 +13222 +13223 +13224 +13225 +13226 +13227 +13228 +13229 +13230 +13231 +13232 +13233 +13234 +13235 +13236 +13237 +13238 +13239 +13240 +13241 +13242 +13243 +13244 +13245 +13246 +13247 +13248 +13249 +13250 +13251 +13252 +13253 +13254 +13255 +13256 +13257 +13258 +13259 +13260 +13261 +13262 +13263 +13264 +13265 +13266 +13267 +13268 +13269 +13270 +13271 +13272 +13273 +13274 +13275 +13276 +13277 +13278 +13279 +13280 +13281 +13282 +13283 +13284 +13285 +13286 +13287 +13288 +13289 +13290 +13291 +13292 +13293 +13294 +13295 +13296 +13297 +13298 +13299 +13300 +13301 +13302 +13303 +13304 +13305 +13306 +13307 +13308 +13309 +13310 +13311 +13312 +13313 +13314 +13315 +13316 +13317 +13318 +13319 +13320 +13321 +13322 +13323 +13324 +13325 +13326 +13327 +13328 +13329 +13330 +13331 +13332 +13333 +13334 +13335 +13336 +13337 +13338 +13339 +13340 +13341 +13342 +13343 +13344 +13345 +13346 +13347 +13348 +13349 +13350 +13351 +13352 +13353 +13354 +13355 +13356 +13357 +13358 +13359 +13360 +13361 +13362 +13363 +13364 +13365 +13366 +13367 +13368 +13369 +13370 +13371 +13372 +13373 +13374 +13375 +13376 +13377 +13378 +13379 +13380 +13381 +13382 +13383 +13384 +13385 +13386 +13387 +13388 +13389 +13390 +13391 +13392 +13393 +13394 +13395 +13396 +13397 +13398 +13399 +13400 +13401 +13402 +13403 +13404 +13405 +13406 +13407 +13408 +13409 +13410 +13411 +13412 +13413 +13414 +13415 +13416 +13417 +13418 +13419 +13420 +13421 +13422 +13423 +13424 +13425 +13426 +13427 +13428 +13429 +13430 +13431 +13432 +13433 +13434 +13435 +13436 +13437 +13438 +13439 +13440 +13441 +13442 +13443 +13444 +13445 +13446 +13447 +13448 +13449 +13450 +13451 +13452 +13453 +13454 +13455 +13456 +13457 +13458 +13459 +13460 +13461 +13462 +13463 +13464 +13465 +13466 +13467 +13468 +13469 +13470 +13471 +13472 +13473 +13474 +13475 +13476 +13477 +13478 +13479 +13480 +13481 +13482 +13483 +13484 +13485 +13486 +13487 +13488 +13489 +13490 +13491 +13492 +13493 +13494 +13495 +13496 +13497 +13498 +13499 +13500 +13501 +13502 +13503 +13504 +13505 +13506 +13507 +13508 +13509 +13510 +13511 +13512 +13513 +13514 +13515 +13516 +13517 +13518 +13519 +13520 +13521 +13522 +13523 +13524 +13525 +13526 +13527 +13528 +13529 +13530 +13531 +13532 +13533 +13534 +13535 +13536 +13537 +13538 +13539 +13540 +13541 +13542 +13543 +13544 +13545 +13546 +13547 +13548 +13549 +13550 +13551 +13552 +13553 +13554 +13555 +13556 +13557 +13558 +13559 +13560 +13561 +13562 +13563 +13564 +13565 +13566 +13567 +13568 +13569 +13570 +13571 +13572 +13573 +13574 +13575 +13576 +13577 +13578 +13579 +13580 +13581 +13582 +13583 +13584 +13585 +13586 +13587 +13588 +13589 +13590 +13591 +13592 +13593 +13594 +13595 +13596 +13597 +13598 +13599 +13600 +13601 +13602 +13603 +13604 +13605 +13606 +13607 +13608 +13609 +13610 +13611 +13612 +13613 +13614 +13615 +13616 +13617 +13618 +13619 +13620 +13621 +13622 +13623 +13624 +13625 +13626 +13627 +13628 +13629 +13630 +13631 +13632 +13633 +13634 +13635 +13636 +13637 +13638 +13639 +13640 +13641 +13642 +13643 +13644 +13645 +13646 +13647 +13648 +13649 +13650 +13651 +13652 +13653 +13654 +13655 +13656 +13657 +13658 +13659 +13660 +13661 +13662 +13663 +13664 +13665 +13666 +13667 +13668 +13669 +13670 +13671 +13672 +13673 +13674 +13675 +13676 +13677 +13678 +13679 +13680 +13681 +13682 +13683 +13684 +13685 +13686 +13687 +13688 +13689 +13690 +13691 +13692 +13693 +13694 +13695 +13696 +13697 +13698 +13699 +13700 +13701 +13702 +13703 +13704 +13705 +13706 +13707 +13708 +13709 +13710 +13711 +13712 +13713 +13714 +13715 +13716 +13717 +13718 +13719 +13720 +13721 +13722 +13723 +13724 +13725 +13726 +13727 +13728 +13729 +13730 +13731 +13732 +13733 +13734 +13735 +13736 +13737 +13738 +13739 +13740 +13741 +13742 +13743 +13744 +13745 +13746 +13747 +13748 +13749 +13750 +13751 +13752 +13753 +13754 +13755 +13756 +13757 +13758 +13759 +13760 +13761 +13762 +13763 +13764 +13765 +13766 +13767 +13768 +13769 +13770 +13771 +13772 +13773 +13774 +13775 +13776 +13777 +13778 +13779 +13780 +13781 +13782 +13783 +13784 +13785 +13786 +13787 +13788 +13789 +13790 +13791 +13792 +13793 +13794 +13795 +13796 +13797 +13798 +13799 +13800 +13801 +13802 +13803 +13804 +13805 +13806 +13807 +13808 +13809 +13810 +13811 +13812 +13813 +13814 +13815 +13816 +13817 +13818 +13819 +13820 +13821 +13822 +13823 +13824 +13825 +13826 +13827 +13828 +13829 +13830 +13831 +13832 +13833 +13834 +13835 +13836 +13837 +13838 +13839 +13840 +13841 +13842 +13843 +13844 +13845 +13846 +13847 +13848 +13849 +13850 +13851 +13852 +13853 +13854 +13855 +13856 +13857 +13858 +13859 +13860 +13861 +13862 +13863 +13864 +13865 +13866 +13867 +13868 +13869 +13870 +13871 +13872 +13873 +13874 +13875 +13876 +13877 +13878 +13879 +13880 +13881 +13882 +13883 +13884 +13885 +13886 +13887 +13888 +13889 +13890 +13891 +13892 +13893 +13894 +13895 +13896 +13897 +13898 +13899 +13900 +13901 +13902 +13903 +13904 +13905 +13906 +13907 +13908 +13909 +13910 +13911 +13912 +13913 +13914 +13915 +13916 +13917 +13918 +13919 +13920 +13921 +13922 +13923 +13924 +13925 +13926 +13927 +13928 +13929 +13930 +13931 +13932 +13933 +13934 +13935 +13936 +13937 +13938 +13939 +13940 +13941 +13942 +13943 +13944 +13945 +13946 +13947 +13948 +13949 +13950 +13951 +13952 +13953 +13954 +13955 +13956 +13957 +13958 +13959 +13960 +13961 +13962 +13963 +13964 +13965 +13966 +13967 +13968 +13969 +13970 +13971 +13972 +13973 +13974 +13975 +13976 +13977 +13978 +13979 +13980 +13981 +13982 +13983 +13984 +13985 +13986 +13987 +13988 +13989 +13990 +13991 +13992 +13993 +13994 +13995 +13996 +13997 +13998 +13999 +14000 +14001 +14002 +14003 +14004 +14005 +14006 +14007 +14008 +14009 +14010 +14011 +14012 +14013 +14014 +14015 +14016 +14017 +14018 +14019 +14020 +14021 +14022 +14023 +14024 +14025 +14026 +14027 +14028 +14029 +14030 +14031 +14032 +14033 +14034 +14035 +14036 +14037 +14038 +14039 +14040 +14041 +14042 +14043 +14044 +14045 +14046 +14047 +14048 +14049 +14050 +14051 +14052 +14053 +14054 +14055 +14056 +14057 +14058 +14059 +14060 +14061 +14062 +14063 +14064 +14065 +14066 +14067 +14068 +14069 +14070 +14071 +14072 +14073 +14074 +14075 +14076 +14077 +14078 +14079 +14080 +14081 +14082 +14083 +14084 +14085 +14086 +14087 +14088 +14089 +14090 +14091 +14092 +14093 +14094 +14095 +14096 +14097 +14098 +14099 +14100 +14101 +14102 +14103 +14104 +14105 +14106 +14107 +14108 +14109 +14110 +14111 +14112 +14113 +14114 +14115 +14116 +14117 +14118 +14119 +14120 +14121 +14122 +14123 +14124 +14125 +14126 +14127 +14128 +14129 +14130 +14131 +14132 +14133 +14134 +14135 +14136 +14137 +14138 +14139 +14140 +14141 +14142 +14143 +14144 +14145 +14146 +14147 +14148 +14149 +14150 +14151 +14152 +14153 +14154 +14155 +14156 +14157 +14158 +14159 +14160 +14161 +14162 +14163 +14164 +14165 +14166 +14167 +14168 +14169 +14170 +14171 +14172 +14173 +14174 +14175 +14176 +14177 +14178 +14179 +14180 +14181 +14182 +14183 +14184 +14185 +14186 +14187 +14188 +14189 +14190 +14191 +14192 +14193 +14194 +14195 +14196 +14197 +14198 +14199 +14200 +14201 +14202 +14203 +14204 +14205 +14206 +14207 +14208 +14209 +14210 +14211 +14212 +14213 +14214 +14215 +14216 +14217 +14218 +14219 +14220 +14221 +14222 +14223 +14224 +14225 +14226 +14227 +14228 +14229 +14230 +14231 +14232 +14233 +14234 +14235 +14236 +14237 +14238 +14239 +14240 +14241 +14242 +14243 +14244 +14245 +14246 +14247 +14248 +14249 +14250 +14251 +14252 +14253 +14254 +14255 +14256 +14257 +14258 +14259 +14260 +14261 +14262 +14263 +14264 +14265 +14266 +14267 +14268 +14269 +14270 +14271 +14272 +14273 +14274 +14275 +14276 +14277 +14278 +14279 +14280 +14281 +14282 +14283 +14284 +14285 +14286 +14287 +14288 +14289 +14290 +14291 +14292 +14293 +14294 +14295 +14296 +14297 +14298 +14299 +14300 +14301 +14302 +14303 +14304 +14305 +14306 +14307 +14308 +14309 +14310 +14311 +14312 +14313 +14314 +14315 +14316 +14317 +14318 +14319 +14320 +14321 +14322 +14323 +14324 +14325 +14326 +14327 +14328 +14329 +14330 +14331 +14332 +14333 +14334 +14335 +14336 +14337 +14338 +14339 +14340 +14341 +14342 +14343 +14344 +14345 +14346 +14347 +14348 +14349 +14350 +14351 +14352 +14353 +14354 +14355 +14356 +14357 +14358 +14359 +14360 +14361 +14362 +14363 +14364 +14365 +14366 +14367 +14368 +14369 +14370 +14371 +14372 +14373 +14374 +14375 +14376 +14377 +14378 +14379 +14380 +14381 +14382 +14383 +14384 +14385 +14386 +14387 +14388 +14389 +14390 +14391 +14392 +14393 +14394 +14395 +14396 +14397 +14398 +14399 +14400 +14401 +14402 +14403 +14404 +14405 +14406 +14407 +14408 +14409 +14410 +14411 +14412 +14413 +14414 +14415 +14416 +14417 +14418 +14419 +14420 +14421 +14422 +14423 +14424 +14425 +14426 +14427 +14428 +14429 +14430 +14431 +14432 +14433 +14434 +14435 +14436 +14437 +14438 +14439 +14440 +14441 +14442 +14443 +14444 +14445 +14446 +14447 +14448 +14449 +14450 +14451 +14452 +14453 +14454 +14455 +14456 +14457 +14458 +14459 +14460 +14461 +14462 +14463 +14464 +14465 +14466 +14467 +14468 +14469 +14470 +14471 +14472 +14473 +14474 +14475 +14476 +14477 +14478 +14479 +14480 +14481 +14482 +14483 +14484 +14485 +14486 +14487 +14488 +14489 +14490 +14491 +14492 +14493 +14494 +14495 +14496 +14497 +14498 +14499 +14500 +14501 +14502 +14503 +14504 +14505 +14506 +14507 +14508 +14509 +14510 +14511 +14512 +14513 +14514 +14515 +14516 +14517 +14518 +14519 +14520 +14521 +14522 +14523 +14524 +14525 +14526 +14527 +14528 +14529 +14530 +14531 +14532 +14533 +14534 +14535 +14536 +14537 +14538 +14539 +14540 +14541 +14542 +14543 +14544 +14545 +14546 +14547 +14548 +14549 +14550 +14551 +14552 +14553 +14554 +14555 +14556 +14557 +14558 +14559 +14560 +14561 +14562 +14563 +14564 +14565 +14566 +14567 +14568 +14569 +14570 +14571 +14572 +14573 +14574 +14575 +14576 +14577 +14578 +14579 +14580 +14581 +14582 +14583 +14584 +14585 +14586 +14587 +14588 +14589 +14590 +14591 +14592 +14593 +14594 +14595 +14596 +14597 +14598 +14599 +14600 +14601 +14602 +14603 +14604 +14605 +14606 +14607 +14608 +14609 +14610 +14611 +14612 +14613 +14614 +14615 +14616 +14617 +14618 +14619 +14620 +14621 +14622 +14623 +14624 +14625 +14626 +14627 +14628 +14629 +14630 +14631 +14632 +14633 +14634 +14635 +14636 +14637 +14638 +14639 +14640 +14641 +14642 +14643 +14644 +14645 +14646 +14647 +14648 +14649 +14650 +14651 +14652 +14653 +14654 +14655 +14656 +14657 +14658 +14659 +14660 +14661 +14662 +14663 +14664 +14665 +14666 +14667 +14668 +14669 +14670 +14671 +14672 +14673 +14674 +14675 +14676 +14677 +14678 +14679 +14680 +14681 +14682 +14683 +14684 +14685 +14686 +14687 +14688 +14689 +14690 +14691 +14692 +14693 +14694 +14695 +14696 +14697 +14698 +14699 +14700 +14701 +14702 +14703 +14704 +14705 +14706 +14707 +14708 +14709 +14710 +14711 +14712 +14713 +14714 +14715 +14716 +14717 +14718 +14719 +14720 +14721 +14722 +14723 +14724 +14725 +14726 +14727 +14728 +14729 +14730 +14731 +14732 +14733 +14734 +14735 +14736 +14737 +14738 +14739 +14740 +14741 +14742 +14743 +14744 +14745 +14746 +14747 +14748 +14749 +14750 +14751 +14752 +14753 +14754 +14755 +14756 +14757 +14758 +14759 +14760 +14761 +14762 +14763 +14764 +14765 +14766 +14767 +14768 +14769 +14770 +14771 +14772 +14773 +14774 +14775 +14776 +14777 +14778 +14779 +14780 +14781 +14782 +14783 +14784 +14785 +14786 +14787 +14788 +14789 +14790 +14791 +14792 +14793 +14794 +14795 +14796 +14797 +14798 +14799 +14800 +14801 +14802 +14803 +14804 +14805 +14806 +14807 +14808 +14809 +14810 +14811 +14812 +14813 +14814 +14815 +14816 +14817 +14818 +14819 +14820 +14821 +14822 +14823 +14824 +14825 +14826 +14827 +14828 +14829 +14830 +14831 +14832 +14833 +14834 +14835 +14836 +14837 +14838 +14839 +14840 +14841 +14842 +14843 +14844 +14845 +14846 +14847 +14848 +14849 +14850 +14851 +14852 +14853 +14854 +14855 +14856 +14857 +14858 +14859 +14860 +14861 +14862 +14863 +14864 +14865 +14866 +14867 +14868 +14869 +14870 +14871 +14872 +14873 +14874 +14875 +14876 +14877 +14878 +14879 +14880 +14881 +14882 +14883 +14884 +14885 +14886 +14887 +14888 +14889 +14890 +14891 +14892 +14893 +14894 +14895 +14896 +14897 +14898 +14899 +14900 +14901 +14902 +14903 +14904 +14905 +14906 +14907 +14908 +14909 +14910 +14911 +14912 +14913 +14914 +14915 +14916 +14917 +14918 +14919 +14920 +14921 +14922 +14923 +14924 +14925 +14926 +14927 +14928 +14929 +14930 +14931 +14932 +14933 +14934 +14935 +14936 +14937 +14938 +14939 +14940 +14941 +14942 +14943 +14944 +14945 +14946 +14947 +14948 +14949 +14950 +14951 +14952 +14953 +14954 +14955 +14956 +14957 +14958 +14959 +14960 +14961 +14962 +14963 +14964 +14965 +14966 +14967 +14968 +14969 +14970 +14971 +14972 +14973 +14974 +14975 +14976 +14977 +14978 +14979 +14980 +14981 +14982 +14983 +14984 +14985 +14986 +14987 +14988 +14989 +14990 +14991 +14992 +14993 +14994 +14995 +14996 +14997 +14998 +14999 +15000 +15001 +15002 +15003 +15004 +15005 +15006 +15007 +15008 +15009 +15010 +15011 +15012 +15013 +15014 +15015 +15016 +15017 +15018 +15019 +15020 +15021 +15022 +15023 +15024 +15025 +15026 +15027 +15028 +15029 +15030 +15031 +15032 +15033 +15034 +15035 +15036 +15037 +15038 +15039 +15040 +15041 +15042 +15043 +15044 +15045 +15046 +15047 +15048 +15049 +15050 +15051 +15052 +15053 +15054 +15055 +15056 +15057 +15058 +15059 +15060 +15061 +15062 +15063 +15064 +15065 +15066 +15067 +15068 +15069 +15070 +15071 +15072 +15073 +15074 +15075 +15076 +15077 +15078 +15079 +15080 +15081 +15082 +15083 +15084 +15085 +15086 +15087 +15088 +15089 +15090 +15091 +15092 +15093 +15094 +15095 +15096 +15097 +15098 +15099 +15100 +15101 +15102 +15103 +15104 +15105 +15106 +15107 +15108 +15109 +15110 +15111 +15112 +15113 +15114 +15115 +15116 +15117 +15118 +15119 +15120 +15121 +15122 +15123 +15124 +15125 +15126 +15127 +15128 +15129 +15130 +15131 +15132 +15133 +15134 +15135 +15136 +15137 +15138 +15139 +15140 +15141 +15142 +15143 +15144 +15145 +15146 +15147 +15148 +15149 +15150 +15151 +15152 +15153 +15154 +15155 +15156 +15157 +15158 +15159 +15160 +15161 +15162 +15163 +15164 +15165 +15166 +15167 +15168 +15169 +15170 +15171 +15172 +15173 +15174 +15175 +15176 +15177 +15178 +15179 +15180 +15181 +15182 +15183 +15184 +15185 +15186 +15187 +15188 +15189 +15190 +15191 +15192 +15193 +15194 +15195 +15196 +15197 +15198 +15199 +15200 +15201 +15202 +15203 +15204 +15205 +15206 +15207 +15208 +15209 +15210 +15211 +15212 +15213 +15214 +15215 +15216 +15217 +15218 +15219 +15220 +15221 +15222 +15223 +15224 +15225 +15226 +15227 +15228 +15229 +15230 +15231 +15232 +15233 +15234 +15235 +15236 +15237 +15238 +15239 +15240 +15241 +15242 +15243 +15244 +15245 +15246 +15247 +15248 +15249 +15250 +15251 +15252 +15253 +15254 +15255 +15256 +15257 +15258 +15259 +15260 +15261 +15262 +15263 +15264 +15265 +15266 +15267 +15268 +15269 +15270 +15271 +15272 +15273 +15274 +15275 +15276 +15277 +15278 +15279 +15280 +15281 +15282 +15283 +15284 +15285 +15286 +15287 +15288 +15289 +15290 +15291 +15292 +15293 +15294 +15295 +15296 +15297 +15298 +15299 +15300 +15301 +15302 +15303 +15304 +15305 +15306 +15307 +15308 +15309 +15310 +15311 +15312 +15313 +15314 +15315 +15316 +15317 +15318 +15319 +15320 +15321 +15322 +15323 +15324 +15325 +15326 +15327 +15328 +15329 +15330 +15331 +15332 +15333 +15334 +15335 +15336 +15337 +15338 +15339 +15340 +15341 +15342 +15343 +15344 +15345 +15346 +15347 +15348 +15349 +15350 +15351 +15352 +15353 +15354 +15355 +15356 +15357 +15358 +15359 +15360 +15361 +15362 +15363 +15364 +15365 +15366 +15367 +15368 +15369 +15370 +15371 +15372 +15373 +15374 +15375 +15376 +15377 +15378 +15379 +15380 +15381 +15382 +15383 +15384 +15385 +15386 +15387 +15388 +15389 +15390 +15391 +15392 +15393 +15394 +15395 +15396 +15397 +15398 +15399 +15400 +15401 +15402 +15403 +15404 +15405 +15406 +15407 +15408 +15409 +15410 +15411 +15412 +15413 +15414 +15415 +15416 +15417 +15418 +15419 +15420 +15421 +15422 +15423 +15424 +15425 +15426 +15427 +15428 +15429 +15430 +15431 +15432 +15433 +15434 +15435 +15436 +15437 +15438 +15439 +15440 +15441 +15442 +15443 +15444 +15445 +15446 +15447 +15448 +15449 +15450 +15451 +15452 +15453 +15454 +15455 +15456 +15457 +15458 +15459 +15460 +15461 +15462 +15463 +15464 +15465 +15466 +15467 +15468 +15469 +15470 +15471 +15472 +15473 +15474 +15475 +15476 +15477 +15478 +15479 +15480 +15481 +15482 +15483 +15484 +15485 +15486 +15487 +15488 +15489 +15490 +15491 +15492 +15493 +15494 +15495 +15496 +15497 +15498 +15499 +15500 +15501 +15502 +15503 +15504 +15505 +15506 +15507 +15508 +15509 +15510 +15511 +15512 +15513 +15514 +15515 +15516 +15517 +15518 +15519 +15520 +15521 +15522 +15523 +15524 +15525 +15526 +15527 +15528 +15529 +15530 +15531 +15532 +15533 +15534 +15535 +15536 +15537 +15538 +15539 +15540 +15541 +15542 +15543 +15544 +15545 +15546 +15547 +15548 +15549 +15550 +15551 +15552 +15553 +15554 +15555 +15556 +15557 +15558 +15559 +15560 +15561 +15562 +15563 +15564 +15565 +15566 +15567 +15568 +15569 +15570 +15571 +15572 +15573 +15574 +15575 +15576 +15577 +15578 +15579 +15580 +15581 +15582 +15583 +15584 +15585 +15586 +15587 +15588 +15589 +15590 +15591 +15592 +15593 +15594 +15595 +15596 +15597 +15598 +15599 +15600 +15601 +15602 +15603 +15604 +15605 +15606 +15607 +15608 +15609 +15610 +15611 +15612 +15613 +15614 +15615 +15616 +15617 +15618 +15619 +15620 +15621 +15622 +15623 +15624 +15625 +15626 +15627 +15628 +15629 +15630 +15631 +15632 +15633 +15634 +15635 +15636 +15637 +15638 +15639 +15640 +15641 +15642 +15643 +15644 +15645 +15646 +15647 +15648 +15649 +15650 +15651 +15652 +15653 +15654 +15655 +15656 +15657 +15658 +15659 +15660 +15661 +15662 +15663 +15664 +15665 +15666 +15667 +15668 +15669 +15670 +15671 +15672 +15673 +15674 +15675 +15676 +15677 +15678 +15679 +15680 +15681 +15682 +15683 +15684 +15685 +15686 +15687 +15688 +15689 +15690 +15691 +15692 +15693 +15694 +15695 +15696 +15697 +15698 +15699 +15700 +15701 +15702 +15703 +15704 +15705 +15706 +15707 +15708 +15709 +15710 +15711 +15712 +15713 +15714 +15715 +15716 +15717 +15718 +15719 +15720 +15721 +15722 +15723 +15724 +15725 +15726 +15727 +15728 +15729 +15730 +15731 +15732 +15733 +15734 +15735 +15736 +15737 +15738 +15739 +15740 +15741 +15742 +15743 +15744 +15745 +15746 +15747 +15748 +15749 +15750 +15751 +15752 +15753 +15754 +15755 +15756 +15757 +15758 +15759 +15760 +15761 +15762 +15763 +15764 +15765 +15766 +15767 +15768 +15769 +15770 +15771 +15772 +15773 +15774 +15775 +15776 +15777 +15778 +15779 +15780 +15781 +15782 +15783 +15784 +15785 +15786 +15787 +15788 +15789 +15790 +15791 +15792 +15793 +15794 +15795 +15796 +15797 +15798 +15799 +15800 +15801 +15802 +15803 +15804 +15805 +15806 +15807 +15808 +15809 +15810 +15811 +15812 +15813 +15814 +15815 +15816 +15817 +15818 +15819 +15820 +15821 +15822 +15823 +15824 +15825 +15826 +15827 +15828 +15829 +15830 +15831 +15832 +15833 +15834 +15835 +15836 +15837 +15838 +15839 +15840 +15841 +15842 +15843 +15844 +15845 +15846 +15847 +15848 +15849 +15850 +15851 +15852 +15853 +15854 +15855 +15856 +15857 +15858 +15859 +15860 +15861 +15862 +15863 +15864 +15865 +15866 +15867 +15868 +15869 +15870 +15871 +15872 +15873 +15874 +15875 +15876 +15877 +15878 +15879 +15880 +15881 +15882 +15883 +15884 +15885 +15886 +15887 +15888 +15889 +15890 +15891 +15892 +15893 +15894 +15895 +15896 +15897 +15898 +15899 +15900 +15901 +15902 +15903 +15904 +15905 +15906 +15907 +15908 +15909 +15910 +15911 +15912 +15913 +15914 +15915 +15916 +15917 +15918 +15919 +15920 +15921 +15922 +15923 +15924 +15925 +15926 +15927 +15928 +15929 +15930 +15931 +15932 +15933 +15934 +15935 +15936 +15937 +15938 +15939 +15940 +15941 +15942 +15943 +15944 +15945 +15946 +15947 +15948 +15949 +15950 +15951 +15952 +15953 +15954 +15955 +15956 +15957 +15958 +15959 +15960 +15961 +15962 +15963 +15964 +15965 +15966 +15967 +15968 +15969 +15970 +15971 +15972 +15973 +15974 +15975 +15976 +15977 +15978 +15979 +15980 +15981 +15982 +15983 +15984 +15985 +15986 +15987 +15988 +15989 +15990 +15991 +15992 +15993 +15994 +15995 +15996 +15997 +15998 +15999 +16000 +16001 +16002 +16003 +16004 +16005 +16006 +16007 +16008 +16009 +16010 +16011 +16012 +16013 +16014 +16015 +16016 +16017 +16018 +16019 +16020 +16021 +16022 +16023 +16024 +16025 +16026 +16027 +16028 +16029 +16030 +16031 +16032 +16033 +16034 +16035 +16036 +16037 +16038 +16039 +16040 +16041 +16042 +16043 +16044 +16045 +16046 +16047 +16048 +16049 +16050 +16051 +16052 +16053 +16054 +16055 +16056 +16057 +16058 +16059 +16060 +16061 +16062 +16063 +16064 +16065 +16066 +16067 +16068 +16069 +16070 +16071 +16072 +16073 +16074 +16075 +16076 +16077 +16078 +16079 +16080 +16081 +16082 +16083 +16084 +16085 +16086 +16087 +16088 +16089 +16090 +16091 +16092 +16093 +16094 +16095 +16096 +16097 +16098 +16099 +16100 +16101 +16102 +16103 +16104 +16105 +16106 +16107 +16108 +16109 +16110 +16111 +16112 +16113 +16114 +16115 +16116 +16117 +16118 +16119 +16120 +16121 +16122 +16123 +16124 +16125 +16126 +16127 +16128 +16129 +16130 +16131 +16132 +16133 +16134 +16135 +16136 +16137 +16138 +16139 +16140 +16141 +16142 +16143 +16144 +16145 +16146 +16147 +16148 +16149 +16150 +16151 +16152 +16153 +16154 +16155 +16156 +16157 +16158 +16159 +16160 +16161 +16162 +16163 +16164 +16165 +16166 +16167 +16168 +16169 +16170 +16171 +16172 +16173 +16174 +16175 +16176 +16177 +16178 +16179 +16180 +16181 +16182 +16183 +16184 +16185 +16186 +16187 +16188 +16189 +16190 +16191 +16192 +16193 +16194 +16195 +16196 +16197 +16198 +16199 +16200 +16201 +16202 +16203 +16204 +16205 +16206 +16207 +16208 +16209 +16210 +16211 +16212 +16213 +16214 +16215 +16216 +16217 +16218 +16219 +16220 +16221 +16222 +16223 +16224 +16225 +16226 +16227 +16228 +16229 +16230 +16231 +16232 +16233 +16234 +16235 +16236 +16237 +16238 +16239 +16240 +16241 +16242 +16243 +16244 +16245 +16246 +16247 +16248 +16249 +16250 +16251 +16252 +16253 +16254 +16255 +16256 +16257 +16258 +16259 +16260 +16261 +16262 +16263 +16264 +16265 +16266 +16267 +16268 +16269 +16270 +16271 +16272 +16273 +16274 +16275 +16276 +16277 +16278 +16279 +16280 +16281 +16282 +16283 +16284 +16285 +16286 +16287 +16288 +16289 +16290 +16291 +16292 +16293 +16294 +16295 +16296 +16297 +16298 +16299 +16300 +16301 +16302 +16303 +16304 +16305 +16306 +16307 +16308 +16309 +16310 +16311 +16312 +16313 +16314 +16315 +16316 +16317 +16318 +16319 +16320 +16321 +16322 +16323 +16324 +16325 +16326 +16327 +16328 +16329 +16330 +16331 +16332 +16333 +16334 +16335 +16336 +16337 +16338 +16339 +16340 +16341 +16342 +16343 +16344 +16345 +16346 +16347 +16348 +16349 +16350 +16351 +16352 +16353 +16354 +16355 +16356 +16357 +16358 +16359 +16360 +16361 +16362 +16363 +16364 +16365 +16366 +16367 +16368 +16369 +16370 +16371 +16372 +16373 +16374 +16375 +16376 +16377 +16378 +16379 +16380 +16381 +16382 +16383 +16384 +16385 +16386 +16387 +16388 +16389 +16390 +16391 +16392 +16393 +16394 +16395 +16396 +16397 +16398 +16399 +16400 +16401 +16402 +16403 +16404 +16405 +16406 +16407 +16408 +16409 +16410 +16411 +16412 +16413 +16414 +16415 +16416 +16417 +16418 +16419 +16420 +16421 +16422 +16423 +16424 +16425 +16426 +16427 +16428 +16429 +16430 +16431 +16432 +16433 +16434 +16435 +16436 +16437 +16438 +16439 +16440 +16441 +16442 +16443 +16444 +16445 +16446 +16447 +16448 +16449 +16450 +16451 +16452 +16453 +16454 +16455 +16456 +16457 +16458 +16459 +16460 +16461 +16462 +16463 +16464 +16465 +16466 +16467 +16468 +16469 +16470 +16471 +16472 +16473 +16474 +16475 +16476 +16477 +16478 +16479 +16480 +16481 +16482 +16483 +16484 +16485 +16486 +16487 +16488 +16489 +16490 +16491 +16492 +16493 +16494 +16495 +16496 +16497 +16498 +16499 +16500 +16501 +16502 +16503 +16504 +16505 +16506 +16507 +16508 +16509 +16510 +16511 +16512 +16513 +16514 +16515 +16516 +16517 +16518 +16519 +16520 +16521 +16522 +16523 +16524 +16525 +16526 +16527 +16528 +16529 +16530 +16531 +16532 +16533 +16534 +16535 +16536 +16537 +16538 +16539 +16540 +16541 +16542 +16543 +16544 +16545 +16546 +16547 +16548 +16549 +16550 +16551 +16552 +16553 +16554 +16555 +16556 +16557 +16558 +16559 +16560 +16561 +16562 +16563 +16564 +16565 +16566 +16567 +16568 +16569 +16570 +16571 +16572 +16573 +16574 +16575 +16576 +16577 +16578 +16579 +16580 +16581 +16582 +16583 +16584 +16585 +16586 +16587 +16588 +16589 +16590 +16591 +16592 +16593 +16594 +16595 +16596 +16597 +16598 +16599 +16600 +16601 +16602 +16603 +16604 +16605 +16606 +16607 +16608 +16609 +16610 +16611 +16612 +16613 +16614 +16615 +16616 +16617 +16618 +16619 +16620 +16621 +16622 +16623 +16624 +16625 +16626 +16627 +16628 +16629 +16630 +16631 +16632 +16633 +16634 +16635 +16636 +16637 +16638 +16639 +16640 +16641 +16642 +16643 +16644 +16645 +16646 +16647 +16648 +16649 +16650 +16651 +16652 +16653 +16654 +16655 +16656 +16657 +16658 +16659 +16660 +16661 +16662 +16663 +16664 +16665 +16666 +16667 +16668 +16669 +16670 +16671 +16672 +16673 +16674 +16675 +16676 +16677 +16678 +16679 +16680 +16681 +16682 +16683 +16684 +16685 +16686 +16687 +16688 +16689 +16690 +16691 +16692 +16693 +16694 +16695 +16696 +16697 +16698 +16699 +16700 +16701 +16702 +16703 +16704 +16705 +16706 +16707 +16708 +16709 +16710 +16711 +16712 +16713 +16714 +16715 +16716 +16717 +16718 +16719 +16720 +16721 +16722 +16723 +16724 +16725 +16726 +16727 +16728 +16729 +16730 +16731 +16732 +16733 +16734 +16735 +16736 +16737 +16738 +16739 +16740 +16741 +16742 +16743 +16744 +16745 +16746 +16747 +16748 +16749 +16750 +16751 +16752 +16753 +16754 +16755 +16756 +16757 +16758 +16759 +16760 +16761 +16762 +16763 +16764 +16765 +16766 +16767 +16768 +16769 +16770 +16771 +16772 +16773 +16774 +16775 +16776 +16777 +16778 +16779 +16780 +16781 +16782 +16783 +16784 +16785 +16786 +16787 +16788 +16789 +16790 +16791 +16792 +16793 +16794 +16795 +16796 +16797 +16798 +16799 +16800 +16801 +16802 +16803 +16804 +16805 +16806 +16807 +16808 +16809 +16810 +16811 +16812 +16813 +16814 +16815 +16816 +16817 +16818 +16819 +16820 +16821 +16822 +16823 +16824 +16825 +16826 +16827 +16828 +16829 +16830 +16831 +16832 +16833 +16834 +16835 +16836 +16837 +16838 +16839 +16840 +16841 +16842 +16843 +16844 +16845 +16846 +16847 +16848 +16849 +16850 +16851 +16852 +16853 +16854 +16855 +16856 +16857 +16858 +16859 +16860 +16861 +16862 +16863 +16864 +16865 +16866 +16867 +16868 +16869 +16870 +16871 +16872 +16873 +16874 +16875 +16876 +16877 +16878 +16879 +16880 +16881 +16882 +16883 +16884 +16885 +16886 +16887 +16888 +16889 +16890 +16891 +16892 +16893 +16894 +16895 +16896 +16897 +16898 +16899 +16900 +16901 +16902 +16903 +16904 +16905 +16906 +16907 +16908 +16909 +16910 +16911 +16912 +16913 +16914 +16915 +16916 +16917 +16918 +16919 +16920 +16921 +16922 +16923 +16924 +16925 +16926 +16927 +16928 +16929 +16930 +16931 +16932 +16933 +16934 +16935 +16936 +16937 +16938 +16939 +16940 +16941 +16942 +16943 +16944 +16945 +16946 +16947 +16948 +16949 +16950 +16951 +16952 +16953 +16954 +16955 +16956 +16957 +16958 +16959 +16960 +16961 +16962 +16963 +16964 +16965 +16966 +16967 +16968 +16969 +16970 +16971 +16972 +16973 +16974 +16975 +16976 +16977 +16978 +16979 +16980 +16981 +16982 +16983 +16984 +16985 +16986 +16987 +16988 +16989 +16990 +16991 +16992 +16993 +16994 +16995 +16996 +16997 +16998 +16999 +17000 +17001 +17002 +17003 +17004 +17005 +17006 +17007 +17008 +17009 +17010 +17011 +17012 +17013 +17014 +17015 +17016 +17017 +17018 +17019 +17020 +17021 +17022 +17023 +17024 +17025 +17026 +17027 +17028 +17029 +17030 +17031 +17032 +17033 +17034 +17035 +17036 +17037 +17038 +17039 +17040 +17041 +17042 +17043 +17044 +17045 +17046 +17047 +17048 +17049 +17050 +17051 +17052 +17053 +17054 +17055 +17056 +17057 +17058 +17059 +17060 +17061 +17062 +17063 +17064 +17065 +17066 +17067 +17068 +17069 +17070 +17071 +17072 +17073 +17074 +17075 +17076 +17077 +17078 +17079 +17080 +17081 +17082 +17083 +17084 +17085 +17086 +17087 +17088 +17089 +17090 +17091 +17092 +17093 +17094 +17095 +17096 +17097 +17098 +17099 +17100 +17101 +17102 +17103 +17104 +17105 +17106 +17107 +17108 +17109 +17110 +17111 +17112 +17113 +17114 +17115 +17116 +17117 +17118 +17119 +17120 +17121 +17122 +17123 +17124 +17125 +17126 +17127 +17128 +17129 +17130 +17131 +17132 +17133 +17134 +17135 +17136 +17137 +17138 +17139 +17140 +17141 +17142 +17143 +17144 +17145 +17146 +17147 +17148 +17149 +17150 +17151 +17152 +17153 +17154 +17155 +17156 +17157 +17158 +17159 +17160 +17161 +17162 +17163 +17164 +17165 +17166 +17167 +17168 +17169 +17170 +17171 +17172 +17173 +17174 +17175 +17176 +17177 +17178 +17179 +17180 +17181 +17182 +17183 +17184 +17185 +17186 +17187 +17188 +17189 +17190 +17191 +17192 +17193 +17194 +17195 +17196 +17197 +17198 +17199 +17200 +17201 +17202 +17203 +17204 +17205 +17206 +17207 +17208 +17209 +17210 +17211 +17212 +17213 +17214 +17215 +17216 +17217 +17218 +17219 +17220 +17221 +17222 +17223 +17224 +17225 +17226 +17227 +17228 +17229 +17230 +17231 +17232 +17233 +17234 +17235 +17236 +17237 +17238 +17239 +17240 +17241 +17242 +17243 +17244 +17245 +17246 +17247 +17248 +17249 +17250 +17251 +17252 +17253 +17254 +17255 +17256 +17257 +17258 +17259 +17260 +17261 +17262 +17263 +17264 +17265 +17266 +17267 +17268 +17269 +17270 +17271 +17272 +17273 +17274 +17275 +17276 +17277 +17278 +17279 +17280 +17281 +17282 +17283 +17284 +17285 +17286 +17287 +17288 +17289 +17290 +17291 +17292 +17293 +17294 +17295 +17296 +17297 +17298 +17299 +17300 +17301 +17302 +17303 +17304 +17305 +17306 +17307 +17308 +17309 +17310 +17311 +17312 +17313 +17314 +17315 +17316 +17317 +17318 +17319 +17320 +17321 +17322 +17323 +17324 +17325 +17326 +17327 +17328 +17329 +17330 +17331 +17332 +17333 +17334 +17335 +17336 +17337 +17338 +17339 +17340 +17341 +17342 +17343 +17344 +17345 +17346 +17347 +17348 +17349 +17350 +17351 +17352 +17353 +17354 +17355 +17356 +17357 +17358 +17359 +17360 +17361 +17362 +17363 +17364 +17365 +17366 +17367 +17368 +17369 +17370 +17371 +17372 +17373 +17374 +17375 +17376 +17377 +17378 +17379 +17380 +17381 +17382 +17383 +17384 +17385 +17386 +17387 +17388 +17389 +17390 +17391 +17392 +17393 +17394 +17395 +17396 +17397 +17398 +17399 +17400 +17401 +17402 +17403 +17404 +17405 +17406 +17407 +17408 +17409 +17410 +17411 +17412 +17413 +17414 +17415 +17416 +17417 +17418 +17419 +17420 +17421 +17422 +17423 +17424 +17425 +17426 +17427 +17428 +17429 +17430 +17431 +17432 +17433 +17434 +17435 +17436 +17437 +17438 +17439 +17440 +17441 +17442 +17443 +17444 +17445 +17446 +17447 +17448 +17449 +17450 +17451 +17452 +17453 +17454 +17455 +17456 +17457 +17458 +17459 +17460 +17461 +17462 +17463 +17464 +17465 +17466 +17467 +17468 +17469 +17470 +17471 +17472 +17473 +17474 +17475 +17476 +17477 +17478 +17479 +17480 +17481 +17482 +17483 +17484 +17485 +17486 +17487 +17488 +17489 +17490 +17491 +17492 +17493 +17494 +17495 +17496 +17497 +17498 +17499 +17500 +17501 +17502 +17503 +17504 +17505 +17506 +17507 +17508 +17509 +17510 +17511 +17512 +17513 +17514 +17515 +17516 +17517 +17518 +17519 +17520 +17521 +17522 +17523 +17524 +17525 +17526 +17527 +17528 +17529 +17530 +17531 +17532 +17533 +17534 +17535 +17536 +17537 +17538 +17539 +17540 +17541 +17542 +17543 +17544 +17545 +17546 +17547 +17548 +17549 +17550 +17551 +17552 +17553 +17554 +17555 +17556 +17557 +17558 +17559 +17560 +17561 +17562 +17563 +17564 +17565 +17566 +17567 +17568 +17569 +17570 +17571 +17572 +17573 +17574 +17575 +17576 +17577 +17578 +17579 +17580 +17581 +17582 +17583 +17584 +17585 +17586 +17587 +17588 +17589 +17590 +17591 +17592 +17593 +17594 +17595 +17596 +17597 +17598 +17599 +17600 +17601 +17602 +17603 +17604 +17605 +17606 +17607 +17608 +17609 +17610 +17611 +17612 +17613 +17614 +17615 +17616 +17617 +17618 +17619 +17620 +17621 +17622 +17623 +17624 +17625 +17626 +17627 +17628 +17629 +17630 +17631 +17632 +17633 +17634 +17635 +17636 +17637 +17638 +17639 +17640 +17641 +17642 +17643 +17644 +17645 +17646 +17647 +17648 +17649 +17650 +17651 +17652 +17653 +17654 +17655 +17656 +17657 +17658 +17659 +17660 +17661 +17662 +17663 +17664 +17665 +17666 +17667 +17668 +17669 +17670 +17671 +17672 +17673 +17674 +17675 +17676 +17677 +17678 +17679 +17680 +17681 +17682 +17683 +17684 +17685 +17686 +17687 +17688 +17689 +17690 +17691 +17692 +17693 +17694 +17695 +17696 +17697 +17698 +17699 +17700 +17701 +17702 +17703 +17704 +17705 +17706 +17707 +17708 +17709 +17710 +17711 +17712 +17713 +17714 +17715 +17716 +17717 +17718 +17719 +17720 +17721 +17722 +17723 +17724 +17725 +17726 +17727 +17728 +17729 +17730 +17731 +17732 +17733 +17734 +17735 +17736 +17737 +17738 +17739 +17740 +17741 +17742 +17743 +17744 +17745 +17746 +17747 +17748 +17749 +17750 +17751 +17752 +17753 +17754 +17755 +17756 +17757 +17758 +17759 +17760 +17761 +17762 +17763 +17764 +17765 +17766 +17767 +17768 +17769 +17770 +17771 +17772 +17773 +17774 +17775 +17776 +17777 +17778 +17779 +17780 +17781 +17782 +17783 +17784 +17785 +17786 +17787 +17788 +17789 +17790 +17791 +17792 +17793 +17794 +17795 +17796 +17797 +17798 +17799 +17800 +17801 +17802 +17803 +17804 +17805 +17806 +17807 +17808 +17809 +17810 +17811 +17812 +17813 +17814 +17815 +17816 +17817 +17818 +17819 +17820 +17821 +17822 +17823 +17824 +17825 +17826 +17827 +17828 +17829 +17830 +17831 +17832 +17833 +17834 +17835 +17836 +17837 +17838 +17839 +17840 +17841 +17842 +17843 +17844 +17845 +17846 +17847 +17848 +17849 +17850 +17851 +17852 +17853 +17854 +17855 +17856 +17857 +17858 +17859 +17860 +17861 +17862 +17863 +17864 +17865 +17866 +17867 +17868 +17869 +17870 +17871 +17872 +17873 +17874 +17875 +17876 +17877 +17878 +17879 +17880 +17881 +17882 +17883 +17884 +17885 +17886 +17887 +17888 +17889 +17890 +17891 +17892 +17893 +17894 +17895 +17896 +17897 +17898 +17899 +17900 +17901 +17902 +17903 +17904 +17905 +17906 +17907 +17908 +17909 +17910 +17911 +17912 +17913 +17914 +17915 +17916 +17917 +17918 +17919 +17920 +17921 +17922 +17923 +17924 +17925 +17926 +17927 +17928 +17929 +17930 +17931 +17932 +17933 +17934 +17935 +17936 +17937 +17938 +17939 +17940 +17941 +17942 +17943 +17944 +17945 +17946 +17947 +17948 +17949 +17950 +17951 +17952 +17953 +17954 +17955 +17956 +17957 +17958 +17959 +17960 +17961 +17962 +17963 +17964 +17965 +17966 +17967 +17968 +17969 +17970 +17971 +17972 +17973 +17974 +17975 +17976 +17977 +17978 +17979 +17980 +17981 +17982 +17983 +17984 +17985 +17986 +17987 +17988 +17989 +17990 +17991 +17992 +17993 +17994 +17995 +17996 +17997 +17998 +17999 +18000 +18001 +18002 +18003 +18004 +18005 +18006 +18007 +18008 +18009 +18010 +18011 +18012 +18013 +18014 +18015 +18016 +18017 +18018 +18019 +18020 +18021 +18022 +18023 +18024 +18025 +18026 +18027 +18028 +18029 +18030 +18031 +18032 +18033 +18034 +18035 +18036 +18037 +18038 +18039 +18040 +18041 +18042 +18043 +18044 +18045 +18046 +18047 +18048 +18049 +18050 +18051 +18052 +18053 +18054 +18055 +18056 +18057 +18058 +18059 +18060 +18061 +18062 +18063 +18064 +18065 +18066 +18067 +18068 +18069 +18070 +18071 +18072 +18073 +18074 +18075 +18076 +18077 +18078 +18079 +18080 +18081 +18082 +18083 +18084 +18085 +18086 +18087 +18088 +18089 +18090 +18091 +18092 +18093 +18094 +18095 +18096 +18097 +18098 +18099 +18100 +18101 +18102 +18103 +18104 +18105 +18106 +18107 +18108 +18109 +18110 +18111 +18112 +18113 +18114 +18115 +18116 +18117 +18118 +18119 +18120 +18121 +18122 +18123 +18124 +18125 +18126 +18127 +18128 +18129 +18130 +18131 +18132 +18133 +18134 +18135 +18136 +18137 +18138 +18139 +18140 +18141 +18142 +18143 +18144 +18145 +18146 +18147 +18148 +18149 +18150 +18151 +18152 +18153 +18154 +18155 +18156 +18157 +18158 +18159 +18160 +18161 +18162 +18163 +18164 +18165 +18166 +18167 +18168 +18169 +18170 +18171 +18172 +18173 +18174 +18175 +18176 +18177 +18178 +18179 +18180 +18181 +18182 +18183 +18184 +18185 +18186 +18187 +18188 +18189 +18190 +18191 +18192 +18193 +18194 +18195 +18196 +18197 +18198 +18199 +18200 +18201 +18202 +18203 +18204 +18205 +18206 +18207 +18208 +18209 +18210 +18211 +18212 +18213 +18214 +18215 +18216 +18217 +18218 +18219 +18220 +18221 +18222 +18223 +18224 +18225 +18226 +18227 +18228 +18229 +18230 +18231 +18232 +18233 +18234 +18235 +18236 +18237 +18238 +18239 +18240 +18241 +18242 +18243 +18244 +18245 +18246 +18247 +18248 +18249 +18250 +18251 +18252 +18253 +18254 +18255 +18256 +18257 +18258 +18259 +18260 +18261 +18262 +18263 +18264 +18265 +18266 +18267 +18268 +18269 +18270 +18271 +18272 +18273 +18274 +18275 +18276 +18277 +18278 +18279 +18280 +18281 +18282 +18283 +18284 +18285 +18286 +18287 +18288 +18289 +18290 +18291 +18292 +18293 +18294 +18295 +18296 +18297 +18298 +18299 +18300 +18301 +18302 +18303 +18304 +18305 +18306 +18307 +18308 +18309 +18310 +18311 +18312 +18313 +18314 +18315 +18316 +18317 +18318 +18319 +18320 +18321 +18322 +18323 +18324 +18325 +18326 +18327 +18328 +18329 +18330 +18331 +18332 +18333 +18334 +18335 +18336 +18337 +18338 +18339 +18340 +18341 +18342 +18343 +18344 +18345 +18346 +18347 +18348 +18349 +18350 +18351 +18352 +18353 +18354 +18355 +18356 +18357 +18358 +18359 +18360 +18361 +18362 +18363 +18364 +18365 +18366 +18367 +18368 +18369 +18370 +18371 +18372 +18373 +18374 +18375 +18376 +18377 +18378 +18379 +18380 +18381 +18382 +18383 +18384 +18385 +18386 +18387 +18388 +18389 +18390 +18391 +18392 +18393 +18394 +18395 +18396 +18397 +18398 +18399 +18400 +18401 +18402 +18403 +18404 +18405 +18406 +18407 +18408 +18409 +18410 +18411 +18412 +18413 +18414 +18415 +18416 +18417 +18418 +18419 +18420 +18421 +18422 +18423 +18424 +18425 +18426 +18427 +18428 +18429 +18430 +18431 +18432 +18433 +18434 +18435 +18436 +18437 +18438 +18439 +18440 +18441 +18442 +18443 +18444 +18445 +18446 +18447 +18448 +18449 +18450 +18451 +18452 +18453 +18454 +18455 +18456 +18457 +18458 +18459 +18460 +18461 +18462 +18463 +18464 +18465 +18466 +18467 +18468 +18469 +18470 +18471 +18472 +18473 +18474 +18475 +18476 +18477 +18478 +18479 +18480 +18481 +18482 +18483 +18484 +18485 +18486 +18487 +18488 +18489 +18490 +18491 +18492 +18493 +18494 +18495 +18496 +18497 +18498 +18499 +18500 +18501 +18502 +18503 +18504 +18505 +18506 +18507 +18508 +18509 +18510 +18511 +18512 +18513 +18514 +18515 +18516 +18517 +18518 +18519 +18520 +18521 +18522 +18523 +18524 +18525 +18526 +18527 +18528 +18529 +18530 +18531 +18532 +18533 +18534 +18535 +18536 +18537 +18538 +18539 +18540 +18541 +18542 +18543 +18544 +18545 +18546 +18547 +18548 +18549 +18550 +18551 +18552 +18553 +18554 +18555 +18556 +18557 +18558 +18559 +18560 +18561 +18562 +18563 +18564 +18565 +18566 +18567 +18568 +18569 +18570 +18571 +18572 +18573 +18574 +18575 +18576 +18577 +18578 +18579 +18580 +18581 +18582 +18583 +18584 +18585 +18586 +18587 +18588 +18589 +18590 +18591 +18592 +18593 +18594 +18595 +18596 +18597 +18598 +18599 +18600 +18601 +18602 +18603 +18604 +18605 +18606 +18607 +18608 +18609 +18610 +18611 +18612 +18613 +18614 +18615 +18616 +18617 +18618 +18619 +18620 +18621 +18622 +18623 +18624 +18625 +18626 +18627 +18628 +18629 +18630 +18631 +18632 +18633 +18634 +18635 +18636 +18637 +18638 +18639 +18640 +18641 +18642 +18643 +18644 +18645 +18646 +18647 +18648 +18649 +18650 +18651 +18652 +18653 +18654 +18655 +18656 +18657 +18658 +18659 +18660 +18661 +18662 +18663 +18664 +18665 +18666 +18667 +18668 +18669 +18670 +18671 +18672 +18673 +18674 +18675 +18676 +18677 +18678 +18679 +18680 +18681 +18682 +18683 +18684 +18685 +18686 +18687 +18688 +18689 +18690 +18691 +18692 +18693 +18694 +18695 +18696 +18697 +18698 +18699 +18700 +18701 +18702 +18703 +18704 +18705 +18706 +18707 +18708 +18709 +18710 +18711 +18712 +18713 +18714 +18715 +18716 +18717 +18718 +18719 +18720 +18721 +18722 +18723 +18724 +18725 +18726 +18727 +18728 +18729 +18730 +18731 +18732 +18733 +18734 +18735 +18736 +18737 +18738 +18739 +18740 +18741 +18742 +18743 +18744 +18745 +18746 +18747 +18748 +18749 +18750 +18751 +18752 +18753 +18754 +18755 +18756 +18757 +18758 +18759 +18760 +18761 +18762 +18763 +18764 +18765 +18766 +18767 +18768 +18769 +18770 +18771 +18772 +18773 +18774 +18775 +18776 +18777 +18778 +18779 +18780 +18781 +18782 +18783 +18784 +18785 +18786 +18787 +18788 +18789 +18790 +18791 +18792 +18793 +18794 +18795 +18796 +18797 +18798 +18799 +18800 +18801 +18802 +18803 +18804 +18805 +18806 +18807 +18808 +18809 +18810 +18811 +18812 +18813 +18814 +18815 +18816 +18817 +18818 +18819 +18820 +18821 +18822 +18823 +18824 +18825 +18826 +18827 +18828 +18829 +18830 +18831 +18832 +18833 +18834 +18835 +18836 +18837 +18838 +18839 +18840 +18841 +18842 +18843 +18844 +18845 +18846 +18847 +18848 +18849 +18850 +18851 +18852 +18853 +18854 +18855 +18856 +18857 +18858 +18859 +18860 +18861 +18862 +18863 +18864 +18865 +18866 +18867 +18868 +18869 +18870 +18871 +18872 +18873 +18874 +18875 +18876 +18877 +18878 +18879 +18880 +18881 +18882 +18883 +18884 +18885 +18886 +18887 +18888 +18889 +18890 +18891 +18892 +18893 +18894 +18895 +18896 +18897 +18898 +18899 +18900 +18901 +18902 +18903 +18904 +18905 +18906 +18907 +18908 +18909 +18910 +18911 +18912 +18913 +18914 +18915 +18916 +18917 +18918 +18919 +18920 +18921 +18922 +18923 +18924 +18925 +18926 +18927 +18928 +18929 +18930 +18931 +18932 +18933 +18934 +18935 +18936 +18937 +18938 +18939 +18940 +18941 +18942 +18943 +18944 +18945 +18946 +18947 +18948 +18949 +18950 +18951 +18952 +18953 +18954 +18955 +18956 +18957 +18958 +18959 +18960 +18961 +18962 +18963 +18964 +18965 +18966 +18967 +18968 +18969 +18970 +18971 +18972 +18973 +18974 +18975 +18976 +18977 +18978 +18979 +18980 +18981 +18982 +18983 +18984 +18985 +18986 +18987 +18988 +18989 +18990 +18991 +18992 +18993 +18994 +18995 +18996 +18997 +18998 +18999 +19000 +19001 +19002 +19003 +19004 +19005 +19006 +19007 +19008 +19009 +19010 +19011 +19012 +19013 +19014 +19015 +19016 +19017 +19018 +19019 +19020 +19021 +19022 +19023 +19024 +19025 +19026 +19027 +19028 +19029 +19030 +19031 +19032 +19033 +19034 +19035 +19036 +19037 +19038 +19039 +19040 +19041 +19042 +19043 +19044 +19045 +19046 +19047 +19048 +19049 +19050 +19051 +19052 +19053 +19054 +19055 +19056 +19057 +19058 +19059 +19060 +19061 +19062 +19063 +19064 +19065 +19066 +19067 +19068 +19069 +19070 +19071 +19072 +19073 +19074 +19075 +19076 +19077 +19078 +19079 +19080 +19081 +19082 +19083 +19084 +19085 +19086 +19087 +19088 +19089 +19090 +19091 +19092 +19093 +19094 +19095 +19096 +19097 +19098 +19099 +19100 +19101 +19102 +19103 +19104 +19105 +19106 +19107 +19108 +19109 +19110 +19111 +19112 +19113 +19114 +19115 +19116 +19117 +19118 +19119 +19120 +19121 +19122 +19123 +19124 +19125 +19126 +19127 +19128 +19129 +19130 +19131 +19132 +19133 +19134 +19135 +19136 +19137 +19138 +19139 +19140 +19141 +19142 +19143 +19144 +19145 +19146 +19147 +19148 +19149 +19150 +19151 +19152 +19153 +19154 +19155 +19156 +19157 +19158 +19159 +19160 +19161 +19162 +19163 +19164 +19165 +19166 +19167 +19168 +19169 +19170 +19171 +19172 +19173 +19174 +19175 +19176 +19177 +19178 +19179 +19180 +19181 +19182 +19183 +19184 +19185 +19186 +19187 +19188 +19189 +19190 +19191 +19192 +19193 +19194 +19195 +19196 +19197 +19198 +19199 +19200 +19201 +19202 +19203 +19204 +19205 +19206 +19207 +19208 +19209 +19210 +19211 +19212 +19213 +19214 +19215 +19216 +19217 +19218 +19219 +19220 +19221 +19222 +19223 +19224 +19225 +19226 +19227 +19228 +19229 +19230 +19231 +19232 +19233 +19234 +19235 +19236 +19237 +19238 +19239 +19240 +19241 +19242 +19243 +19244 +19245 +19246 +19247 +19248 +19249 +19250 +19251 +19252 +19253 +19254 +19255 +19256 +19257 +19258 +19259 +19260 +19261 +19262 +19263 +19264 +19265 +19266 +19267 +19268 +19269 +19270 +19271 +19272 +19273 +19274 +19275 +19276 +19277 +19278 +19279 +19280 +19281 +19282 +19283 +19284 +19285 +19286 +19287 +19288 +19289 +19290 +19291 +19292 +19293 +19294 +19295 +19296 +19297 +19298 +19299 +19300 +19301 +19302 +19303 +19304 +19305 +19306 +19307 +19308 +19309 +19310 +19311 +19312 +19313 +19314 +19315 +19316 +19317 +19318 +19319 +19320 +19321 +19322 +19323 +19324 +19325 +19326 +19327 +19328 +19329 +19330 +19331 +19332 +19333 +19334 +19335 +19336 +19337 +19338 +19339 +19340 +19341 +19342 +19343 +19344 +19345 +19346 +19347 +19348 +19349 +19350 +19351 +19352 +19353 +19354 +19355 +19356 +19357 +19358 +19359 +19360 +19361 +19362 +19363 +19364 +19365 +19366 +19367 +19368 +19369 +19370 +19371 +19372 +19373 +19374 +19375 +19376 +19377 +19378 +19379 +19380 +19381 +19382 +19383 +19384 +19385 +19386 +19387 +19388 +19389 +19390 +19391 +19392 +19393 +19394 +19395 +19396 +19397 +19398 +19399 +19400 +19401 +19402 +19403 +19404 +19405 +19406 +19407 +19408 +19409 +19410 +19411 +19412 +19413 +19414 +19415 +19416 +19417 +19418 +19419 +19420 +19421 +19422 +19423 +19424 +19425 +19426 +19427 +19428 +19429 +19430 +19431 +19432 +19433 +19434 +19435 +19436 +19437 +19438 +19439 +19440 +19441 +19442 +19443 +19444 +19445 +19446 +19447 +19448 +19449 +19450 +19451 +19452 +19453 +19454 +19455 +19456 +19457 +19458 +19459 +19460 +19461 +19462 +19463 +19464 +19465 +19466 +19467 +19468 +19469 +19470 +19471 +19472 +19473 +19474 +19475 +19476 +19477 +19478 +19479 +19480 +19481 +19482 +19483 +19484 +19485 +19486 +19487 +19488 +19489 +19490 +19491 +19492 +19493 +19494 +19495 +19496 +19497 +19498 +19499 +19500 +19501 +19502 +19503 +19504 +19505 +19506 +19507 +19508 +19509 +19510 +19511 +19512 +19513 +19514 +19515 +19516 +19517 +19518 +19519 +19520 +19521 +19522 +19523 +19524 +19525 +19526 +19527 +19528 +19529 +19530 +19531 +19532 +19533 +19534 +19535 +19536 +19537 +19538 +19539 +19540 +19541 +19542 +19543 +19544 +19545 +19546 +19547 +19548 +19549 +19550 +19551 +19552 +19553 +19554 +19555 +19556 +19557 +19558 +19559 +19560 +19561 +19562 +19563 +19564 +19565 +19566 +19567 +19568 +19569 +19570 +19571 +19572 +19573 +19574 +19575 +19576 +19577 +19578 +19579 +19580 +19581 +19582 +19583 +19584 +19585 +19586 +19587 +19588 +19589 +19590 +19591 +19592 +19593 +19594 +19595 +19596 +19597 +19598 +19599 +19600 +19601 +19602 +19603 +19604 +19605 +19606 +19607 +19608 +19609 +19610 +19611 +19612 +19613 +19614 +19615 +19616 +19617 +19618 +19619 +19620 +19621 +19622 +19623 +19624 +19625 +19626 +19627 +19628 +19629 +19630 +19631 +19632 +19633 +19634 +19635 +19636 +19637 +19638 +19639 +19640 +19641 +19642 +19643 +19644 +19645 +19646 +19647 +19648 +19649 +19650 +19651 +19652 +19653 +19654 +19655 +19656 +19657 +19658 +19659 +19660 +19661 +19662 +19663 +19664 +19665 +19666 +19667 +19668 +19669 +19670 +19671 +19672 +19673 +19674 +19675 +19676 +19677 +19678 +19679 +19680 +19681 +19682 +19683 +19684 +19685 +19686 +19687 +19688 +19689 +19690 +19691 +19692 +19693 +19694 +19695 +19696 +19697 +19698 +19699 +19700 +19701 +19702 +19703 +19704 +19705 +19706 +19707 +19708 +19709 +19710 +19711 +19712 +19713 +19714 +19715 +19716 +19717 +19718 +19719 +19720 +19721 +19722 +19723 +19724 +19725 +19726 +19727 +19728 +19729 +19730 +19731 +19732 +19733 +19734 +19735 +19736 +19737 +19738 +19739 +19740 +19741 +19742 +19743 +19744 +19745 +19746 +19747 +19748 +19749 +19750 +19751 +19752 +19753 +19754 +19755 +19756 +19757 +19758 +19759 +19760 +19761 +19762 +19763 +19764 +19765 +19766 +19767 +19768 +19769 +19770 +19771 +19772 +19773 +19774 +19775 +19776 +19777 +19778 +19779 +19780 +19781 +19782 +19783 +19784 +19785 +19786 +19787 +19788 +19789 +19790 +19791 +19792 +19793 +19794 +19795 +19796 +19797 +19798 +19799 +19800 +19801 +19802 +19803 +19804 +19805 +19806 +19807 +19808 +19809 +19810 +19811 +19812 +19813 +19814 +19815 +19816 +19817 +19818 +19819 +19820 +19821 +19822 +19823 +19824 +19825 +19826 +19827 +19828 +19829 +19830 +19831 +19832 +19833 +19834 +19835 +19836 +19837 +19838 +19839 +19840 +19841 +19842 +19843 +19844 +19845 +19846 +19847 +19848 +19849 +19850 +19851 +19852 +19853 +19854 +19855 +19856 +19857 +19858 +19859 +19860 +19861 +19862 +19863 +19864 +19865 +19866 +19867 +19868 +19869 +19870 +19871 +19872 +19873 +19874 +19875 +19876 +19877 +19878 +19879 +19880 +19881 +19882 +19883 +19884 +19885 +19886 +19887 +19888 +19889 +19890 +19891 +19892 +19893 +19894 +19895 +19896 +19897 +19898 +19899 +19900 +19901 +19902 +19903 +19904 +19905 +19906 +19907 +19908 +19909 +19910 +19911 +19912 +19913 +19914 +19915 +19916 +19917 +19918 +19919 +19920 +19921 +19922 +19923 +19924 +19925 +19926 +19927 +19928 +19929 +19930 +19931 +19932 +19933 +19934 +19935 +19936 +19937 +19938 +19939 +19940 +19941 +19942 +19943 +19944 +19945 +19946 +19947 +19948 +19949 +19950 +19951 +19952 +19953 +19954 +19955 +19956 +19957 +19958 +19959 +19960 +19961 +19962 +19963 +19964 +19965 +19966 +19967 +19968 +19969 +19970 +19971 +19972 +19973 +19974 +19975 +19976 +19977 +19978 +19979 +19980 +19981 +19982 +19983 +19984 +19985 +19986 +19987 +19988 +19989 +19990 +19991 +19992 +19993 +19994 +19995 +19996 +19997 +19998 +19999 +20000 diff --git a/tests/fixtures/sort/ext_sort.txt b/tests/fixtures/sort/ext_sort.txt new file mode 100644 index 000000000..a409d67e1 --- /dev/null +++ b/tests/fixtures/sort/ext_sort.txt @@ -0,0 +1,20000 @@ +9155 +10575 +10442 +15874 +17013 +12130 +15558 +18263 +6574 +8957 +9851 +16606 +12331 +9865 +13795 +270 +6590 +11141 +4620 +5945 +10904 +8652 +8442 +8907 +1935 +13100 +3961 +7538 +4159 +11986 +4394 +9321 +15560 +5264 +5121 +11532 +6980 +2807 +19760 +8032 +5158 +13698 +4458 +9106 +3773 +1625 +9914 +8287 +5155 +14326 +18137 +19522 +9270 +6153 +1920 +12517 +13259 +17618 +9930 +3630 +15924 +4540 +263 +14212 +15620 +11328 +8704 +17848 +1614 +12587 +17970 +4542 +11976 +12885 +8743 +16323 +14582 +12101 +12472 +12620 +10713 +5148 +7522 +2417 +8602 +7860 +19596 +2892 +13359 +7731 +3707 +4628 +4710 +5642 +1610 +4784 +10128 +16341 +14168 +5829 +17901 +9447 +1041 +15193 +15260 +11224 +11723 +13368 +14011 +12200 +2001 +18479 +3965 +16642 +16680 +2384 +7557 +5539 +4305 +14588 +17386 +1359 +17721 +1142 +7287 +9946 +13139 +15022 +17237 +14454 +14358 +11297 +12485 +857 +19282 +117 +19179 +16040 +6978 +10743 +19161 +12308 +9509 +13233 +4666 +5850 +17040 +2473 +17323 +6728 +11500 +4401 +13887 +19944 +17601 +19731 +10715 +8327 +11196 +7944 +9068 +13253 +13913 +13419 +17649 +16894 +3333 +5747 +3000 +12724 +17772 +17341 +11854 +6042 +10362 +3438 +13828 +471 +17747 +13707 +17902 +8184 +3385 +19387 +46 +1848 +15848 +10768 +17481 +436 +17581 +4629 +13210 +5085 +19485 +44 +12549 +16407 +9646 +9884 +15802 +2461 +15134 +2936 +8156 +17411 +1228 +18200 +14616 +17443 +5512 +6017 +10137 +11424 +11940 +6655 +17669 +6147 +16570 +17281 +18915 +19689 +18013 +17895 +6925 +19766 +18634 +7195 +7001 +12335 +3908 +18754 +4833 +12486 +12892 +18879 +3691 +1693 +158 +8682 +1029 +3429 +644 +1664 +13972 +5352 +15422 +1928 +14561 +3322 +3570 +6298 +2251 +12216 +19823 +12658 +2081 +4177 +13843 +18454 +13755 +9340 +6373 +3290 +2893 +16274 +16610 +12782 +7269 +5681 +19372 +7843 +14844 +16236 +16004 +1136 +5759 +19347 +7684 +7435 +5892 +7305 +2504 +7064 +13264 +8781 +1327 +19943 +18122 +1803 +9999 +13695 +9742 +8274 +744 +2252 +10524 +1434 +4601 +6071 +10489 +19626 +10745 +18312 +872 +19166 +3417 +9272 +1433 +9431 +6486 +3532 +18452 +17830 +9835 +2495 +10475 +16725 +16019 +6594 +11355 +13055 +14782 +11924 +18838 +3563 +428 +12993 +14223 +6658 +2783 +1726 +6929 +13053 +19175 +8564 +4867 +19604 +17416 +9347 +2275 +16381 +9817 +11176 +14576 +6906 +9805 +14149 +2241 +11030 +453 +835 +15452 +2879 +4018 +17396 +16133 +1301 +3450 +10182 +2389 +19201 +4443 +14368 +2537 +7452 +1583 +13955 +3875 +7479 +17561 +3247 +16310 +5253 +8899 +6523 +10260 +13065 +11077 +15109 +7249 +12046 +5313 +8914 +19949 +2196 +3654 +7145 +12166 +18340 +17929 +12466 +7866 +3831 +15095 +2506 +17691 +12992 +6591 +9661 +19538 +2161 +3991 +6766 +18180 +182 +15952 +9709 +19601 +13427 +19071 +12698 +12157 +7963 +3485 +19327 +6029 +12948 +2261 +2844 +4864 +12148 +9187 +6695 +8171 +19771 +3782 +14122 +11658 +330 +10750 +6932 +4436 +15622 +5710 +18750 +5765 +10545 +10897 +16609 +9183 +802 +14708 +3423 +11557 +19098 +3641 +14490 +3249 +1355 +16886 +18500 +15309 +8010 +18543 +2342 +3813 +2135 +9055 +15148 +12720 +3253 +737 +11788 +253 +13352 +1521 +13949 +1957 +10884 +2273 +14730 +13979 +2401 +595 +12697 +9932 +11372 +11915 +760 +10930 +10659 +6472 +19706 +6488 +9730 +17705 +16085 +4134 +2070 +4852 +5122 +16359 +7044 +8510 +10868 +10172 +510 +11550 +260 +11181 +3018 +3668 +904 +10271 +17104 +12764 +3364 +1878 +2803 +14040 +1149 +15626 +12809 +11008 +2903 +8352 +12761 +13470 +11258 +1400 +8381 +12422 +8402 +3919 +7164 +17263 +18167 +4549 +7654 +6034 +8438 +19451 +12122 +5167 +19334 +9771 +1111 +2932 +18324 +4897 +12687 +1720 +2767 +7616 +6431 +16918 +579 +16504 +4559 +14384 +6337 +4008 +7937 +1086 +5314 +12489 +5211 +17500 +19641 +14815 +7967 +8140 +5239 +2571 +18601 +16197 +5142 +12714 +14895 +3432 +3995 +9206 +7668 +8703 +1661 +4315 +5941 +6849 +2505 +19547 +11736 +5319 +986 +7846 +16050 +9227 +13121 +1012 +18236 +4888 +3885 +11135 +17395 +4303 +3836 +18544 +9807 +15248 +10626 +13846 +17286 +5581 +14007 +2062 +4619 +14864 +13869 +2442 +17728 +11590 +16382 +19117 +19446 +6843 +8694 +14439 +3453 +2700 +9821 +8089 +9645 +14679 +4356 +11980 +5408 +2668 +8053 +1647 +19959 +17083 +14916 +8841 +2319 +11984 +12867 +4292 +4633 +1492 +10716 +12880 +8243 +3929 +2225 +2943 +17578 +12138 +5648 +9614 +15487 +18868 +10779 +12716 +403 +2908 +7040 +4772 +19912 +14823 +5045 +14250 +19733 +13073 +6947 +10387 +8021 +5201 +4488 +18161 +4100 +1422 +16865 +7646 +4370 +17271 +19121 +9808 +2613 +15130 +18893 +11654 +5903 +15058 +12954 +6480 +5764 +15830 +17813 +10224 +6324 +4412 +5607 +9497 +7849 +1291 +9401 +19126 +15067 +10197 +18480 +6258 +8590 +17216 +7437 +16511 +18807 +4267 +18809 +14488 +2345 +4395 +11054 +7624 +3708 +896 +18870 +19517 +2950 +7950 +19529 +155 +19786 +138 +7168 +2130 +10699 +19821 +19156 +11071 +8912 +9515 +17234 +10388 +195 +19909 +16687 +18628 +5870 +10436 +16939 +6562 +8748 +4929 +6282 +9147 +12262 +18067 +17029 +5593 +11043 +15042 +19516 +7618 +13353 +3793 +108 +8255 +8446 +9675 +6111 +5162 +17668 +11558 +17151 +4502 +4353 +19681 +9842 +9229 +13095 +6533 +5706 +14713 +130 +4435 +918 +5195 +4348 +13863 +1247 +13221 +11826 +14356 +7315 +19382 +14025 +10586 +13495 +2418 +13930 +17352 +10751 +5332 +19001 +148 +12921 +8219 +11851 +12210 +5136 +16621 +309 +11602 +3224 +2787 +831 +2423 +14749 +12053 +9389 +15413 +17125 +7143 +19332 +10719 +10433 +4229 +6003 +3725 +17978 +3086 +17007 +6993 +16758 +6015 +16832 +2292 +16344 +13161 +7474 +12363 +10615 +19599 +6856 +10583 +19326 +15872 +3318 +17397 +14798 +8994 +13566 +949 +8506 +17722 +14915 +16273 +14643 +12334 +6859 +3148 +16594 +1809 +16350 +10681 +5696 +19560 +11625 +12856 +14535 +12292 +10444 +14707 +19226 +15085 +17377 +10766 +1833 +14675 +6297 +11865 +9647 +1269 +11299 +18222 +12310 +5715 +3293 +5580 +16180 +3198 +5655 +14952 +4632 +18179 +14464 +18546 +19062 +14401 +10772 +11719 +12926 +6267 +9163 +7559 +6358 +6903 +12503 +842 +90 +11725 +5854 +2083 +9137 +3292 +2781 +10855 +13137 +1993 +6499 +1395 +4910 +9378 +445 +13443 +7039 +15416 +7434 +13093 +14981 +10377 +13571 +5652 +19404 +14335 +8636 +13404 +18715 +12722 +19189 +9989 +12956 +7993 +7848 +5771 +16660 +9063 +8452 +4845 +14997 +6883 +11982 +1931 +9618 +11593 +12712 +16973 +17452 +9993 +3728 +18598 +15326 +15360 +7612 +18837 +5048 +9659 +14572 +732 +10948 +8383 +15938 +9822 +15502 +231 +9438 +13629 +4987 +16527 +7250 +17364 +14600 +13817 +7983 +7154 +4103 +14313 +6931 +13983 +14009 +11778 +5478 +11543 +13446 +13037 +17142 +8307 +17676 +16495 +2274 +15324 +8828 +7999 +861 +6566 +17141 +9039 +694 +2882 +10144 +17051 +15743 +11263 +17156 +9122 +9916 +14398 +7063 +6237 +5638 +15763 +1781 +2049 +10105 +1246 +7266 +7500 +18846 +5535 +15135 +18616 +10987 +4354 +18659 +18721 +4950 +14666 +12702 +663 +11894 +11411 +19965 +3137 +5388 +7768 +5069 +7826 +19221 +1176 +18638 +3314 +36 +17709 +15578 +14710 +4791 +6413 +5461 +7776 +13204 +2302 +6382 +9622 +8769 +8967 +4208 +15083 +2464 +9639 +19391 +14833 +9760 +1577 +681 +236 +1985 +12792 +10147 +11477 +14536 +8012 +18444 +13183 +7810 +7717 +8593 +1654 +18958 +19769 +5659 +13596 +8469 +17760 +16629 +17402 +2140 +16696 +14869 +8103 +18250 +6429 +469 +7934 +16281 +16623 +7074 +4984 +9624 +16339 +7825 +19700 +6866 +15914 +19964 +19336 +13712 +11268 +3216 +10218 +12589 +4453 +14596 +16420 +1512 +18977 +11886 +11637 +12934 +496 +18503 +7326 +1082 +19257 +12682 +5177 +12065 +1500 +8097 +17636 +1973 +12448 +2209 +6165 +17875 +13036 +11911 +17546 +18476 +14284 +12114 +15674 +7720 +3484 +10471 +6089 +3441 +12887 +19772 +5168 +16222 +18075 +6793 +1074 +3428 +7593 +8465 +14048 +3463 +15554 +15329 +4352 +6135 +11756 +4268 +1482 +3523 +14390 +3681 +3090 +15748 +2656 +9255 +6536 +10528 +8574 +1926 +18022 +8625 +19593 +12288 +10632 +18212 +9782 +1875 +2082 +13643 +16171 +8385 +15465 +5751 +9420 +1004 +7771 +12499 +12525 +6097 +1513 +15921 +15484 +12663 +17072 +4693 +15491 +9584 +5864 +4977 +7841 +15412 +7923 +3536 +2540 +6911 +10031 +2206 +18931 +1856 +205 +8152 +12603 +4552 +19026 +9223 +10389 +11271 +16914 +16266 +9829 +8124 +3549 +11605 +16055 +5537 +19801 +11601 +3499 +7699 +735 +9037 +18773 +11078 +18295 +11935 +16267 +5072 +1822 +16710 +5097 +2812 +13191 +12077 +9819 +5524 +17767 +10587 +10513 +1994 +19862 +18907 +7968 +15272 +7789 +2409 +54 +19531 +8092 +3347 +673 +5449 +18599 +11457 +9023 +18216 +151 +3325 +7491 +12272 +1089 +17925 +12543 +4855 +910 +8895 +2662 +17435 +11455 +6263 +6049 +10308 +14696 +1120 +1857 +14658 +14709 +1827 +9133 +3732 +2308 +5021 +19895 +5130 +2321 +8478 +13751 +14668 +12758 +6368 +925 +6567 +13960 +4392 +17533 +11140 +8696 +4902 +19715 +19592 +19009 +6990 +7368 +2884 +1761 +9096 +5776 +19203 +12034 +13669 +14639 +8126 +11779 +16533 +1682 +208 +7340 +6351 +6564 +16168 +11449 +15859 +14482 +19057 +13581 +5965 +18130 +1427 +8670 +16155 +17607 +3754 +5050 +8369 +10157 +7623 +4580 +10289 +14147 +4613 +6316 +8441 +13630 +19850 +15986 +1160 +17770 +19266 +83 +6643 +4694 +10353 +4909 +4727 +15659 +5584 +2641 +3698 +3165 +13039 +13312 +4756 +3598 +8958 +1898 +16121 +18728 +6332 +6539 +12401 +18019 +13123 +15388 +1972 +3111 +13250 +13944 +15408 +9078 +452 +14346 +7560 +2306 +3883 +6312 +3541 +5518 +3377 +19440 +11416 +6459 +19730 +1018 +17717 +19070 +2979 +7628 +3294 +14038 +17208 +16535 +14850 +7 +16427 +18239 +5066 +9609 +9194 +11734 +14200 +4917 +13090 +2490 +3735 +3829 +13487 +7105 +4108 +450 +10527 +3391 +13080 +3884 +9246 +14599 +12759 +9940 +5528 +1425 +3209 +4 +11016 +14852 +17253 +15713 +4653 +5849 +4787 +16819 +12491 +19370 +12594 +3657 +10088 +3741 +9981 +3779 +18367 +4760 +8416 +16092 +18242 +12718 +18491 +13873 +10163 +11696 +4073 +1570 +18443 +8328 +3675 +4282 +7763 +6861 +17516 +18429 +7823 +11156 +9205 +18297 +13738 +7811 +3578 +8944 +2115 +16468 +3907 +15293 +10044 +6555 +2826 +9838 +6750 +15892 +4279 +6867 +7938 +9788 +1115 +13625 +2146 +11581 +1096 +18237 +10236 +9201 +17460 +13171 +9963 +7202 +3966 +4294 +5573 +10232 +13791 +19875 +9298 +11298 +18253 +10352 +14014 +10952 +16802 +17984 +3027 +11733 +3150 +3514 +5796 +16181 +16322 +15572 +9196 +9392 +6842 +13963 +7868 +4416 +5411 +4598 +12392 +8187 +8809 +1722 +14154 +7136 +10114 +806 +4506 +2210 +1234 +6487 +12601 +6345 +1222 +11032 +17837 +1436 +18510 +13192 +13409 +2916 +15236 +2171 +19849 +18365 +13536 +7186 +18356 +13562 +13042 +15819 +11301 +5777 +15869 +9462 +264 +6749 +6724 +19018 +3737 +4210 +13420 +16942 +8870 +14209 +19898 +882 +4021 +18094 +438 +15220 +12025 +19916 +8802 +16090 +7216 +13396 +19431 +15182 +11773 +2064 +2735 +4187 +14432 +6551 +14174 +4687 +14750 +19246 +5868 +8400 +16515 +3028 +1855 +15898 +12605 +2643 +9103 +7130 +10895 +9089 +2771 +19857 +10033 +19263 +9119 +14706 +14207 +18470 +11743 +18163 +859 +13245 +14698 +11764 +9748 +15584 +19089 +13388 +18928 +3289 +19660 +16732 +16194 +70 +19077 +2745 +11216 +5550 +15300 +7565 +1766 +9684 +18285 +3060 +14989 +5227 +2072 +8080 +14517 +12391 +13686 +7525 +14548 +11403 +8754 +16046 +10953 +7112 +5592 +6882 +9080 +4589 +13437 +16114 +5753 +6118 +16947 +4566 +9633 +7033 +13356 +9164 +13671 +9689 +11207 +18007 +2402 +15376 +16038 +6571 +4875 +3055 +16146 +18484 +19778 +12574 +966 +16007 +6908 +10248 +9118 +1820 +1830 +14591 +15893 +13881 +6987 +5794 +13255 +12003 +4537 +18485 +3567 +7203 +19079 +6338 +8671 +15590 +19654 +2552 +10446 +14189 +3207 +9582 +12209 +18993 +6941 +14448 +18639 +18694 +5814 +10292 +8377 +10796 +10680 +15 +870 +1777 +15404 +2044 +4232 +3917 +5371 +7725 +8974 +8227 +6073 +12404 +9084 +19466 +14267 +8223 +7381 +7682 +632 +12671 +1325 +11845 +11113 +8412 +19417 +6357 +17205 +380 +4919 +7842 +14100 +16317 +15235 +14613 +18211 +7461 +19872 +9913 +11784 +13436 +1859 +6597 +13750 +10136 +14029 +10842 +1795 +682 +11266 +16047 +5758 +1194 +16086 +7301 +6798 +10818 +3495 +17518 +298 +16767 +18903 +15113 +10358 +5676 +18567 +4812 +15165 +9477 +2283 +11622 +13722 +3073 +16187 +2544 +2482 +7534 +18691 +14260 +274 +14682 +3231 +16237 +6606 +16494 +12612 +17826 +18266 +17652 +11954 +19394 +16356 +15781 +6498 +7613 +18308 +9852 +12495 +11569 +6822 +15384 +18414 +16240 +17425 +3619 +2998 +4060 +281 +12126 +13552 +17956 +8648 +5199 +11470 +19559 +2184 +4127 +1782 +11302 +8272 +7227 +4378 +6617 +17001 +5565 +12212 +15110 +12717 +9177 +203 +15928 +14386 +18671 +3664 +5577 +5825 +18883 +11563 +7790 +16821 +19075 +11407 +4067 +481 +15007 +10607 +1437 +16173 +10793 +647 +3305 +2691 +15054 +1132 +8766 +14500 +14137 +11852 +14871 +765 +16596 +1727 +2747 +9784 +9337 +16309 +16810 +16508 +12105 +1127 +13997 +17502 +17305 +18799 +12701 +5281 +13678 +4044 +18969 +14380 +19526 +15873 +8501 +299 +1764 +18487 +19620 +4094 +9237 +12970 +4240 +14861 +11285 +1470 +3766 +9496 +9656 +5356 +7509 +9637 +15046 +19038 +1072 +13336 +784 +18523 +6996 +15991 +581 +14929 +16799 +4522 +8701 +9889 +5016 +7199 +19270 +15705 +2611 +10397 +19448 +17801 +11447 +16190 +16130 +3153 +12318 +5677 +14727 +13117 +9800 +15894 +15709 +9081 +5403 +15579 +6359 +5576 +552 +19272 +11427 +4343 +11192 +12732 +4424 +17024 +7715 +12633 +2562 +12400 +4330 +16677 +1150 +6402 +18957 +9338 +4726 +10064 +8644 +17968 +79 +14922 +12178 +7837 +11881 +18551 +2096 +7547 +6879 +16607 +17532 +17944 +10284 +10009 +9868 +2088 +7165 +17038 +14403 +11088 +9668 +13714 +9125 +13804 +13547 +200 +11058 +19642 +3016 +5375 +14376 +5202 +3268 +8476 +14820 +12021 +17213 +12957 +8549 +15190 +5397 +3339 +8084 +15262 +10297 +14127 +18950 +14402 +11073 +5657 +10318 +13866 +9180 +18302 +3183 +2397 +11747 +9357 +2022 +10447 +16582 +19247 +14261 +19923 +14503 +16422 +8778 +18439 +19673 +1679 +9156 +14756 +8836 +792 +14801 +14510 +4556 +12417 +6110 +15972 +1030 +2328 +525 +16238 +11164 +11570 +17610 +4118 +11897 +9444 +14758 +2978 +16124 +7333 +6201 +14646 +819 +16867 +758 +4095 +19080 +15720 +17338 +18410 +12800 +16679 +10495 +6063 +10001 +18512 +14521 +6137 +18160 +5900 +3606 +15776 +9127 +13491 +17690 +9173 +5013 +3745 +13371 +9315 +4604 +14737 +4625 +7850 +3071 +2681 +13915 +3716 +7617 +12597 +3512 +3565 +19435 +7564 +17592 +9035 +12394 +2941 +6473 +13995 +18469 +11981 +17680 +1932 +19172 +1959 +16466 +2043 +11677 +7471 +7635 +12870 +10337 +15199 +2517 +17905 +10239 +5615 +2269 +13794 +2664 +8883 +10087 +9073 +10617 +5548 +10940 +19822 +11659 +13147 +12171 +3164 +17034 +9484 +17682 +7142 +3559 +12980 +11388 +7572 +17292 +3311 +3561 +7590 +14998 +7454 +2874 +6168 +937 +14366 +8090 +8710 +12599 +8443 +8970 +18892 +329 +3254 +7781 +16904 +4896 +12343 +11478 +10553 +4132 +17450 +12864 +3510 +1402 +12055 +8183 +165 +9948 +3214 +6878 +5933 +228 +3230 +1281 +15215 +10404 +3367 +4014 +6632 +9043 +4818 +6661 +19398 +18517 +7085 +17011 +13682 +15769 +13317 +3843 +11294 +7302 +8179 +12539 +16626 +3607 +15203 +2254 +5182 +16659 +12406 +12573 +5917 +2373 +11062 +17231 +3916 +3105 +9085 +18613 +11408 +17161 +17366 +11591 +4238 +4660 +2848 +5891 +12804 +10288 +8174 +14239 +12514 +4654 +4250 +2852 +6314 +2368 +4331 +10102 +9215 +4154 +9775 +15534 +16486 +6072 +17897 +6804 +9025 +6688 +8484 +7362 +7728 +18465 +17285 +7308 +13520 +11839 +8577 +3723 +4068 +5628 +18978 +9294 +13440 +3112 +13821 +12027 +13670 +1564 +15275 +10179 +9740 +7109 +8716 +11499 +11306 +4092 +15096 +18461 +19703 +15349 +1079 +15249 +4831 +8609 +5402 +6252 +12462 +17598 +8256 +4873 +901 +1505 +10888 +13587 +16191 +1683 +13035 +13771 +4377 +17164 +4385 +9517 +18346 +10202 +807 +3176 +3393 +10311 +11856 +4420 +17807 +16514 +17296 +10185 +7689 +2121 +603 +19848 +14957 +6235 +3765 +6958 +16961 +16835 +11823 +15218 +3673 +14694 +9799 +10988 +11521 +16713 +2904 +6079 +3201 +18911 +4517 +5304 +6689 +3034 +17456 +14288 +3025 +9030 +10834 +72 +3640 +5419 +17997 +12664 +17332 +3392 +18034 +8415 +10580 +2582 +7067 +12922 +8116 +6069 +9456 +3945 +12121 +3205 +1471 +18203 +4563 +11758 +15828 +12696 +3029 +5878 +7584 +18021 +9781 +13367 +12159 +8708 +17930 +18482 +3810 +19468 +9992 +9466 +16650 +8319 +8075 +13026 +12750 +4248 +3756 +6904 +5987 +10300 +19513 +7835 +14553 +7997 +13118 +13895 +14201 +828 +19873 +14254 +14321 +6442 +7713 +18594 +9283 +3803 +18739 +5800 +8088 +11797 +12172 +17438 +8013 +6444 +2526 +19303 +7752 +5243 +10222 +14888 +1987 +12728 +2281 +14094 +13189 +18421 +232 +17792 +12315 +5112 +10252 +14472 +11282 +13205 +8666 +16375 +13298 +53 +16122 +15019 +18453 +11551 +17430 +10461 +11789 +5997 +2790 +18547 +12547 +6067 +19149 +2820 +18032 +10802 +10133 +18017 +7667 +13452 +16972 +4192 +2519 +15381 +12924 +10438 +19016 +4955 +3903 +10237 +15152 +2860 +19938 +5412 +14294 +2867 +15553 +8672 +13106 +11603 +408 +546 +6437 +7921 +7487 +11587 +14507 +4111 +15628 +13295 +8340 +18279 +7259 +14649 +19506 +14785 +10293 +14742 +19136 +6265 +1087 +12052 +15843 +18727 +5484 +845 +15185 +13976 +15646 +17664 +1068 +4335 +8722 +12020 +488 +2706 +282 +3534 +1294 +3043 +11397 +2713 +2975 +944 +10208 +100 +19744 +4924 +2107 +17519 +7385 +2507 +17006 +2763 +7137 +17314 +15094 +12296 +12237 +10544 +2758 +76 +12678 +10763 +3780 +17067 +8968 +975 +3659 +13505 +15863 +17766 +3363 +5423 +7463 +2178 +4126 +14612 +7382 +19859 +676 +12313 +9295 +8640 +11309 +10754 +13801 +12608 +13889 +18117 +16269 +3412 +10649 +12279 +3508 +7951 +13929 +12250 +17637 +8922 +18762 +18329 +14702 +9507 +19961 +850 +9597 +4414 +13247 +16822 +10422 +19205 +3713 +3436 +7248 +4243 +10902 +8762 +15545 +12653 +18681 +13455 +14691 +10967 +3489 +18920 +12677 +9470 +2134 +15958 +350 +19280 +17241 +19725 +3551 +7246 +19535 +7724 +18758 +1731 +3332 +17522 +467 +2625 +9608 +14969 +1323 +9233 +16160 +14099 +5302 +8554 +4449 +5333 +9273 +16185 +14522 +17604 +19605 +6303 +12330 +15146 +14008 +7440 +541 +11439 +1576 +16787 +16643 +3187 +1217 +9606 +833 +7777 +15549 +18991 +11612 +4806 +5366 +12275 +19215 +19449 +12385 +8947 +15472 +13739 +14947 +1225 +12670 +6053 +2155 +5435 +15931 +13807 +16673 +8413 +15099 +10069 +13744 +9676 +12002 +16917 +1192 +13262 +15795 +12097 +4419 +2704 +15031 +10581 +13753 +5473 +721 +8536 +15470 +12176 +11335 +3264 +1075 +3213 +12203 +1035 +18985 +621 +4571 +832 +18668 +1008 +6614 +10274 +7827 +18731 +2421 +10075 +15523 +2499 +3761 +13927 +7374 +2263 +4507 +209 +8779 +492 +9195 +18556 +10960 +16021 +10464 +5662 +18906 +13364 +13332 +13413 +8647 +2909 +6985 +16880 +16692 +17599 +788 +14233 +7122 +14248 +14319 +9678 +7149 +10526 +1587 +19510 +5975 +4104 +3319 +9437 +996 +4384 +6922 +5501 +17736 +3483 +7518 +8495 +368 +1461 +9559 +15729 +6638 +5407 +13111 +14491 +8824 +6615 +2284 +14186 +10279 +11785 +10097 +16919 +14273 +15533 +4079 +18344 +2175 +15635 +14883 +4928 +8034 +19930 +13460 +5421 +8072 +18176 +18157 +14272 +6271 +15237 +8601 +12886 +8145 +3390 +11900 +8524 +3615 +3893 +17727 +3817 +16812 +1177 +19385 +2759 +14657 +11853 +19444 +12660 +3987 +2960 +16110 +12808 +1706 +9939 +11800 +4441 +14638 +459 +13076 +18192 +10061 +18226 +8324 +19067 +17264 +14165 +19323 +3747 +9056 +4321 +9546 +8030 +2356 +18989 +16340 +11970 +16797 +14564 +5216 +359 +1881 +17087 +11333 +18725 +10957 +5118 +2959 +4838 +12425 +14879 +11620 +5585 +12994 +3695 +18873 +2286 +10737 +16869 +11498 +18970 +4309 +772 +4029 +18787 +8505 +4596 +2731 +12749 +1349 +16805 +7028 +14818 +5098 +7543 +6474 +8697 +4439 +5308 +12946 +18423 +13965 +11975 +2260 +7046 +17257 +15904 +1114 +7306 +5780 +2764 +8788 +19176 +15016 +7772 +5060 +10836 +9873 +11267 +10981 +8305 +5044 +5872 +19841 +7894 +8600 +19411 +5946 +13648 +255 +2447 +1558 +490 +17820 +17806 +15541 +893 +6841 +15371 +11170 +10039 +9423 +17294 +18620 +8311 +17065 +968 +3834 +545 +16969 +12174 +4056 +16630 +8260 +8432 +10238 +14779 +4390 +2883 +14841 +10338 +13475 +7354 +14736 +13167 +2772 +18162 +15228 +12305 +12719 +1644 +10361 +7441 +17815 +11553 +8711 +1066 +5487 +9175 +9275 +5210 +18973 +13240 +17780 +2550 +12982 +14832 +10927 +4661 +16513 +12471 +15925 +2872 +12541 +17750 +18498 +6578 +3611 +17994 +13313 +22 +18334 +2349 +1415 +16900 +13975 +3906 +7735 +9625 +11194 +18855 +11188 +16154 +10403 +3064 +15075 +1773 +5986 +8845 +12083 +920 +3050 +17827 +5658 +16020 +17909 +7881 +4933 +12873 +8433 +8631 +18760 +15821 +7722 +11460 +17342 +2034 +17490 +10780 +883 +14097 +15098 +14621 +6636 +5720 +1124 +16757 +10874 +15537 +1557 +10173 +17788 +1749 +8910 +13488 +11561 +18073 +15930 +12831 +14569 +2545 +11320 +11238 +19196 +3753 +12191 +1336 +17805 +8758 +8200 +7914 +2095 +8798 +6651 +11808 +2970 +19186 +15383 +19024 +8654 +14744 +12256 +1023 +963 +7280 +8548 +11983 +1423 +3704 +17529 +10627 +743 +3001 +16400 +5489 +5010 +180 +5853 +13238 +12358 +15963 +14717 +11350 +3159 +18665 +14449 +15660 +19809 +18975 +2655 +306 +4030 +7739 +8509 +14567 +8664 +9500 +13230 +16056 +567 +15875 +2716 +18559 +17496 +1212 +8394 +1828 +864 +16868 +16018 +4259 +2470 +10454 +14166 +5460 +4926 +10011 +3869 +1324 +13729 +5361 +11782 +9954 +10348 +16472 +11978 +10548 +2312 +14032 +4262 +4674 +1050 +855 +6933 +18283 +15984 +15960 +609 +5280 +7784 +3261 +10588 +13579 +19058 +13563 +15211 +17376 +18894 +13797 +14550 +15929 +11391 +19746 +2497 +2762 +5351 +3573 +7663 +13316 +5052 +18248 +10496 +9108 +17914 +1589 +10976 +12564 +5640 +7052 +3650 +19288 +17354 +5334 +1612 +8429 +19457 +12336 +844 +17372 +2722 +4020 +13935 +2141 +13269 +4597 +14880 +17706 +10707 +4160 +8588 +7049 +909 +17520 +17317 +18777 +10 +18364 +11199 +17916 +11227 +1696 +14325 +12214 +7281 +14539 +16857 +1591 +6780 +6432 +8874 +10189 +9146 +15350 +5802 +18704 +2868 +4437 +172 +4893 +14474 +13974 +13089 +5457 +10797 +13858 +6339 +821 +8035 +10046 +14123 +19109 +15420 +16257 +11615 +7462 +8867 +8253 +13737 +20 +107 +17031 +13078 +8634 +1974 +14051 +16800 +8299 +19040 +17896 +9109 +7124 +12631 +17931 +1890 +6823 +8000 +4767 +2295 +15732 +1915 +10664 +15103 +9049 +18741 +17887 +3320 +12120 +9574 +17724 +9563 +6734 +2316 +31 +3671 +4605 +1084 +9813 +6435 +520 +19309 +3978 +6141 +17935 +6068 +18886 +3372 +4828 +99 +14056 +13544 +17509 +853 +14034 +18028 +11904 +16706 +11293 +8434 +7642 +12996 +14371 +3298 +17073 +267 +18636 +6920 +4529 +4574 +8390 +11360 +5255 +5601 +12248 +10778 +13658 +5784 +5476 +7671 +19286 +18144 +1927 +14367 +6863 +3805 +17986 +16333 +3227 +18191 +3301 +18071 +7150 +8780 +13907 +12393 +16166 +11973 +15147 +9541 +14193 +7318 +3712 +13708 +11345 +10427 +4323 +12026 +10245 +19967 +1405 +388 +16958 +2546 +7439 +10000 +16496 +16774 +6023 +16728 +13478 +2087 +11657 +14383 +4916 +18463 +4677 +4421 +17658 +8330 +1958 +11352 +7005 +19148 +5629 +14120 +5343 +10312 +18412 +17808 +770 +13891 +7283 +1547 +19493 +8091 +13424 +303 +3252 +12888 +2924 +12908 +13878 +13442 +15359 +5838 +11929 +5483 +8948 +15000 +8852 +12754 +7103 +17378 +1990 +2635 +1314 +19376 +10616 +10319 +569 +16644 +18499 +6572 +7071 +8093 +2218 +7749 +8333 +3011 +6260 +3358 +13649 +3821 +11575 +17623 +8707 +19293 +9585 +16920 +16461 +15377 +19556 +8660 +11807 +9534 +12326 +6289 +16950 +12742 +7347 +339 +12876 +15013 +6976 +16105 +15043 +18193 +1348 +14931 +583 +19476 +8455 +7530 +13897 +13373 +13684 +13655 +5635 +9475 +377 +16225 +12223 +17025 +13706 +11763 +8756 +834 +10618 +19095 +11740 +15561 +7160 +11522 +8688 +7273 +4477 +14811 +9832 +12352 +14281 +7299 +8576 +1020 +2961 +12276 +1193 +16897 +3473 +1185 +1198 +1353 +12881 +4077 +15642 +5511 +12840 +9750 +13101 +8026 +2282 +14224 +5480 +10594 +9903 +16358 +458 +18927 +19045 +9355 +7312 +3094 +12584 +6116 +16287 +3091 +967 +19947 +5920 +5979 +4883 +11583 +15033 +2160 +3479 +15639 +1204 +16521 +2777 +13765 +3517 +18076 +16376 +4802 +1197 +11109 +11985 +7585 +11451 +19169 +10648 +18223 +9554 +18688 +16116 +19412 +19371 +17483 +10726 +13054 +15421 +15637 +5861 +2355 +6874 +2054 +8052 +4763 +5086 +17479 +16568 +286 +11314 +5630 +7638 +4785 +18259 +15063 +5927 +1902 +9487 +4430 +12127 +7984 +5323 +14082 +10574 +11798 +19408 +13315 +17400 +12810 +6405 +10885 +18982 +6347 +6107 +12879 +14257 +6212 +5437 +12789 +17322 +2320 +17370 +1352 +14275 +8316 +19375 +2163 +10911 +12257 +13001 +3599 +13911 +12429 +5992 +16178 +11690 +6844 +10925 +11040 +13829 +3140 +5848 +18252 +14050 +18210 +13620 +4801 +6090 +8264 +1200 +13511 +14581 +18087 +15366 +4711 +5058 +18509 +10826 +10217 +19119 +6825 +12066 +17167 +2869 +262 +14434 +2468 +11247 +4607 +16667 +4655 +4009 +19788 +11664 +15257 +3981 +11512 +18254 +241 +14508 +5385 +14884 +508 +1010 +3533 +9474 +12500 +476 +19878 +8245 +6221 +16128 +11381 +13202 +13770 +9372 +15750 +9753 +12263 +7979 +8317 +16030 +1428 +3304 +5890 +10040 +16840 +11006 +103 +15437 +1237 +11452 +6471 +14688 +815 +10416 +14624 +15082 +12329 +10168 +7885 +8055 +6173 +1700 +14259 +19945 +3396 +11197 +5867 +14058 +2610 +7808 +18272 +19800 +11895 +5748 +5678 +19439 +18288 +7690 +17950 +4670 +6855 +13832 +2375 +16247 +11035 +8411 +7119 +8172 +19577 +18407 +16033 +9886 +618 +6757 +8663 +7024 +4404 +3775 +5904 +3496 +11175 +1178 +18651 +540 +14839 +715 +14427 +19913 +11250 +10014 +18733 +1745 +8873 +19741 +17799 +19854 +17954 +1250 +10275 +9579 +1962 +7512 +15141 +4980 +7405 +8585 +10711 +16475 +11717 +18530 +7008 +17316 +2051 +17537 +13796 +1477 +7907 +6230 +8903 +15358 +17492 +12721 +12635 +9425 +7215 +4657 +15880 +5509 +9729 +5597 +15150 +928 +4194 +1885 +3352 +8450 +12802 +4699 +12373 +19727 +1007 +6365 +5531 +4505 +5181 +6139 +2523 +10598 +10730 +12987 +2547 +3516 +680 +18052 +19078 +4568 +18027 +2796 +1980 +19124 +7240 +10159 +19131 +17431 +8023 +2190 +13260 +5143 +7262 +5203 +1040 +509 +8107 +6132 +547 +7740 +865 +3225 +14348 +3 +17698 +4518 +14070 +8993 +3922 +15050 +7829 +15039 +9427 +18434 +2757 +6380 +6816 +19475 +4465 +1804 +19770 +10633 +6774 +768 +5538 +14069 +4741 +18234 +16102 +7251 +2454 +18843 +9332 +5262 +16198 +9344 +2407 +10856 +1814 +12693 +3466 +12384 +14777 +2629 +16280 +9697 +6262 +6032 +6644 +5723 +8028 +8690 +13169 +19321 +3191 +2749 +10658 +8676 +4347 +17068 +19545 +2317 +2813 +8048 +17365 +16087 +5632 +7524 +5071 +16539 +3634 +18685 +1946 +12445 +16760 +18307 +8191 +6206 +18602 +2410 +11873 +8244 +2398 +9473 +19936 +14607 +13920 +4149 +17223 +13809 +9226 +5991 +16764 +19141 +5709 +8294 +1017 +14026 +13389 +10963 +19174 +8850 +16622 +8344 +11248 +3721 +16117 +5731 +2810 +12726 +3596 +3072 +18576 +13369 +5425 +5430 +8846 +15783 +9879 +12443 +4728 +17085 +3188 +16256 +15425 +19267 +1227 +6170 +12137 +4440 +10004 +7949 +8129 +11707 +11220 +1256 +17421 +2007 +1174 +10396 +16049 +9773 +606 +2132 +9313 +5728 +4251 +14010 +10174 +16283 +3203 +11755 +2623 +2621 +15918 +18477 +14574 +6723 +5389 +14221 +7239 +2113 +16454 +16901 +6715 +4547 +12311 +19231 +15456 +18515 +5515 +245 +13456 +15386 +19927 +13823 +13702 +19978 +12766 +8962 +18392 +11673 +12983 +19551 +11223 +12740 +5505 +5675 +3331 +3480 +15799 +19170 +14468 +160 +19132 +6284 +19458 +16980 +1611 +15836 +2937 +10568 +14133 +7288 +13865 +13763 +19851 +16933 +12944 +10334 +9367 +8276 +11863 +9117 +2688 +16561 +13508 +14352 +13068 +4508 +14538 +13431 +5919 +17369 +12828 +13981 +2997 +16843 +4373 +8359 +15842 +12756 +2176 +13393 +811 +3988 +19814 +5265 +14033 +10860 +9549 +8273 +5788 +1121 +18359 +16169 +5434 +1772 +18744 +1245 +11874 +7131 +15168 +2703 +3079 +18947 +12397 +11053 +14900 +13163 +18433 +9651 +1265 +3100 +12452 +688 +18261 +19883 +14141 +4545 +16244 +2580 +9951 +16887 +9712 +2110 +17558 +13218 +5065 +4403 +2004 +18790 +17525 +285 +19661 +3891 +14106 +4975 +1769 +2406 +2606 +9744 +19775 +6918 +15410 +16248 +13083 +5247 +16733 +4649 +7420 +19254 +2432 +11139 +16608 +2 +11832 +7230 +1347 +16937 +15047 +3078 +9306 +6563 +10611 +5911 +7459 +634 +17381 +18323 +15443 +12902 +16385 +17239 +12229 +12747 +13786 +16923 +123 +11850 +17773 +12220 +14943 +4254 +3024 +6683 +9112 +9558 +16065 +9754 +6114 +10785 +9192 +15021 +4091 +10762 +3976 +13894 +7588 +13490 +2973 +3498 +6313 +14176 +12108 +3786 +17893 +5156 +18260 +7296 +4740 +15948 +10610 +12735 +5967 +2618 +7175 +558 +16931 +14886 +17757 +13469 +1182 +4252 +16241 +13638 +6514 +12370 +917 +18078 +16360 +8323 +8702 +7892 +1787 +9268 +12665 +16747 +8786 +10053 +4096 +4359 +9432 +12741 +4991 +9333 +12123 +1365 +1734 +8892 +804 +1002 +8888 +14001 +19777 +1742 +7191 +6492 +17321 +2068 +991 +15616 +18921 +2856 +8685 +8401 +19251 +7252 +16467 +4786 +1337 +7162 +12561 +660 +18128 +16329 +15805 +8765 +8106 +13931 +4253 +1899 +8547 +9874 +10070 +19855 +7469 +9369 +10993 +18299 +14560 +421 +11541 +17590 +11107 +17359 +8500 +18818 +17552 +3470 +8992 +2177 +9326 +16060 +1450 +8070 +6250 +9581 +5212 +12286 +10537 +16717 +17325 +16510 +11787 +9036 +16108 +16005 +15393 +15649 +12496 +1824 +561 +10873 +19807 +1411 +6834 +19425 +4272 +7146 +9611 +18896 +18294 +17823 +4757 +14002 +2660 +3994 +14277 +969 +5206 +6735 +16567 +4575 +13624 +9110 +798 +15233 +15876 +13539 +16505 +15269 +12232 +9980 +15998 +7732 +14228 +14795 +3918 +11534 +7952 +11627 +16205 +16946 +17793 +9019 +3471 +1285 +239 +17953 +15475 +5977 +12189 +13401 +10815 +6292 +19903 +7327 +6760 +6556 +16262 +12474 +3330 +12228 +2534 +19901 +5445 +6362 +3049 +1628 +6325 +16137 +15806 +3920 +19692 +1698 +8638 +10448 +7519 +2776 +2619 +13450 +19679 +12958 +18504 +7800 +15653 +19708 +13496 +11813 +3969 +1617 +5836 +14118 +3660 +3449 +959 +17861 +17043 +16370 +3242 +16253 +11202 +17489 +5862 +1627 +18955 +19988 +1843 +7603 +2440 +15520 +11045 +7795 +19496 +9489 +9346 +6272 +11126 +18224 +1203 +18554 +11653 +18772 +16097 +9587 +7388 +8880 +19014 +543 +14562 +12990 +3748 +5647 +8797 +14232 +11971 +19479 +7610 +10091 +1271 +7820 +1673 +17328 +2594 +7343 +11236 +874 +3089 +7018 +16476 +2543 +6482 +17442 +67 +8750 +16804 +12381 +5006 +10131 +14996 +19473 +8490 +2193 +16635 +1165 +7183 +814 +17197 +6759 +6047 +10512 +2091 +9638 +204 +12493 +4565 +15707 +8232 +6329 +12355 +5023 +17163 +729 +11060 +6342 +14723 +11649 +16541 +15807 +10782 +8629 +6041 +11896 +8801 +17349 +10470 +9858 +14075 +14626 +19697 +6045 +6171 +9956 +49 +12119 +3902 +14407 +15065 +15294 +6202 +11537 +16072 +17906 +2861 +7833 +16529 +7693 +8692 +614 +8952 +17057 +17227 +4899 +4345 +6184 +16556 +12150 +6706 +17543 +19292 +18274 +10212 +15172 +18316 +3349 +6396 +2695 +1666 +4209 +18277 +14509 +7526 +14587 +13711 +2701 +11318 +7359 +7451 +14937 +2357 +12236 +9354 +4396 +15724 +774 +9248 +8741 +13075 +13720 +1303 +8266 +297 +12084 +10243 +16373 +17437 +4478 +1064 +15761 +8887 +3265 +7413 +14851 +18791 +16852 +667 +6673 +251 +16703 +16756 +5197 +8570 +18065 +13676 +11280 +7581 +14199 +1681 +1094 +10998 +14419 +15655 +3625 +2520 +13185 +7110 +15500 +6580 +8159 +3202 +14387 +10684 +19307 +5348 +14265 +13122 +11848 +1550 +12552 +8395 +6506 +17169 +14684 +7286 +12039 +1476 +5620 +8775 +14177 +19511 +1005 +15419 +1601 +1675 +17138 +9162 +9172 +10298 +7258 +16748 +12974 +7620 +3694 +16357 +7943 +11426 +5682 +6565 +10333 +15589 +12567 +2227 +9777 +18432 +613 +839 +13344 +12205 +7941 +19245 +14968 +15595 +19147 +11613 +1695 +7502 +13653 +16324 +803 +4036 +14420 +2923 +18862 +11313 +10819 +8182 +15715 +6773 +12703 +6414 +17412 +2858 +17174 +7742 +9877 +11493 +38 +15556 +16962 +2829 +6205 +11902 +396 +11129 +5880 +14792 +12441 +17998 +69 +13664 +11580 +6084 +3525 +9990 +7069 +12692 +11726 +6145 +16715 +10095 +4293 +8728 +2823 +19998 +11276 +11329 +17646 +8009 +35 +5078 +10708 +7392 +11222 +4618 +12078 +1442 +14970 +15156 +6881 +3971 +2911 +7753 +4560 +14590 +4355 +10084 +6390 +8986 +7987 +7601 +2294 +17913 +7088 +15866 +15701 +5670 +14004 +8526 +17111 +14183 +14182 +6203 +18037 +4071 +18852 +10211 +7609 +15179 +15823 +10365 +13672 +19276 +15221 +11722 +6340 +16977 +9780 +14231 +11031 +719 +9797 +10705 +6775 +15435 +2592 +5224 +11256 +1386 +15251 +3125 +441 +6043 +16791 +19722 +16489 +15714 +2712 +7068 +4070 +16193 +7334 +8960 +1948 +9576 +13483 +11828 +18934 +7745 +13740 +16678 +718 +19145 +9998 +18233 +15327 +13971 +2756 +9602 +11095 +16348 +97 +10999 +8721 +13599 +4590 +11814 +8074 +16174 +8943 +12654 +12045 +15975 +3539 +10518 +15246 +15548 +14337 +16401 +468 +17409 +11063 +3783 +13608 +4621 +1536 +18990 +4994 +8020 +2780 +638 +11465 +14086 +4173 +14497 +6868 +316 +2151 +3672 +6934 +12739 +1026 +17584 +15290 +18151 +12861 +8203 +17444 +18532 +17967 +15313 +16661 +15814 +14410 +9567 +8268 +1418 +16936 +17695 +14013 +5707 +15870 +16975 +17117 +11635 +18866 +16530 +10301 +16424 +9982 +2076 +19111 +1867 +9856 +8551 +13097 +1984 +11011 +10725 +16015 +371 +17 +2327 +5102 +13302 +9442 +13025 +5669 +4622 +1660 +7876 +18531 +6832 +13570 +11001 +8166 +15467 +16666 +1251 +13377 +5770 +7265 +3851 +726 +18150 +18646 +10270 +3239 +871 +7779 +141 +11644 +879 +18105 +14687 +17158 +18605 +3895 +5298 +5081 +3272 +16441 +7632 +7404 +18597 +8138 +5721 +10678 +8186 +17795 +140 +16811 +3569 +6559 +5141 +7292 +15254 +325 +12348 +11977 +9511 +13403 +14563 +8578 +8341 +18473 +5754 +4098 +19989 +9179 +5426 +8502 +7425 +2617 +9323 +8332 +13509 +3338 +9921 +16739 +3402 +6214 +15692 +17999 +14914 +18267 +4713 +8558 +6464 +17779 +15722 +5367 +15661 +3244 +12322 +4212 +6552 +6885 +14614 +15927 +218 +14159 +10385 +12655 +10478 +1455 +16632 +8738 +19565 +1549 +14692 +17387 +17918 +17168 +4199 +19484 +16932 +5324 +5631 +4647 +10460 +10110 +17033 +13275 +12807 +19811 +5860 +2595 +16139 +10339 +11386 +17716 +15566 +822 +8953 +19795 +14760 +9985 +15775 +3328 +7022 +5666 +15704 +19926 +12575 +16347 +1313 +9941 +2840 +15093 +14148 +13627 +14344 +11941 +19970 +269 +1318 +14241 +3019 +8419 +19940 +10171 +4662 +5194 +4841 +4117 +14623 +19575 +14416 +824 +938 +7891 +6103 +15516 +10614 +5925 +16818 +18166 +13515 +95 +15406 +19134 +11628 +17979 +1319 +2744 +5070 +18099 +2359 +16881 +7317 +10696 +7630 +13391 +15771 +7346 +18313 +8844 +16699 +12388 +19591 +6455 +14269 +1646 +11540 +8579 +12195 +6517 +10214 +3041 +4329 +16465 +14135 +2266 +8262 +7932 +16970 +7365 +15173 +16220 +18402 +18717 +8235 +12535 +15352 +3006 +14791 +3232 +18218 +15011 +17642 +3547 +753 +5095 +17252 +3986 +3626 +4497 +1981 +9866 +1851 +2915 +633 +3008 +19073 +1672 +16067 +1304 +2770 +17594 +10531 +1316 +3147 +17340 +8314 +4191 +5851 +19306 +12734 +9571 +7472 +1162 +762 +12501 +14104 +3462 +12118 +19537 +10867 +16985 +17121 +10331 +7207 +19291 +14819 +19171 +16103 +1810 +11822 +10167 +5644 +2750 +11956 +2638 +13430 +8726 +15164 +16921 +14834 +3228 +13214 +12320 +9083 +7657 +9446 +14592 +14480 +11566 +17878 +625 +1444 +19721 +16743 +17076 +2512 +19734 +5115 +8468 +16031 +6970 +12674 +17941 +12439 +16286 +15833 +611 +8489 +15367 +1186 +12645 +19794 +12851 +15045 +5815 +13926 +7450 +5447 +5587 +10840 +1473 +19572 +10193 +4492 +1052 +13756 +13386 +14158 +15816 +17053 +19329 +19381 +4953 +8058 +4528 +12776 +18810 +2164 +9786 +16531 +7587 +17128 +6062 +15194 +8386 +15312 +4862 +10516 +18457 +15501 +8014 +5049 +18686 +9979 +13290 +15217 +8258 +11885 +4992 +4804 +1051 +6808 +8996 +11544 +11998 +15433 +2593 +3503 +4553 +11775 +11830 +11155 +19097 +2350 +16824 +3867 +16311 +4512 +886 +9208 +17629 +15605 +10366 +15288 +1553 +14342 +5401 +7079 +4521 +17777 +13521 +11149 +10379 +18112 +11283 +19934 +11485 +11910 +14617 +14283 +514 +19618 +13292 +9308 +19877 +1636 +17647 +13923 +370 +14766 +15962 +10177 +9713 +18321 +14160 +6218 +13667 +5873 +8849 +10158 +7221 +6839 +11160 +11390 +147 +5220 +6953 +934 +13510 +15121 +15338 +1278 +1170 +7464 +2101 +11571 +9134 +7423 +6336 +15471 +17458 +7918 +3378 +7319 +19483 +8734 +9613 +2720 +11265 +6765 +8372 +16232 +10871 +8774 +19504 +6028 +1021 +11576 +1953 +15631 +5944 +12261 +612 +6211 +3979 +2476 +10484 +9700 +12035 +6418 +4433 +12461 +1613 +9820 +10101 +13982 +11020 +6507 +14873 +12825 +848 +2488 +15477 +17672 +6393 +11936 +13241 +7177 +18271 +16063 +5349 +378 +19032 +2933 +3890 +15297 +14979 +1748 +1585 +19406 +16633 +9888 +14586 +8125 +5082 +12337 +16759 +19325 +7031 +19274 +17178 +16104 +12095 +11438 +9745 +3021 +19471 +6770 +685 +11585 +14793 +18847 +6305 +2910 +14485 +15512 +4915 +2846 +19600 +19826 +17586 +7415 +17362 +19755 +15594 +11122 +8146 +12596 +19407 +14661 +18918 +740 +10393 +10191 +8210 +19827 +13785 +5128 +8170 +12032 +11174 +18322 +10912 +9443 +9480 +17635 +11351 +17084 +1498 +19752 +6420 +16387 +2785 +10149 +9115 +16305 +15375 +5907 +16639 +11511 +12763 +14669 +19299 +18490 +18933 +6629 +16839 +13719 +15680 +7349 +17553 +14295 +18195 +18256 +10788 +9975 +6476 +10935 +10392 +389 +3844 +19319 +17549 +1842 +10006 +10265 +6475 +9405 +11574 +14731 +8437 +669 +2441 +8966 +7289 +17410 +1950 +4738 +12818 +4793 +15009 +9634 +4324 +18380 +9530 +9671 +17036 +6731 +13583 +7000 +9502 +12830 +2057 +15810 +19666 +2205 +4409 +18016 +410 +5606 +5739 +13569 +4962 +18270 +5416 +6407 +13061 +9050 +1272 +8997 +13365 +5845 +13713 +14425 +2265 +10491 +592 +18033 +6223 +16491 +15591 +738 +5190 +826 +5386 +2798 +293 +19242 +14475 +6215 +10246 +9257 +6672 +7032 +11375 +17202 +11877 +4074 +9239 +14305 +13632 +14683 +5833 +505 +3046 +14960 +11614 +3942 +14130 +5686 +16352 +10081 +8296 +13752 +8627 +1530 +338 +448 +13840 +18948 +8403 +9402 +5948 +19030 +4638 +176 +2649 +15559 +1741 +8406 +3346 +6269 +15673 +10151 +5292 +1710 +11436 +10951 +978 +12139 +6625 +1317 +8876 +5074 +16409 +4326 +3646 +2822 +10979 +11204 +4072 +3444 +16458 +4057 +16554 +1263 +3146 +5159 +15116 +6853 +2358 +18281 +8954 +1703 +3167 +8099 +12532 +7680 +19549 +5730 +6675 +10926 +13175 +915 +2866 +17992 +3420 +9885 +8557 +4295 +2335 +9846 +11275 +9523 +11905 +19419 +15062 +8896 +10465 +7295 +15543 +13428 +4676 +19146 +10621 +8863 +15911 +17798 +18672 +10390 +3336 +7783 +18747 +12730 +5523 +13064 +11019 +6104 +2153 +12998 +19804 +8382 +11757 +7257 +1593 +7521 +3356 +18181 +17710 +2027 +6584 +16875 +16215 +8757 +14496 +15645 +9207 +13318 +10850 +10286 +654 +6096 +16083 +6467 +17575 +4066 +16453 +9550 +6660 +7766 +19152 +18049 +10443 +41 +13514 +16334 +7436 +12473 +16583 +14053 +14297 +15656 +12490 +6886 +19208 +10432 +6377 +18863 +256 +10634 +2990 +7431 +19759 +6002 +2585 +9536 +3886 +6257 +16525 +15323 +1707 +16165 +15648 +5876 +1048 +4265 +15344 +307 +17066 +5471 +7759 +19561 +8494 +11599 +13033 +14924 +13999 +3749 +12344 +4163 +19128 +19181 +3887 +2925 +12744 +12087 +17291 +11311 +3662 +17814 +10228 +19105 +18335 +19362 +5667 +18221 +17856 +9600 +6035 +5779 +17796 +11086 +15926 +3731 +1908 +16463 +320 +3222 +1451 +14375 +14012 +939 +11594 +9718 +11737 +15853 +14296 +13639 +14856 +9824 +507 +4520 +15803 +16938 +15506 +16470 +11068 +17334 +585 +8791 +7489 +19713 +12283 +7707 +3277 +2922 +10703 +13287 +18629 +4452 +15291 +18096 +5059 +9210 +14525 +11264 +4363 +15985 +7696 +7087 +3120 +1823 +11117 +14340 +16978 +2399 +13609 +7387 +1223 +10276 +3259 +628 +10430 +13209 +1452 +14247 +5018 +7081 +1868 +14663 +11959 +3143 +1219 +9732 +14714 +13594 +1358 +17261 +12848 +9178 +3235 +7091 +4715 +16349 +3386 +4183 +2654 +15049 +11768 +6525 +1254 +19219 +17371 +2554 +19981 +14935 +19342 +3506 +1253 +6705 +5429 +12177 +8613 +16303 +15514 +1978 +4585 +8365 +10140 +11950 +10947 +10651 +16127 +104 +16307 +1472 +10463 +12915 +1869 +17835 +8275 +10313 +11693 +18844 +14498 +12745 +16452 +13059 +19867 +16929 +5899 +1169 +5028 +6875 +6076 +3645 +11112 +12965 +7086 +13105 +11584 +19634 +14686 +7704 +16408 +16825 +14870 +13901 +11735 +10240 +4206 +4372 +6818 +6966 +7073 +8078 +503 +10373 +19271 +7351 +1416 +9702 +14438 +4273 +16421 +10187 +17427 +17797 +19366 +2521 +19397 +12893 +18562 +14896 +7338 +17932 +14203 +5328 +14418 +1519 +1737 +13480 +16145 +9785 +8458 +14726 +6824 +3310 +5844 +16095 +6259 +7621 +407 +12332 +1768 +14428 +2949 +12868 +2459 +9959 +4235 +2453 +15633 +18381 +18265 +10798 +19553 +6570 +19683 +13912 +4956 +16701 +7492 +3388 +8057 +7060 +16726 +9445 +5902 +9935 +17300 +6714 +86 +9015 +6465 +12651 +13030 +6620 +7225 +10315 +7121 +9095 +13362 +14194 +16129 +5679 +13769 +5767 +13162 +14483 +5204 +1019 +3174 +1449 +2905 +6050 +16589 +17834 +13582 +6598 +12528 +6971 +13350 +6102 +16308 +17064 +12426 +10974 +8112 +1864 +5856 +10451 +17559 +12475 +10499 +8163 +13683 +3580 +7045 +18352 +7551 +3700 +10320 +12245 +10160 +9726 +14578 +2031 +5491 +10494 +15716 +15909 +9305 +4371 +1955 +2396 +14843 +607 +19224 +12076 +11346 +13781 +2080 +19571 +1340 +7270 +10090 +15693 +6521 +2337 +10535 +15053 +12504 +7303 +3973 +10345 +18965 +1756 +2566 +2982 +695 +11573 +12760 +19702 +17811 +18104 +11103 +8990 +3733 +13172 +3842 +7247 +16693 +19401 +432 +15256 +15841 +6010 +11327 +1286 +10485 +1971 +1866 +9683 +13271 +14751 +5999 +2766 +5272 +7797 +8620 +2299 +11316 +11133 +5782 +14115 +6175 +7272 +5107 +2945 +16915 +18776 +11021 +13335 +5957 +14566 +15760 +3989 +3800 +7457 +4890 +11228 +2531 +686 +6115 +15981 +14391 +13766 +17630 +17154 +9555 +13019 +10872 +11234 +5340 +1863 +1370 +14740 +4696 +5940 +16460 +13689 +16637 +8303 +12407 +5600 +9533 +19776 +18427 +17960 +4938 +10310 +4055 +12240 +11317 +548 +12440 +11530 +3898 +1574 +9605 +6099 +7442 +13387 +16315 +5396 +17035 +17877 +15302 +7410 +10299 +17225 +16548 +10487 +5377 +19773 +8735 +6194 +11480 +17802 +10625 +7793 +5671 +13685 +15966 +6278 +4336 +455 +15178 +11860 +16209 +7867 +8376 +3062 +12403 +12938 +8787 +17420 +9419 +4813 +5622 +13646 +10469 +2914 +8238 +5269 +13951 +6064 +6140 +18419 +11691 +14216 +14502 +15767 +16925 +10567 +6489 +14825 +2124 +11036 +19312 +6526 +1417 +19688 +15686 +1945 +8192 +250 +11190 +133 +14045 +1532 +8040 +6543 +9400 +3492 +19554 +2147 +19503 +13124 +3688 +4002 +14845 +8580 +18435 +14868 +5075 +15577 +354 +19876 +19144 +11075 +10962 +6633 +16722 +14885 +14299 +11838 +13305 +12772 +3932 +14951 +17939 +3031 +7323 +6950 +852 +13363 +14899 +2280 +16826 +3702 +18951 +1091 +6722 +11533 +13493 +10468 +11598 +12737 +6310 +577 +14609 +5214 +1762 +17122 +11048 +13327 +8569 +18535 +19894 +6281 +9934 +2634 +14894 +10866 +3475 +4038 +9783 +1273 +17957 +14467 +14236 +13904 +14214 +14541 +2074 +12175 +12103 +15535 +4486 +4398 +2964 +1949 +10382 +4874 +7688 +15407 +13482 +17181 +11261 +13127 +19277 +2023 +6077 +7275 +9377 +8527 +15550 +1333 +5219 +9526 +12349 +8185 +16890 +1711 +15551 +15787 +3093 +14372 +14809 +12968 +10799 +2962 +17273 +13548 +8261 +10261 +11642 +622 +1904 +2162 +17288 +15265 +17126 +12512 +5824 +11646 +11960 +17214 +19724 +9793 +14379 +2120 +16520 +16141 +8193 +12282 +15688 +10020 +4837 +7787 +10477 +15160 +10748 +705 +400 +460 +16391 +15749 +19480 +12273 +17088 +14699 +18840 +14936 +14023 +7279 +10559 +5689 +2224 +1153 +7903 +3552 +16959 +17667 +17120 +18425 +11482 +18264 +15253 +1657 +7214 +14136 +11004 +8188 +16316 +9540 +3864 +18474 +162 +18604 +902 +12929 +4256 +8560 +1027 +8281 +11085 +7595 +5993 +7698 +15978 +19090 +3045 +4970 +16614 +9643 +9131 +12238 +19655 +10226 +15959 +8282 +11286 +2984 +11394 +2133 +19586 +761 +4551 +18276 +13194 +7337 +14770 +19065 +15191 +17765 +18905 +11882 +12131 +10882 +18656 +19550 +596 +9725 +13297 +3693 +2428 +16485 +9569 +4201 +5363 +725 +5273 +17098 +8931 +362 +10770 +10340 +1940 +3013 +6516 +10542 +3308 +16371 +18478 +9044 +2307 +284 +1888 +2325 +18719 +9845 +423 +5781 +3273 +1467 +11749 +9641 +626 +7718 +13674 +10922 +15280 +6896 +7379 +10062 +9297 +17937 +11474 +9111 +4461 +19049 +8298 +14370 +1947 +5145 +18411 +19017 +12477 +11189 +16061 +1060 +15899 +5859 +19452 +16459 +11762 +742 +10520 +11469 +19933 +15974 +7395 +8820 +6119 +14287 +19360 +3181 +12085 +3968 +941 +11968 +11166 +1097 +18215 +14204 +16455 +2732 +13499 +6439 +1376 +18674 +14211 +13531 +2935 +4308 +791 +10316 +2696 +19330 +2037 +5054 +1723 +17714 +5760 +11347 +7117 +17910 +19498 +2365 +16898 +7394 +8668 +16335 +14940 +2094 +6481 +14309 +7661 +3493 +2715 +18742 +13598 +12327 +13445 +12533 +3927 +5610 +15112 +3957 +249 +4463 +16670 +776 +9071 +1894 +4716 +16708 +11464 +10753 +1725 +11680 +2220 +16285 +14743 +10295 +12075 +17333 +15529 +16010 +1641 +18549 +10728 +8515 +17018 +9765 +18745 +1754 +2877 +13622 +3889 +1753 +5032 +6425 +3998 +13268 +18086 +6124 +9672 +10994 +12976 +1493 +8039 +6802 +312 +11252 +18244 +19843 +18273 +17254 +7764 +2940 +16572 +8587 +11556 +18522 +3419 +11039 +14084 +3649 +15987 +7172 +14810 +7016 +17060 +13877 +1871 +11376 +5176 +4584 +12354 +10178 +18832 +18569 +14443 +219 +2150 +18328 +2311 +2370 +5488 +17743 +1970 +7738 +15562 +3845 +1146 +1100 +916 +1308 +1537 +19514 +9833 +3848 +16558 +8033 +1404 +6013 +6291 +1378 +19580 +7096 +17146 +3862 +17870 +846 +12736 +2855 +10034 +15604 +9230 +15270 +101 +440 +723 +2615 +13576 +3211 +19581 +17771 +11631 +3647 +1835 +8641 +4827 +8098 +13977 +10411 +17632 +16948 +2608 +373 +2233 +8980 +18729 +9277 +14417 +5691 +14108 +15746 +11056 +16425 +6320 +13618 +15906 +11572 +4595 +8773 +13120 +964 +15387 +15092 +9421 +7151 +17886 +4730 +16956 +16296 +3434 +10851 +9667 +4573 +10449 +6927 +16545 +12056 +11187 +892 +6889 +3017 +14949 +10702 +563 +17595 +17625 +12779 +713 +5529 +15189 +9512 +10591 +7277 +19636 +5160 +17784 +13013 +2258 +9892 +15846 +1049 +16321 +18912 +18534 +2541 +14494 +7645 +739 +593 +7652 +14119 +706 +4843 +248 +4645 +14126 +16547 +11262 +14393 +3967 +10667 +6900 +10923 +7138 +1350 +4868 +313 +19297 +18600 +11524 +4810 +5183 +11776 +2438 +2011 +15691 +6454 +1059 +13072 +1092 +6315 +16500 +17965 +6113 +12995 +17171 +19764 +14942 +2058 +16433 +2709 +159 +7077 +9580 +16157 +7733 +1964 +9762 +18158 +16611 +19454 +17005 +16738 +4569 +17220 +17232 +2063 +10749 +7924 +3763 +7563 +5801 +11663 +2951 +2760 +9996 +16059 +11545 +14519 +14320 +14530 +16587 +9330 +13952 +7913 +9424 +3939 +12379 +2805 +19612 +14863 +6327 +4216 +8927 +3457 +4161 +5192 +6276 +11930 +13811 +4947 +1551 +11051 +12770 +2603 +1568 +10025 +9815 +12170 +10886 +14733 +6125 +16147 +7647 +2089 +127 +5617 +4269 +9869 +211 +8821 +7182 +773 +3282 +2024 +18935 +18489 +9693 +12551 +2510 +4368 +15201 +11091 +13418 +8794 +16934 +8565 +7115 +14739 +12751 +9292 +11110 +797 +8362 +18954 +9219 +1305 +3852 +13234 +7947 +15610 +17274 +8364 +17507 +3799 +19316 +629 +5821 +5380 +9772 +8680 +2069 +9957 +19628 +14470 +1490 +6322 +9217 +10894 +15912 +19072 +7546 +18711 +9351 +17344 +16406 +6415 +3959 +11623 +9160 +16294 +7222 +10098 +18890 +19443 +734 +14258 +15864 +18055 +18246 +6952 +6609 +8740 +16001 +9927 +6227 +11079 +8645 +2831 +11816 +14363 +13110 +7886 +4339 +13742 +10878 +3590 +4367 +18580 +18164 +15478 +8747 +8293 +3047 +17462 +12161 +15668 +11724 +10645 +1356 +17465 +11186 +3727 +17337 +659 +2360 +17991 +19640 +18114 +4882 +8533 +10305 +10492 +1688 +3665 +10652 +19392 +11198 +10619 +8539 +6588 +202 +17699 +10700 +12293 +18008 +1095 +1288 +7094 +6729 +5572 +16779 +10148 +10890 +15373 +9518 +17190 +18618 +4302 +15469 +11796 +5261 +122 +17466 +19447 +3274 +9795 +11991 +9519 +5778 +9665 +1635 +18131 +15427 +17103 +8905 +18095 +11588 +261 +7822 +62 +11419 +10227 +10517 +7116 +16457 +16965 +18396 +8595 +15547 +3528 +7210 +9254 +11244 +11212 +19311 +4615 +8425 +18269 +18696 +13793 +5586 +13113 +5564 +17045 +2481 +6008 +2795 +4734 +18793 +17000 +1816 +10467 +9492 +15747 +3426 +9901 +17713 +18100 +17977 +18676 +4190 +6447 +7129 +19236 +3460 +7909 +4431 +19501 +18626 +5369 +12849 +6301 +10500 +4374 +4567 +17293 +6733 +2963 +17132 +605 +8854 +12211 +4789 +13052 +10774 +2017 +8015 +2614 +5858 +235 +12576 +14343 +4922 +10671 +11567 +15774 +14374 +4541 +19063 +19127 +7798 +8865 +12836 +2487 +7329 +1374 +17585 +15231 +7887 +178 +9238 +4853 +11334 +5582 +3938 +2466 +4554 +16792 +13012 +6969 +8270 +19735 +3772 +73 +5399 +12822 +2969 +2449 +11242 +16487 +10213 +2400 +16873 +16410 +858 +18653 +5307 +17497 +185 +13165 +6163 +4049 +13697 +3059 +4869 +1807 +17432 +16156 +10456 +17152 +12289 +2602 +8389 +19662 +15718 +5722 +11290 +10880 +9857 +887 +18643 +8479 +19928 +8683 +12984 +5602 +8661 +10905 +10283 +6754 +169 +14716 +9011 +8027 +14197 +6600 +14988 +12833 +14043 +14173 +4865 +15399 +16534 +18426 +18932 +7367 +8195 +19627 +3233 +11855 +10906 +1306 +3002 +12252 +15698 +3686 +8691 +7433 +6355 +515 +15365 +15792 +6835 +12959 +2492 +12167 +13079 +15476 +4448 +5609 +14565 +4766 +19833 +10251 +7996 +12524 +1900 +11099 +19864 +10577 +17485 +13293 +5513 +7649 +1258 +9091 +16369 +19542 +16597 +18165 +6074 +1919 +15933 +5830 +9029 +6037 +16183 +11732 +19318 +15450 +15849 +13164 +19983 +16278 +15728 +3899 +3546 +13134 +9193 +823 +5110 +2705 +17755 +17570 +17742 +324 +8350 +18757 +1701 +16686 +280 +5384 +18751 +14360 +4879 +12368 +1406 +15391 +16833 +18391 +11402 +5553 +12571 +19140 +6217 +12347 +17947 +19528 +17166 +2974 +18291 +6809 +6669 +17279 +19214 +3588 +16084 +9191 +495 +11090 +14771 +9669 +5983 +12523 +936 +10085 +4671 +16565 +18606 +9997 +3237 +4035 +14838 +14312 +19151 +10384 +3448 +2451 +17883 +15967 +11651 +17093 +10507 +19902 +10324 +17904 +19882 +19900 +14545 +9190 +3769 +12907 +1746 +3257 +11883 +6231 +11105 +8805 +8336 +5755 +645 +19052 +14412 +17748 +6387 +12985 +1393 +6764 +3877 +9361 +11440 +12346 +675 +12017 +4707 +13942 +10989 +10650 +12102 +11636 +14206 +19305 +2727 +15731 +9468 +6149 +14234 +12074 +16152 +7915 +5151 +12725 +8674 +4204 +19991 +13550 +14573 +17671 +17651 +27 +2640 +3554 +3361 +19783 +66 +2573 +12249 +14556 +5008 +61 +4264 +8263 +18409 +80 +6667 +3530 +9801 +7316 +7517 +17828 +52 +952 +5209 +5982 +3830 +7134 +15370 +11547 +13602 +6708 +16806 +19590 +5301 +5633 +8908 +9950 +841 +16730 +2109 +6060 +6915 +8353 +13086 +9952 +9301 +9449 +12165 +13024 +946 +19044 +30 +12837 +4968 +7857 +15117 +9839 +12898 +13051 +1341 +4535 +4982 +8334 +14171 +4635 +8435 +14079 +17272 +5774 +11843 +7989 +17933 +5953 +3467 +7520 +16231 +11182 +15613 +2789 +15163 +12458 +19614 +17393 +6277 +10314 +2354 +14161 +2824 +11909 +4770 +15581 +18891 +4826 +13497 +12040 +3540 +5560 +4277 +16318 +6738 +19472 +19344 +1475 +1705 +7973 +12852 +9735 +9016 +9592 +4391 +19209 +961 +7515 +14681 +7568 +2864 +17477 +8571 +9121 +3401 +14306 +8461 +14476 +10724 +4705 +15333 +4039 +1465 +2477 +2183 +17209 +18621 +17140 +8833 +18347 +19269 +17183 +19213 +13603 +18152 +6595 +15114 +11868 +17711 +2533 +591 +16742 +4052 +16053 +700 +8729 +3256 +19633 +18336 +8724 +6702 +1671 +19818 +382 +16 +16447 +5683 +5248 +4749 +6758 +11708 +752 +12797 +1494 +4703 +5157 +183 +19955 +12015 +9076 +15188 +15625 +19415 +7757 +11507 +15700 +19069 +18963 +14164 +11888 +19283 +17157 +394 +14840 +14513 +464 +11163 +15624 +4739 +18529 +12859 +2955 +3135 +3061 +4931 +6718 +10241 +17568 +17791 +19036 +4180 +11739 +15131 +14618 +17382 +242 +13273 +19837 +12625 +10363 +1344 +18788 +19028 +9907 +9261 +6656 +5113 +8095 +391 +3337 +16601 +1735 +6685 +10663 +913 +1721 +18056 +6468 +17737 +3098 +7917 +15449 +14897 +2475 +10296 +7658 +9244 +7393 +5544 +9486 +18857 +1685 +19229 +2170 +14208 +10582 +3690 +1221 +10675 +2753 +9972 +8881 +6155 +19762 +4474 +8736 +16992 +19671 +19979 +19110 +17617 +18592 +6479 +14134 +8375 +18015 +14678 +1071 +4820 +14020 +11147 +878 +10569 +6830 +6891 +439 +3795 +9936 +13331 +17982 +381 +3507 +6031 +12832 +15267 +6461 +6522 +2719 +8568 +3285 +4905 +15241 +1173 +11857 +10741 +15871 +5812 +4217 +15939 +11997 +273 +9654 +15536 +16036 +15105 +15708 +4665 +13862 +2954 +6634 +13320 +9861 +8301 +14575 +6778 +9339 +11272 +4880 +12945 +18349 +12218 +18024 +3129 +17170 +18168 +17759 +1360 +9435 +15989 +3431 +6851 +14891 +14738 +7321 +9336 +4558 +9461 +3548 +10630 +16000 +10483 +9918 +19210 +19569 +19896 +11083 +18936 +7653 +14225 +1514 +19748 +9034 +15258 +1516 +3770 +12785 +3781 +11315 +12424 +6892 +10414 +12314 +3157 +5096 +9166 +15303 +2189 +8838 +18492 +2680 +6426 +10697 +12333 +1503 +10769 +4426 +1744 +1667 +9736 +13034 +3518 +14252 +39 +19635 +7834 +19081 +8712 +6510 +2262 +18516 +16549 +18310 +1487 +6143 +3010 +8938 +8199 +19665 +17116 +12405 +9583 +9768 +14913 +7445 +7284 +1648 +17852 +16507 +19845 +8220 +7633 +11504 +16816 +12246 +9138 +19042 +2111 +2103 +8101 +4376 +19343 +17733 +9891 +11721 +14944 +13011 +7233 +13709 +7497 +8891 +18775 +10374 +9212 +9214 +18577 +8814 +11918 +3865 +3897 +8919 +18946 +18640 +12360 +17318 +8739 +383 +16762 +3873 +18292 +7537 +8677 +15824 +18232 +3194 +5787 +12028 +15332 +290 +12153 +1439 +5179 +16210 +19941 +13131 +11859 +11461 +17375 +14773 +146 +17014 +15061 +33 +9267 +16428 +9417 +12916 +9472 +1670 +19143 +16099 +5207 +8894 +11279 +16501 +361 +17180 +2569 +18449 +19718 +500 +19966 +3750 +4579 +16723 +8076 +4322 +18058 +17470 +4795 +13477 +5350 +6955 +5634 +14198 +3537 +13970 +17670 +6253 +15321 +10971 +328 +13640 +18502 +17659 +17541 +8991 +5567 +2298 +11490 +12821 +12527 +1208 +4152 +8764 +19745 +10821 +12727 +5492 +7025 +4223 +6156 +2073 +2799 +12353 +15820 +11932 +9629 +28 +8407 +9738 +11246 +13471 +12611 +11138 +6945 +6367 +7170 +7942 +13479 +4743 +8982 +8853 +6547 +12207 +19828 +3952 +8217 +538 +16993 +10727 +19123 +19118 +18574 +19974 +16464 +13631 +16828 +206 +13190 +11750 +658 +7879 +10083 +15459 +17922 +1443 +17613 +18045 +3329 +2714 +2587 +11538 +153 +10941 +11061 +2827 +2841 +15304 +13414 +3825 +4737 +1070 +4366 +15585 +7775 +2006 +19929 +1458 +7208 +15862 +9717 +7930 +10688 +18001 +9434 +4298 +1216 +7871 +15060 +15934 +9575 +12632 +549 +3870 +16910 +5007 +7226 +7174 +9203 +139 +4623 +12201 +15495 +18009 +3487 +8800 +13607 +4725 +1879 +8289 +10371 +19138 +16200 +3373 +6998 +16566 +15488 +837 +14529 +18170 +4815 +8308 +4611 +9564 +11694 +9410 +15106 +4664 +10041 +1510 +10835 +11841 +19533 +18327 +13441 +10486 +91 +4405 +9995 +1343 +17137 +13673 +19595 +10005 +14806 +18703 +14991 +17962 +3464 +11582 +13716 +14196 +7741 +8581 +1460 +18501 +5019 +10733 +1780 +4934 +9157 +19518 +7796 +15493 +13257 +10092 +1524 +9415 +7140 +18177 +8151 +4263 +7494 +3088 +10876 +19832 +16850 +6829 +2498 +18806 +4042 +15615 +19469 +4858 +18851 +13180 +11382 +13199 +533 +1464 +17678 +8929 +12911 +8209 +13900 +8388 +950 +11406 +11319 +1895 +9430 +199 +7061 +11799 +9794 +19258 +11861 +4923 +13227 +12434 +18900 +13917 +7992 +9379 +18004 +9398 +15494 +13617 +13277 +8486 +2229 +6800 +17394 +7196 +132 +17662 +8066 +11308 +18061 +7760 +2653 +19492 +4476 +11177 +17404 +1649 +12086 +5101 +16246 +15982 +16860 +2301 +17193 +14664 +15780 +3309 +85 +14415 +15586 +10800 +8965 +9384 +2047 +3118 +13551 +13157 +14022 +6192 +7371 +12258 +5589 +2697 +13140 +1495 +10028 +17589 +5970 +19410 +14039 +10096 +19607 +7758 +4702 +14821 +7755 +15005 +11592 +16602 +5062 +6178 +15634 +8306 +5969 +9557 +6840 +11698 +2913 +6519 +5613 +16986 +1618 +11728 +16964 +9827 +19768 +16351 +16081 +2041 +20000 +19840 +425 +16736 +5961 +9537 +7053 +7328 +18140 +17990 +5379 +2408 +16744 +10723 +13309 +18235 +532 +19810 +10423 +149 +5716 +10557 +8693 +6640 +16555 +4974 +16798 +11157 +7332 +17008 +9126 +4525 +6186 +15576 +8913 +8659 +11829 +4365 +1107 +1802 +9291 +197 +5368 +12778 +9264 +15612 +11287 +18333 +3699 +19486 +6968 +11494 +2145 +13340 +5803 +13996 +14826 +17842 +2556 +13613 +3740 +12562 +2313 +14665 +13324 +19985 +12914 +19182 +8045 +10325 +18876 +18386 +5056 +18655 +19922 +4245 +5120 +15460 +9364 +12080 +17101 +8511 +14332 +4612 +4782 +8532 +15298 +10424 +757 +8839 +5752 +16955 +2341 +3642 +5541 +5034 +4059 +12266 +18647 +8816 +781 +13267 +12622 +10514 +18828 +3037 +14938 +1907 +187 +17248 +16877 +5664 +18680 +16663 +15362 +10546 +8207 +771 +13824 +7488 +17846 +5690 +19954 +18339 +2900 +2687 +6356 +3583 +11656 +6225 +13465 +7291 +9296 +18139 +14052 +18149 +17484 +11145 +2560 +11009 +10813 +10828 +4193 +1624 +6162 +8175 +17588 +11955 +18029 +10115 +1322 +14889 +4515 +4691 +17524 +17787 +6618 +1481 +7508 +16484 +12415 +9174 +18111 +14062 +2897 +7417 +11700 +7743 +16106 +6174 +2279 +7390 +13408 +177 +4280 +14347 +12790 +9561 +16586 +14129 +16766 +9003 +14523 +10123 +12005 +15650 +310 +9924 +19436 +10417 +15540 +75 +8871 +10116 +15314 +15818 +14055 +16217 +7665 +728 +13464 +7945 +2752 +18395 +16814 +11817 +6977 +18240 +11923 +980 +15882 +17301 +19982 +15905 +14945 +13099 +7263 +14413 +6051 +7803 +4142 +2902 +2056 +2865 +799 +11741 +15907 +587 +14784 +10686 +336 +1854 +225 +142 +3592 +13961 +10503 +10488 +19160 +12375 +911 +2009 +2042 +17563 +8723 +17424 +4129 +11951 +5546 +13500 +11456 +14142 +13666 +9418 +18587 +15896 +9082 +5196 +18320 +19615 +17551 +9064 +10565 +4500 +14585 +16436 +12483 +1840 +17899 +9362 +9265 +5914 +11898 +11167 +9854 +4034 +5603 +8410 +3760 +19308 +17423 +16598 +15754 +8761 +6956 +3490 +3789 +12964 +13390 +10810 +12432 +3838 +3940 +12895 +15567 +15108 +7453 +8940 +5738 +11901 +6198 +3397 +14695 +16878 +5909 +13232 +17926 +3679 +3238 +7424 +15736 +5806 +19995 +7589 +88 +9062 +8669 +19248 +11074 +12419 +3215 +4120 +3170 +16863 +1817 +522 +4447 +13208 +13705 +6716 +6302 +10037 +6181 +17873 +6654 +16859 +6287 +19237 +16149 +1923 +891 +12891 +18735 +9451 +15936 +9271 +2738 +4239 +18558 +18238 +15856 +19092 +14689 +17996 +6589 +9790 +3509 +1147 +11270 +15614 +5257 +1000 +16135 +6852 +1573 +584 +126 +17694 +16945 +12143 +18584 +17415 +2443 +7664 +1929 +14887 +3007 +1571 +5508 +18113 +18537 +19310 +14948 +6219 +19055 +9327 +19233 +14977 +19158 +17198 +18438 +1297 +19463 +9834 +2185 +289 +2174 +3279 +18722 +3739 +5981 +18889 +8717 +6870 +12827 +16306 +6255 +15222 +3175 +10656 +8059 +5289 +6052 +10126 +3560 +19019 +19074 +12548 +5330 +15107 +18956 +5288 +4637 +6821 +11801 +5088 +5452 +2576 +2834 +4381 +13668 +11780 +11804 +5424 +14355 +6981 +2104 +16546 +8161 +10689 +18147 +15017 +12454 +18385 +2188 +343 +4839 +18811 +14270 +2967 +8460 +4386 +9252 +16823 +11793 +10022 +10653 +19374 +8698 +457 +894 +7594 +18736 +19646 +10901 +18689 +11811 +8414 +7155 +12829 +1658 +14408 +15804 +6374 +9964 +12155 +12140 +10759 +4932 +11621 +6410 +5931 +18136 +14831 +7458 +17869 +11119 +3141 +5364 +19699 +8658 +13235 +17915 +5036 +16390 +15583 +17150 +11454 +9910 +7687 +5442 +18189 +3206 +11422 +1501 +11769 +12845 +13812 +8449 +9623 +5164 +2168 +16268 +6984 +4464 +3909 +5980 +18325 +16419 +4709 +9107 +19421 +8760 +5287 +17942 +4958 +12387 +19304 +17419 +3734 +6524 +4895 +7821 +1716 +1546 +14829 +16990 +11678 +8834 +5479 +4444 +9386 +18126 +5303 +17539 +10729 +6092 +8165 +15001 +13300 +8236 +9150 +8047 +10551 +23 +3545 +1274 +11937 +8542 +7556 +10419 +10356 +17900 +17353 +9499 +3443 +1009 +19184 +2166 +3286 +4697 +17062 +8110 +18209 +10401 +4301 +1332 +10394 +6138 +12113 +15988 +518 +8851 +5053 +17620 +8265 +14409 +3035 +6495 +9881 +10823 +11887 +16272 +12427 +7411 +16009 +11610 +1774 +19133 +16576 +7034 +12846 +491 +3263 +10455 +12707 +6398 +18204 +11277 +18709 +7026 +1548 +1872 +4779 +10606 +10162 +14620 +9721 +11912 +2666 +9061 +15600 +13962 +3960 +17738 +10266 +18363 +296 +15379 +14978 +5068 +19585 +5063 +15742 +7960 +9703 +18496 +16483 +6962 +16024 +5847 +18880 +17082 +16627 +1158 +8713 +6611 +10861 +8002 +14392 +13153 +2005 +9893 +9373 +2981 +2404 +14315 +10758 +12193 +19139 +1109 +7555 +12242 +10900 +19676 +5433 +17606 +12966 +13381 +11096 +11559 +7201 +11098 +6936 +9796 +1639 +3491 +4075 +1847 +13108 +12556 +1138 +19437 +5810 +10578 +8128 +11076 +356 +14980 +16203 +17092 +6511 +11810 +2248 +7762 +2859 +19566 +15143 +16479 +7911 +334 +15087 +15355 +7859 +19225 +4031 +10309 +1850 +14217 +15213 +12197 +15837 +6239 +9542 +10685 +8347 +18355 +5353 +13973 +19705 +13486 +12640 +18286 +7935 +15308 +19663 +3828 +9048 +2102 +4413 +15773 +18959 +5736 +11906 +10121 +7106 +12519 +14813 +8283 +17995 +18245 +17528 +9664 +17514 +12975 +19180 +10776 +5813 +8248 +10809 +5714 +18275 +17511 +3266 +6741 +18541 +15913 +11962 +17903 +16954 +11969 +10747 +10817 +4768 +12361 +4644 +1078 +10225 +15292 +7541 +16364 +8121 +10849 +10508 +12672 +13805 +12359 +3415 +105 +12544 +2669 +6109 +8445 +10024 +1011 +13058 +12643 +25 +10913 +18623 +4593 +3931 +12 +2221 +3544 +15357 +193 +8483 +7874 +4218 +19784 +17358 +18553 +9388 +13395 +10285 +7980 +15753 +13612 +14110 +8226 +12962 +9413 +2444 +3839 +8799 +8120 +14653 +1806 +6353 +16080 +4093 +7975 +16968 +2983 +11979 +11746 +11862 +5064 +8423 +698 +10196 +18712 +5556 +17463 +16295 +14169 +6756 +4706 +6466 +12484 +6914 +5041 +444 +9153 +10742 +9094 +6229 +14835 +1788 +8358 +17297 +8194 +15854 +19056 +5225 +12367 +3178 +7933 +18853 +13392 +12610 +5908 +11284 +15829 +358 +8681 +243 +9363 +17255 +15461 +15770 +8428 +9848 +9961 +2207 +18578 +18187 +4603 +3604 +1631 +11597 +18360 +15034 +17912 +4234 +4361 +4811 +2907 +2663 +4064 +5274 +10457 +7662 +13062 +2525 +3350 +10254 +8331 +18062 +10376 +5816 +14534 +3204 +4854 +15587 +13195 +3990 +14365 +4017 +18262 +5611 +11794 +13077 +9691 +12285 +13835 +10349 +19198 +11448 +15544 +1689 +13986 +16431 +1164 +366 +19223 +9911 +14965 +3609 +9947 +1315 +19685 +18734 +11761 +6602 +511 +18486 +17360 +19869 +18089 +14054 +19378 +19515 +664 +13338 +12624 +19588 +13516 +15006 +19774 +717 +19102 +18654 +17433 +4752 +3812 +10509 +14495 +679 +5734 +13129 +12281 +1133 +3697 +7499 +18278 +15640 +10425 +15827 +8545 +18831 +13749 +17888 +10938 +6204 +18059 +5646 +11803 +13074 +10833 +14143 +11005 +9293 +12834 +1786 +4866 +19249 +9836 +16392 +9181 +13855 +13176 +9478 +701 +9132 +1542 +8862 +12341 +17309 +18661 +16443 +4640 +14857 +8290 +17283 +5496 +14242 +8793 +4576 +4679 +11889 +1438 +4925 +10355 +3819 +6376 +2532 +13526 +14322 +10347 +18854 +5956 +19423 +15665 +8915 +4663 +10692 +19644 +15832 +18590 +8621 +13148 +1924 +17794 +9825 +15755 +10445 +19190 +17758 +14146 +6411 +2590 +8168 +15901 +3581 +6440 +15382 +14036 +13349 +6535 +6592 +13203 +3161 +4546 +8444 +17303 +18984 +15002 +8550 +19625 +19884 +233 +7041 +19499 +18047 +4468 +17987 +13641 +10589 +17550 +7576 +7093 +19113 +1199 +1380 +9182 +12319 +19033 +1001 +17730 +13731 +7802 +19430 +6872 +8684 +6579 +8562 +6561 +11864 +6199 +15051 +15499 +11065 +18566 +5996 +13174 +4025 +7480 +15136 +17700 +3085 +5949 +7681 +9905 +11608 +13104 +17189 +7514 +3618 +3353 +17233 +8916 +15953 +18436 +13280 +5084 +7360 +5594 +6404 +6187 +10143 +3949 +14028 +4990 +2245 +13736 +17844 +11748 +15719 +17244 +876 +3124 +15721 +14763 +5663 +11000 +18467 +6098 +17920 +2939 +6864 +7719 +2765 +5726 +16995 +13325 +11705 +18849 +8292 +3368 +13861 +16438 +6196 +17304 +18376 +4872 +1275 +2509 +2557 +2340 +7021 +2650 +1794 +12043 +14059 +9408 +18205 +18720 +13010 +17627 +11010 +17859 +17921 +10917 +12656 +5336 +8553 +6975 +4383 +4116 +14693 +11771 +6093 +26 +9224 +14712 +17335 +17173 +9586 +17776 +1680 +10825 +4244 +15878 +8628 +17165 +3066 +818 +7383 +15126 +18393 +10612 +15174 +5763 +12617 +14167 +5003 +3096 +13585 +1448 +9831 +3218 +16882 +15123 +18705 +2600 +15032 +7241 +11132 +3297 +13040 +18608 +9974 +14192 +13958 +7070 +8843 +7418 +4296 +4012 +5566 +16074 +5409 +5514 +14423 +10210 +9240 +10558 +8792 +4174 +13021 +2508 +8744 +3624 +9826 +2099 +2450 +4997 +8300 +9202 +7290 +8354 +9455 +11114 +7824 +18422 +16705 +14690 +5329 +6739 +15794 +1497 +2426 +5643 +15341 +4695 +17307 +18641 +16288 +6546 +3326 +1076 +6025 +19048 +5454 +11089 +10899 +16578 +1260 +17090 +2870 +11028 +12158 +13954 +17078 +18718 +16446 +9290 +4775 +1233 +19424 +15685 +18182 +11836 +5741 +18378 +11354 +1724 +11609 +10093 +5968 +19296 +16590 +3108 +10896 +12243 +11 +14765 +11872 +12395 +11295 +17536 +18464 +5233 +995 +13449 +11846 +427 +4382 +11744 +12932 +18627 +1129 +7773 +1205 +15155 +12680 +10125 +16326 +6385 +13849 +11225 +3097 +16889 +399 +10042 +15394 +7503 +3511 +3026 +16831 +2012 +7969 +15158 +13916 +15538 +19165 +15629 +3383 +1615 +4608 +8345 +2181 +15602 +2985 +1206 +13910 +17242 +9573 +10789 +13366 +9324 +627 +10717 +18922 +2137 +6038 +8617 +14878 +1819 +12227 +16619 +7412 +15689 +13830 +14674 +15857 +18942 +8806 +3933 +5087 +17243 +17602 +4797 +9471 +12786 +6785 +15817 +12382 +5942 +13423 +657 +766 +12638 +15369 +2293 +4261 +4313 +19519 +13182 +13050 +294 +11471 +9368 +3217 +8225 +3714 +416 +12791 +19712 +11496 +10593 +1969 +1261 +9453 +5450 +2264 +1520 +17027 +14102 +8043 +16588 +17379 +16002 +7426 +4960 +1687 +12676 +17614 +7422 +2558 +10843 +16071 +16109 +11505 +9696 +2708 +15539 +15414 +560 +12428 +3582 +9599 +12300 +404 +5525 +7065 +19368 +15657 +14440 +15111 +13909 +3300 +10929 +13225 +17587 +4320 +2199 +5742 +11038 +4081 +7977 +12058 +3130 +1240 +13057 +11639 +5235 +8789 +2850 +5839 +19562 +8872 +17666 +18377 +10257 +17356 +18091 +7705 +215 +9917 +7102 +18268 +16976 +9009 +16032 +7962 +7666 +15885 +7128 +14114 +17499 +12351 +7184 +1280 +9320 +12328 +19482 +11432 +8508 +6719 +16981 +1659 +14253 +13553 +17390 +8635 +10945 +13402 +1201 +15144 +18331 +1181 +5335 +10864 +16844 +11055 +4745 +6403 +13747 +1579 +6133 +8077 +7402 +17330 +10048 +102 +838 +6401 +12303 +14218 +9804 +17107 +13621 +10206 +881 +11995 +17268 +15072 +12536 +9458 +18251 +8868 +13994 +13206 +15980 +17688 +13754 +1299 +17566 +15345 +6375 +3924 +17069 +5004 +14362 +9065 +18043 +714 +18225 +15089 +3084 +287 +11080 +7256 +7701 +18466 +15354 +11668 +3553 +14395 +19915 +18786 +825 +19835 +13575 +1831 +247 +2016 +4286 +8007 +12230 +1643 +3260 +1430 +19238 +16397 +11291 +17482 +18813 +12673 +11067 +1375 +13775 +16079 +7496 +6538 +8212 +10677 +12710 +15102 +1845 +10450 +11695 +3706 +8988 +16641 +3005 +5552 +11908 +6518 +18416 +18560 +14484 +10812 +311 +6700 +2489 +4814 +6725 +8462 +8875 +8512 +6328 +7243 +15055 +18698 +12542 +10775 +5152 +5131 +5549 +16658 +17703 +10351 +15186 +9407 +13696 +14357 +4714 +18871 +6300 +5477 +222 +5256 +14735 +9200 +16765 +4856 +13549 +1656 +18619 +13966 +2214 +5952 +796 +8537 +3193 +5094 +16830 +11210 +11692 +11994 +8379 +17603 +2092 +13879 +18172 +15026 +4179 +16301 +19341 +9136 +11273 +5930 +7936 +5398 +14274 +18913 +13555 +14098 +1330 +2296 +12950 +16616 +11661 +2529 +6732 +14431 +12913 +13222 +3720 +11359 +18399 +9565 +18706 +17075 +12089 +17130 +2672 +5208 +15159 +9660 +3923 +2693 +16745 +2934 +9287 +13990 +4122 +17963 +6070 +2628 +19739 +6648 +8719 +17515 +11774 +15489 +14652 +5338 +4892 +3433 +4484 +11632 +9010 +3033 +8230 +3200 +3977 +17847 +8202 +18675 +1119 +10056 +1413 +7573 +14341 +1036 +17108 +12098 +4137 +3651 +13379 +7904 +10641 +14976 +2180 +10944 +19356 +473 +16989 +8832 +15224 +6890 +2891 +2126 +4276 +11488 +12117 +6388 +4027 +8068 +409 +17687 +18778 +4822 +414 +18290 +3038 +6865 +10262 +14837 +10141 +9792 +13545 +18885 +4761 +4475 +3529 +19217 +19693 +3348 +7212 +18332 +7372 +12269 +3032 +11697 +16051 +13558 +2718 +19082 +6046 +11137 +6485 +9052 +14647 +13333 +5828 +4397 +7386 +8605 +5651 +6024 +5189 +16345 +15434 +14725 +4514 +11023 +13382 +7578 +13239 +4532 +15295 +11892 +9545 +2152 +14703 +18904 +8531 +15223 +18084 +4524 +74 +367 +3302 +15187 +17948 +5692 +6128 +6172 +862 +1800 +4684 +9414 +11033 +13573 +6000 +9615 +17476 +9059 +4805 +17054 +8582 +11549 +5650 +12626 +14308 +2238 +11714 +566 +5316 +2751 +4733 +970 +18769 +12843 +4658 +6209 +8830 +18171 +19497 +14780 +3730 +2661 +12284 +7012 +12277 +10008 +11933 +3142 +18428 +3667 +161 +12152 +12630 +18370 +7939 +2046 +3771 +16803 +13993 +2129 +14608 +18839 +12060 +16404 +11870 +18923 +10698 +6659 +2352 +1561 +4226 +14741 +17692 +9649 +5073 +15142 +19353 +6009 +3240 +5344 +10170 +4550 +17907 +11679 +7651 +6796 +397 +7148 +12621 +5558 +3054 +14049 +2090 +13856 +12317 +10660 +2514 +14338 +10482 +19202 +11648 +16403 +1335 +19574 +16665 +11989 +4482 +16313 +4481 +17290 +3742 +4407 +1528 +12901 +7516 +15834 +10231 +4945 +11337 +12609 +5516 +17039 +18026 +11509 +15831 +6085 +976 +18368 +2249 +17712 +16657 +2085 +1054 +1880 +6681 +8094 +9636 +18833 +14291 +7659 +11754 +19714 +17347 +1565 +18074 +7791 +6470 +2289 +10027 +1934 +12826 +19295 +17179 +6112 +15008 +17860 +9711 +12338 +18888 +9216 +17573 +15741 +6557 +11821 +3785 +13764 +14853 +5129 +15446 +1243 +2836 +11920 +4115 +19743 +1410 +16817 +18379 +3104 +5661 +13223 +4483 +2670 +945 +17124 +4863 +13476 +18169 +15448 +18006 +19349 +8378 +1572 +12920 +7204 +4230 +19243 +9426 +12260 +16377 +10194 +2825 +4908 +11368 +3711 +19583 +17367 +292 +12903 +16330 +5391 +13254 +494 +16445 +13690 +7236 +15348 +14251 +9734 +13249 +16374 +11516 +7995 +5557 +5923 +11866 +7832 +5799 +19500 +8329 +18652 +19034 +5186 +14753 +1061 +3947 +5497 +4861 +9169 +15018 +12213 +2179 +13884 +5766 +4358 +14866 +3880 +19470 +14598 +11652 +10155 +19732 +13138 +4287 +131 +16430 +7010 +4214 +12811 +1373 +9830 +13243 +3291 +12009 +14404 +3379 +12855 +1414 +13853 +14429 +4053 +3056 +4022 +9318 +7637 +3941 +5417 +16719 +8978 +16432 +19505 +12738 +1730 +16789 +18145 +18371 +3497 +8119 +7470 +17800 +13870 +18571 +6554 +11431 +14445 +7197 +1933 +15623 +12777 +7361 +13844 +143 +10883 +14593 +11806 +2645 +17512 +6887 +18030 +3128 +4333 +8911 +10198 +12129 +5486 +8823 +17547 +14551 +11753 +10950 +16379 +8214 +2067 +18622 +17927 +14904 +4844 +14790 +5246 +12162 +10631 +649 +15091 +14794 +11946 +6781 +17949 +15902 +6055 +8503 +9719 +7506 +15682 +4291 +15994 +13645 +9256 +4156 +12447 +7339 +3122 +3601 +19670 +15202 +14577 +17212 +6424 +6674 +4651 +2075 +8017 +8278 +12156 +8651 +5077 +1480 +3701 +4011 +9612 +17701 +12062 +19899 +15976 +12877 +14421 +18792 +6460 +12295 +13661 +3963 +3847 +12668 +5080 +16134 +2026 +12629 +10804 +10771 +12274 +214 +4299 +9503 +19905 +6019 +2035 +6059 +17521 +5218 +16809 +536 +6746 +12068 +9279 +19432 +15122 +10060 +10991 +6585 +1998 +10142 +3087 +11964 +1525 +1860 +11818 +2219 +14103 +13693 +5011 +7139 +13527 +5879 +7955 +9316 +16697 +8869 +11251 +1534 +4284 +733 +17527 +5569 +14351 +17418 +15542 +14860 +9767 +13284 +15301 +15678 +15219 +17311 +11024 +5699 +4445 +12438 +13540 +746 +6888 +19798 +6721 +1389 +145 +4078 +8049 +15621 +6921 +12862 +14073 +18173 +10683 +5789 +18085 +2374 +16355 +14745 +12662 +4894 +8104 +12685 +6399 +13724 +10841 +18069 +9887 +2652 +11241 +18298 +8535 +8474 +16926 +9249 +7268 +6697 +19167 +1891 +11305 +8575 +18527 +6213 +8178 +13619 +19999 +18898 +13321 +10640 +8731 +7009 +7686 +2565 +4069 +12508 +15261 +2480 +18716 +17238 +15868 +8197 +19619 +14934 +18585 +10536 +767 +13067 +8857 +18953 +16893 +13144 +19008 +4964 +9658 +4364 +9335 +8459 +379 +18910 +3409 +6979 +1409 +4906 +15490 +630 +2038 +2139 +5842 +6783 +696 +18035 +4543 +14226 +19690 +5147 +9021 +6036 +1128 +4325 +13201 +18119 +10323 +9701 +9494 +10550 +18311 +15527 +19917 +1122 +3407 +19589 +4884 +11907 +196 +19568 +6717 +13372 +13179 +16909 +14987 +6436 +14334 +5811 +15522 +1797 +3359 +15798 +8064 +7523 +11405 +15664 +8189 +14549 +19802 +2658 +3425 +14066 +15800 +11281 +16488 +9329 +8763 +10973 +3798 +5228 +3687 +13819 +9967 +3652 +8239 +8705 +8318 +10694 +7897 +9937 +11380 +17016 +10220 +12399 +17621 +1650 +17071 +14603 +15735 +7510 +3269 +17403 +4318 +3343 +2797 +3306 +3109 +5897 +3526 +9286 +14601 +10375 +16691 +10859 +16013 +14381 +1922 +8004 +7460 +19022 +10605 +14881 +8109 +16389 +9692 +435 +5 +1713 +7550 +12168 +4808 +9708 +9148 +4876 +16569 +6508 +13657 +17582 +1709 +1232 +16140 +4037 +17249 +14610 +1293 +8356 +7380 +8971 +14378 +19185 +16906 +15180 +14222 +3003 +8038 +17821 +11942 +18488 +10903 +11667 +608 +2157 +12806 +8649 +3983 +16528 +14 +15311 +17327 +13842 +19256 +16648 +184 +9485 +15858 +1309 +4362 +16655 +17745 +13184 +9441 +4408 +16967 +19195 +16827 +16671 +9188 +3824 +18471 +6317 +14144 +13056 +10402 +1108 +18707 +4121 +16297 +11827 +18002 +19301 +16290 +10822 +7114 +17389 +534 +2364 +1159 +17258 +18341 +6967 +13564 +2743 +6768 +8031 +18141 +363 +18456 +14654 +7884 +19790 +8115 +12111 +8521 +12506 +7756 +7607 +5973 +8925 +18952 +11686 +6895 +5100 +9731 +3107 +7847 +1580 +4143 +2344 +7721 +7342 +13237 +6268 +12144 +4032 +10943 +4063 +565 +15758 +18159 +1539 +14732 +8563 +18624 +7076 +9595 +13354 +4046 +7679 +5455 +14993 +13154 +4082 +2148 +10549 +12991 +5394 +4182 +12226 +16029 +19740 +16292 +4952 +19380 +10330 +10990 +17685 +6678 +6527 +18455 +17874 +9650 +18494 +3807 +2346 +6256 +11483 +1874 +14111 +10073 +10328 +692 +10003 +10563 +9710 +8137 +3685 +16689 +129 +6753 +5559 +12231 +1290 +4105 +8819 +5636 +14720 +5296 +11586 +16631 +7615 +11047 +14797 +7355 +1883 +14151 +8546 +15245 +11203 +1408 +11596 +1398 +4197 +14191 +7156 +16091 +19409 +1633 +5277 +4310 +6226 +454 +9288 +5453 +9412 +3163 +5238 +10146 +6151 +2424 +4145 +18612 +16892 +19907 +5693 +15343 +12492 +13020 +965 +4898 +17448 +4652 +10150 +17739 +16189 +4860 +4473 +8136 +8583 +7726 +1771 +2539 +8935 +12164 +12192 +11527 +5119 +14890 +9766 +2570 +16100 +9570 +14602 +16325 +7391 +6299 +14230 +8447 +5740 +6443 +14046 +17740 +10235 +19488 +13597 +2875 +17644 +16773 +1058 +12614 +13906 +16543 +16394 +14939 +14905 +11468 +18827 +18564 +19462 +13002 +14430 +13677 +9396 +5223 +16987 +11600 +8430 +6621 +13135 +12534 +4158 +10596 +14768 +3334 +16593 +7267 +82 +1042 +13700 +13928 +3593 +2728 +17783 +13616 +18860 +16477 +1884 +3341 +4455 +8523 +5360 +6744 +9787 +712 +12234 +64 +9539 +6601 +14071 +18366 +5169 +15012 +16284 +4748 +2977 +5519 +8456 +7158 +8475 +9258 +16579 +8251 +3106 +10428 +19736 +1630 +16367 +4317 +15059 +16195 +7278 +5500 +8624 +6245 +18284 +12479 +4221 +17256 +17119 +135 +9422 +8909 +7927 +10264 +13017 +7051 +12580 +4089 +192 +11988 +6610 +19422 +8392 +17224 +5795 +5322 +1995 +6348 +9236 +4495 +6127 +19207 +16255 +4836 +9538 +19629 +14271 +11400 +3575 +18824 +4141 +15209 +13699 +2686 +14671 +6450 +13634 +18974 +19253 +14865 +16388 +18247 +1431 +12762 +7066 +5608 +5924 +6989 +14656 +9274 +6333 +15028 +6261 +1608 +11467 +19389 +13533 +1491 +3458 +1015 +2794 +8610 +18369 +7108 +7727 +10071 +19320 +8491 +12090 +8934 +3015 +9067 +16834 +4963 +14605 +18596 +2405 +1991 +6503 +15378 +8231 +17919 +4285 +2496 +555 +793 +2366 +13341 +18687 +1202 +6599 +17973 +11949 +9640 +5875 +12414 +14414 +6094 +10920 +13905 +18533 +10406 +13899 +8976 +1188 +7873 +19188 +12857 +16770 +10304 +18077 +9698 +17593 +12390 +11536 +9862 +5744 +5033 +13998 +5743 +1031 +10481 +9928 +5362 +5057 +5149 +18682 +5295 +2131 +5474 +8808 +3726 +14455 +4422 +12459 +8650 +17260 +14754 +15993 +5910 +18194 +19914 +4904 +11235 +6712 +2430 +9566 +7643 +19651 +10145 +19816 +18051 +15056 +6195 +2502 +12838 +259 +8380 +13378 +5311 +5826 +19153 +13762 +14531 +12340 +19532 +4780 +15970 +5958 +17384 +15415 +11795 +9404 +7356 +11945 +8321 +10830 +321 +18044 +5786 +6319 +12251 +10016 +2739 +7976 +15319 +4319 +4911 +5599 +12753 +11303 +1210 +10440 +11546 +6635 +18617 +19729 +662 +18724 +16559 +17829 +8114 +19373 +14057 +16199 +5306 +2419 +3513 +17503 +3405 +1569 +15166 +3577 +15124 +9460 +3982 +12280 +18257 +5138 +15084 +5883 +3655 +18036 +18505 +11417 +7229 +11943 +19333 +7107 +19524 +4591 +754 +17184 +9066 +5834 +11322 +8985 +3984 +13421 +3023 +13334 +18132 +2854 +7330 +5410 +18118 +4630 +6441 +12951 +7407 +170 +12047 +5117 +683 +3403 +5236 +48 +14544 +18723 +2059 +816 +14524 +6826 +17732 +7325 +17945 +14759 +16490 +12132 +8464 +7878 +12376 +13459 +304 +11191 +13578 +3797 +16282 +3876 +14933 +9528 +3901 +16331 +429 +7880 +2484 +3398 +19582 +1596 +933 +9747 +18174 +2976 +12715 +1793 +8877 +2291 +2237 +6528 +16638 +6294 +279 +16974 +11660 +14995 +6694 +8041 +10029 +7527 +4754 +4013 +671 +7549 +18405 +12204 +9904 +11927 +11064 +7059 +14336 +4228 +15510 +1463 +1763 +17946 +1144 +14279 +15274 +9282 +16781 +17761 +14361 +4040 +9075 +19980 +4399 +13015 +8746 +17579 +17764 +18041 +7678 +3115 +14892 +13945 +4227 +1014 +5415 +570 +18354 +18808 +19142 +6619 +19507 +15353 +1381 +10597 +14597 +11939 +11383 +4369 +19836 +18420 +11034 +15451 +2912 +13181 +17693 +1844 +14101 +6400 +4602 +813 +17818 +14729 +13534 +10620 +18667 +3751 +15942 +9006 +10752 +11965 +13946 +2648 +11486 +19726 +7409 +11815 +15088 +1116 +5694 +1914 +433 +14816 +9631 +17106 +7899 +14328 +6912 +485 +16628 +11183 +17172 +7634 +10806 +5383 +16801 +14746 +19564 +19742 +13883 +13397 +15364 +1255 +2775 +12225 +7235 +19495 +3944 +17472 +1581 +19200 +7814 +3585 +19346 +14017 +899 +18850 +4157 +16396 +16035 +8732 +13932 +17177 +13345 +19379 +1145 +897 +17560 +4224 +16014 +16988 +4311 +13296 +1552 +10120 +13326 +5962 +16790 +19631 +10321 +10996 +4099 +17222 +7567 +18442 +19656 +6238 +19874 +16230 +12050 +2236 +11548 +7056 +14571 +1382 +1642 +14526 +14518 +16550 +6581 +19885 +223 +7449 +1605 +16119 +10624 +15328 +2097 +10058 +6965 +5217 +1196 +11506 +4668 +9397 +4270 +18060 +11229 +7898 +7554 +13048 +13155 +15733 +4900 +1715 +12112 +3515 +16874 +9746 +42 +13347 +11552 +1110 +3879 +19871 +974 +7765 +6836 +3355 +124 +13405 +1469 +13989 +11169 +14533 +1105 +3168 +15740 +18925 +9135 +736 +13177 +1101 +13116 +19539 +19803 +11259 +10571 +93 +4170 +2136 +2149 +8315 +5835 +5146 +16683 +14300 +8141 +14570 +16242 +10722 +6364 +11082 +13458 +8339 +7965 +6334 +16884 +12771 +14021 +550 +8998 +51 +17976 +19806 +703 +1339 +4380 +8285 +9915 +9806 +16158 +3052 +18125 +1999 +18684 +13119 +2395 +8639 +19154 +2367 +19891 +5733 +14450 +8069 +17660 +16498 +997 +1941 +7448 +8520 +14805 +6120 +11676 +14629 +186 +17567 +5106 +355 +8831 +8257 +18066 +19011 +17346 +17940 +17746 +3905 +11148 +14121 +16966 +9385 +13780 +6134 +13248 +17871 +19839 +8060 +2276 +19749 +2901 +15838 +16115 +7095 +8085 +16777 +1674 +10712 +15745 +3953 +3837 +8037 +10572 +9635 +14477 +11699 +827 +5470 +5852 +9543 +1751 +5889 +18669 +16258 +9054 +17331 +13029 +12408 +4429 +10307 +15826 +14301 +973 +16123 +276 +16012 +9945 +18403 +15923 +17262 +15662 +9810 +9923 +7320 +8457 +16911 +10992 +14162 +12969 +10473 +16845 +16935 +3251 +11115 +8087 +12731 +9823 +5242 +7432 +10216 +18202 +10939 +17401 +15813 +3446 +3081 +18924 +15029 +16668 +317 +3012 +19552 +11206 +6558 +6081 +3628 +9662 +6419 +15498 +13943 +15513 +7818 +990 +6923 +68 +15508 +15161 +5127 +4534 +9165 +3951 +8100 +14187 +16434 +18842 +10691 +1620 +14783 +7801 +3241 +2583 +10907 +14552 +19695 +3846 +13358 +2516 +6664 +13523 +1545 +11097 +17269 +19369 +14156 +17245 +17640 +14426 +780 +17131 +11392 +4758 +7819 +19694 +6701 +6 +13947 +17021 +17924 +7401 +4138 +322 +13588 +10490 +8715 +8267 +6898 +10476 +5051 +17392 +3042 +14317 +17980 +6122 +13792 +12560 +11510 +15968 +13481 +17974 +2988 +14875 +4936 +10290 +6408 +3543 +8371 +13400 +19824 +6448 +92 +5547 +6228 +15940 +19420 +2314 +14093 +19285 +16682 +17955 +18802 +7961 +15630 +16023 +11819 +13723 +10369 +3833 +9724 +14906 +18783 +8086 +12179 +6687 +19870 +18518 +4144 +16795 +4781 +14676 +9051 +16876 +6409 +5315 +12644 +1062 +2581 +14210 +14092 +18300 +7029 +15641 +9977 +12109 +13322 +1312 +7861 +8218 +17468 +156 +12623 +14235 +16161 +18642 +16952 +15766 +5871 +7614 +18929 +6142 +12679 +18408 +15070 +1377 +12570 +13385 +16243 +17250 +18081 +19255 +506 +3915 +571 +12497 +4080 +19023 +8920 +6752 +7676 +4499 +4480 +16328 +16656 +14090 +8466 +9488 +2876 +11340 +19962 +7599 +136 +11041 +1976 +13485 +15259 +6786 +16277 +17096 +6086 +1155 +1056 +7694 +19490 +14437 +7592 +19787 +5475 +17600 +2226 +18997 +9883 +12463 +12711 +6354 +2538 +13032 +13256 +5251 +9382 +15900 +342 +60 +14505 +10564 +16440 +18611 +12910 +4570 +5290 +6246 +12646 +12124 +11903 +4114 +5877 +14619 +16366 +17310 +8269 +8063 +12941 +5439 +4672 +13660 +19265 +6164 +691 +5819 +19567 +3586 +12884 +463 +3722 +9383 +15725 +13096 +18398 +16383 +2272 +12977 +14807 +9041 +1445 +4237 +6946 +15317 +1607 +11531 +11336 +16343 +4271 +19812 +6220 +2839 +8725 +13567 +3746 +7737 +8132 +5393 +1279 +19012 +9170 +12350 +7839 +19813 +840 +3313 +7600 +12378 +19107 +5762 +4650 +17686 +601 +13133 +8396 +3794 +19 +13838 +3387 +5441 +12389 +15696 +10875 +12803 +16761 +5972 +5866 +15504 +17399 +8488 +2958 +18573 +8886 +16413 +5205 +13433 +10655 +661 +6616 +15515 +7253 +8342 +2612 +3134 +4165 +4972 +782 +1829 +994 +12931 +12936 +4155 +13115 +4503 +15575 +12267 +17414 +16263 +6652 +15759 +8463 +2156 +4001 +17493 +17236 +10519 +15734 +9902 +2899 +12783 +11925 +19649 +18825 +15683 +18897 +18155 +278 +13790 +6180 +14615 +5616 +9942 +12801 +15687 +5249 +6727 +11421 +16176 +15891 +16112 +3219 +19367 +10367 +12593 +2889 +6144 +650 +12616 +7037 +8346 +808 +18834 +15385 +2616 +617 +14786 +12559 +19508 +17657 +9012 +19716 +4107 +8946 +956 +12154 +4215 +11869 +16151 +17287 +18666 +5318 +6745 +10410 +1067 +8018 +5305 +1983 +18588 +12684 +8572 +529 +13503 +5510 +6185 +14035 +10441 +1183 +7232 +9933 +12188 +18404 +3853 +11560 +15524 +789 +10959 +5000 +6490 +8541 +16202 +544 +19494 +8642 +4562 +4776 +19919 +13538 +18005 +9880 +8858 +13826 +16786 +4957 +16752 +4800 +15603 +9596 +16896 +2578 +12051 +7294 +17473 +3669 +11445 +18692 +6846 +10007 +4470 +5649 +16365 +5376 +13628 +13590 +5188 +2250 +2390 +14215 +18135 +420 +1652 +13217 +7493 +12689 +7716 +13398 +4297 +18088 +12986 +7571 +13886 +2045 +18737 +18243 +18971 +3610 +14397 +3579 +15336 +6321 +6391 +1719 +17319 +17572 +15230 +6030 +17246 +9619 +12294 +2268 +15325 +9604 +14781 +17879 +3826 +4181 +15004 +12981 +11931 +3501 +12309 +19467 +16188 +8 +5761 +5047 +7931 +9004 +17186 +14911 +2842 +1834 +19606 +12927 +12540 +17812 +13687 +17206 +9098 +16417 +11704 +486 +2431 +17720 +11230 +4778 +19738 +8234 +1102 +15532 +906 +10229 +4889 +16302 +10964 +9673 +4084 +9912 +3422 +7104 +14634 +10845 +10107 +11378 +14685 +2387 +18342 +15699 +3608 +13286 +12217 +5020 +16120 +8543 +12979 +15796 +6285 +16456 +4969 +18845 +751 +6159 +16228 +2015 +3849 +9102 +849 +13417 +1778 +2108 +810 +6833 +17100 +4136 +4526 +348 +17187 +272 +5831 +16304 +12874 +7566 +4659 +3616 +9359 +18645 +3395 +8105 +1424 +14024 +18314 +18123 +2361 +17857 +2684 +2114 +8646 +10010 +17429 +12024 +10773 +6044 +9399 +16045 +542 +15225 +7062 +17619 +4266 +9973 +17451 +11670 +749 +9490 +5913 +5705 +19084 +13556 +7908 +8753 +1939 +5448 +15643 +11219 +6994 +5668 +7353 +19668 +18092 +2999 +15511 +18207 +2458 +9269 +18782 +14041 +19358 +18175 +7113 +5123 +11606 +13263 +17892 +16218 +17449 +302 +8544 +15266 +19465 +15546 +10679 +13494 +6645 +18063 +12033 +8167 +4509 +13501 +258 +18154 +12290 +955 +13412 +12081 +7255 +3542 +11523 +5713 +16957 +10784 +19886 +6200 +12116 +10209 +10858 +18213 +4051 +5413 +3900 +5108 +16709 +17534 +19460 +8025 +8304 +7176 +9374 +3312 +4346 +8566 +19106 +6582 +14715 +12146 +18011 +1930 +353 +4631 +212 +7994 +9144 +18080 +12526 +18229 +5619 +6973 +19112 +9953 +5365 +7482 +3955 +19937 +7042 +13816 +17966 +2216 +9590 +17203 +8882 +4918 +5865 +17565 +10643 +6811 +12356 +3950 +3632 +5804 +2244 +15663 +19481 +19534 +14651 +15785 +152 +13732 +2563 +13773 +11820 +12989 +7313 +16704 +8337 +19352 +7817 +2633 +15432 +8955 +18497 +15417 +8848 +4148 +3764 +10395 +14673 +2832 +6877 +15756 +19707 +4682 +8989 +9450 +2535 +9723 +6006 +12912 +2255 +521 +7730 +277 +15079 +8893 +11751 +318 +15397 +6416 +4171 +12817 +12345 +11092 +19364 +18796 +19477 +5517 +5241 +14964 +10554 +16073 +5680 +9393 +12410 +305 +4274 +4973 +7369 +15226 +5568 +12591 +2721 +9655 +11535 +9032 +8160 +10585 +16016 +7561 +8812 +9828 +5266 +1351 +5358 +7389 +3806 +18586 +12487 +15436 +4986 +9498 +12180 +4337 +15483 +8271 +11957 +16279 +9859 +15845 +1502 +11066 +6412 +2620 +3718 +14954 +19976 +10790 +4471 +4189 +18102 +18519 +14333 +12339 +17361 +8204 +4937 +1295 +19094 +868 +4859 +19365 +3413 +1775 +13156 +5936 +5390 +11015 +2632 +11963 +7828 +6039 +3276 +5656 +17665 +7501 +1080 +9674 +11243 +7922 +11042 +6307 +13892 +13940 +9482 +8866 +15118 +6576 +1784 +17951 +1901 +8366 +17129 +10200 +16885 +19240 +16170 +3948 +3835 +7631 +12396 +19611 +17295 +9348 +8686 +12530 +3715 +3212 +9841 +8213 +1604 +11849 +1684 +465 +10127 +6136 +10256 +1758 +13323 +1538 +17436 +2032 +12036 +8338 +10050 +17148 +14112 +15592 +5090 +13384 +15492 +15339 +13173 +6208 +4736 +18764 +18690 +18124 +1396 +18789 +9741 +18713 +10086 +16093 +4799 +19020 +1485 +365 +17407 +15632 +3063 +5342 +13070 +1329 +8878 +11152 +13787 +15979 +4891 +16398 +7218 +11289 +18249 +5469 +11418 +10870 +1887 +16429 +8675 +7986 +11159 +5387 +58 +9168 +16846 +17467 +9452 +17447 +16503 +651 +5458 +6370 +10169 +10063 +18820 +406 +17339 +17417 +1384 +19709 +12480 +9303 +2448 +5043 +6791 +3077 +6666 +19340 +17611 +10865 +9900 +11742 +12695 +17002 +14767 +11634 +17841 +19678 +9097 +11650 +1808 +110 +1770 +10863 +17491 +14461 +3759 +5698 +12149 +19093 +17735 +5231 +2271 +14514 +12100 +13467 +2066 +2445 +15481 +5126 +3149 +16664 +6392 +10399 +65 +7855 +7970 +18525 +6126 +15844 +5768 +18805 +9621 +15101 +5300 +19530 +5312 +4151 +13860 +19004 +1266 +10777 +5150 +4816 +5172 +252 +702 +5823 +5863 +11111 +11508 +13339 +3729 +10030 +9843 +2624 +17380 +8679 +3709 +8051 +9589 +3365 +3280 +18010 +15057 +230 +12690 +8969 +11484 +19218 +13348 +17118 +2100 +14262 +10255 +594 +19852 +7830 +19461 +5345 +5502 +6363 +2256 +13852 +17012 +2494 +19958 +4995 +15409 +6789 +3974 +10122 +1511 +7786 +1248 +8127 +13084 +2500 +18524 +19013 +5258 +15961 +3004 +5337 +523 +19968 +6275 +8662 +19779 +10343 +1977 +15801 +12321 +16162 +7341 +9281 +14796 +9310 +19512 +19405 +19782 +14145 +13741 +19751 +150 +11990 +11568 +9552 +1184 +11165 +2873 +17804 +18743 +14394 +16041 +15676 +8228 +15025 +8795 +720 +5934 +349 +16651 +11366 +19021 +424 +14289 +2211 +8771 +5009 +18528 +17697 +19558 +3653 +11674 +14124 +6360 +18317 +13045 +16862 +17105 +14501 +10910 +18357 +13959 +17312 +11254 +6699 +1163 +14463 +17133 +1379 +3424 +19235 +16506 +13474 +2966 +750 +6251 +17774 +6665 +11172 +16449 +18591 +11218 +15077 +11681 +7650 +1566 +16690 +971 +5472 +19354 +623 +5621 +14700 +9018 +15048 +10975 +12805 +2052 +19103 +16048 +12467 +6146 +11128 +13798 +16299 +5624 +11716 +15569 +12681 +19003 +14089 +5005 +17643 +18054 +8507 +624 +9764 +430 +5139 +12955 +17633 +18795 +1088 +19129 +843 +19819 +14469 +15666 +4582 +9920 +13681 +10066 +8065 +9648 +2202 +96 +7484 +1267 +690 +16179 +3182 +5125 +10811 +5724 +15850 +2222 +9046 +6423 +13733 +15684 +15392 +17495 +12863 +498 +10164 +18966 +12418 +6222 +9976 +288 +16999 +15426 +13872 +14451 +9547 +10515 +7344 +854 +7902 +2768 +15997 +19294 +17308 +12595 +18887 +2689 +6909 +12091 +12844 +3926 +6542 +15474 +18227 +201 +5498 +17574 +4109 +9572 +4954 +6101 +7505 +16538 +16793 +17192 +17517 +351 +5133 +2050 +1282 +18186 +11233 +14645 +19337 +6158 +3351 +914 +5701 +3136 +5414 +1420 +11999 +11489 +7586 +442 +7805 +3809 +3138 +8291 +13592 +16618 +16338 +2127 +3972 +5562 +9028 +14912 +5653 +4166 +3743 +7872 +599 +6954 +10668 +2845 +19100 +16617 +154 +11374 +6779 +14547 +13415 +17097 +18994 +8608 +14527 +2486 +1154 +17689 +7869 +15346 +2994 +13851 +7350 +5431 +19834 +13357 +5989 +17009 +10533 +2351 +14369 +2285 +15677 +15380 +1910 +13132 +5639 +4735 +3009 +12004 +14330 +12835 +7845 +19647 +4479 +7473 +9394 +11296 +5279 +7863 +10458 +3954 +8373 +5988 +1220 +461 +2378 +14855 +5317 +10956 +1916 +4531 +4698 +15306 +11094 +12270 +13226 +8061 +14314 +9906 +8102 +11665 +3427 +962 +4411 +9899 +4033 +2513 +18110 +12481 +6737 +3661 +13591 +1504 +5545 +17726 +11208 +2362 +17204 +12377 +13014 +15207 +4708 +2123 +18387 +10018 +10180 +3360 +13529 +972 +12769 +6091 +11867 +2515 +17622 +7430 +18493 +19051 +18031 +221 +9493 +18830 +106 +12860 +6790 +1446 +1412 +3044 +4097 +9986 +3684 +9728 +656 +8351 +10291 +19880 +9679 +3629 +15564 +7370 +13046 +5887 +11953 +12133 +5966 +3892 +19597 +7750 +707 +5503 +1714 +9459 +12006 +1362 +12357 +14220 +5717 +18583 +1321 +15403 +19546 +3620 +890 +17508 +4586 +9158 +15310 +923 +17278 +6502 +9406 +1790 +1540 +16916 +14091 +13304 +4494 +10326 +2230 +56 +13219 +17320 +14202 +15503 +3861 +78 +10493 +4999 +1081 +17061 +11221 +10599 +12757 +8972 +1653 +15651 +9007 +3796 +5485 +14396 +13088 +4283 +5605 +13224 +12841 +4878 +9395 +10511 +11666 +4720 +5286 +9628 +18394 +3878 +16354 +4939 +8961 +14625 +19956 +1130 +227 +3913 +5466 +3840 +8302 +5604 +10791 +985 +7015 +210 +8742 +3455 +14814 +15884 +6188 +10504 +18972 +5749 +9758 +7990 +1103 +17428 +17696 +7485 +18153 +16044 +19587 +7929 +14339 +14636 +8643 +19199 +16971 +1486 +11765 +10794 +11709 +7038 +9211 +19761 +10590 +18417 +15283 +3850 +11144 +11415 +17838 +8117 +19322 +9644 +18451 +12386 +7331 +17077 +1241 +13242 +11125 +18440 +6857 +14276 +948 +13000 +13679 +18108 +14331 +14504 +9428 +15808 +15276 +1889 +15889 +7092 +4491 +9983 +5339 +14909 +2277 +18390 +8313 +19687 +2677 +15712 +4442 +8932 +17952 +9513 +9342 +16675 +14298 +11027 +2809 +12147 +16592 +1466 +9322 +17985 +1361 +15154 +6515 +15035 +3816 +7558 +11435 +19043 +6607 +13969 +4050 +19652 +17195 +16509 +18631 +13091 +121 +4472 +6568 +63 +12768 +10195 +16562 +13875 +19791 +1584 +7606 +4387 +15320 +11616 +711 +3083 +9262 +10710 +431 +15922 +17958 +4246 +513 +4971 +15273 +4172 +179 +3030 +3110 +17836 +3411 +7670 +12478 +2343 +11805 +5184 +18447 +4592 +12949 +16164 +8917 +14532 +10124 +14711 +18508 +5684 +2267 +15883 +5745 +19792 +5436 +3051 +8936 +8933 +11459 +14963 +7533 +14460 +16624 +19992 +10765 +10332 +14389 +13601 +10117 +19429 +7209 +5347 +2379 +4765 +12342 +17445 +13760 +6061 +13939 +3600 +4168 +17313 +11921 +7373 +2793 +4088 +10740 +9908 +1873 +13799 +11871 +6386 +17673 +18756 +6457 +9522 +14324 +7836 +19278 +19573 +14453 +710 +12582 +6241 +16363 +8485 +16552 +14762 +15601 +5534 +14245 +13043 +16439 +17385 +10985 +13933 +12906 +3602 +727 +18115 +3648 +17155 +10543 +8471 +13825 +10986 +5137 +9331 +15772 +18383 +11781 +10937 +10474 +3173 +13848 +10555 +15690 +7083 +3299 +12421 +5542 +9260 +14095 +9350 +14971 +6232 +7345 +77 +17822 +374 +912 +14680 +12700 +10161 +16652 +18815 +16724 +4907 +5555 +12470 +7205 +578 +7780 +851 +1865 +16522 +9008 +7293 +9463 +3192 +18536 +5030 +4959 +18579 +4587 +6512 +1024 +10980 +10898 +13636 +3095 +18779 +1733 +12699 +16512 +16480 +1179 +10921 +14594 +14388 +18748 +8898 +7486 +55 +17440 +9243 +1668 +8540 +13289 +9436 +17648 +346 +12871 +16749 +164 +12634 +19758 +8937 +15947 +1861 +17849 +4989 +12096 +16698 +11948 +8399 +12457 +15480 +12110 +11619 +16899 +11292 +9074 +5373 +14465 +15305 +10982 +6406 +112 +1284 +9356 +13890 +8959 +15879 +580 +7714 +5506 +3190 +17881 +14872 +4306 +18338 +9994 +17684 +17898 +87 +472 +6161 +4723 +3447 +18148 +3158 +18542 +4948 +3738 +5297 +19281 +14788 +15127 +13776 +13611 +10153 +7853 +12450 +10108 +14227 +19753 +6458 +1003 +19973 +15140 +6123 +16735 +6928 +4327 +10420 +16963 +940 +19622 +17196 +1925 +10566 +9358 +12819 +2030 +9681 +10077 +4334 +6690 +14318 +16043 +426 +1151 +8349 +4942 +5598 +2853 +19639 +11215 +4920 +13434 +9791 +2586 +5757 +5697 +14377 +16353 +18217 +8190 +10013 +18738 +19502 +18120 +9467 +9864 +16532 +11069 +8612 +1937 +1372 +15299 +16473 +5612 +12577 +15073 +716 +13517 +16768 +18633 +14902 +15125 +1093 +2678 +7539 +5928 +6696 +8923 +10954 +2918 +12820 +2851 +16694 +7692 +16254 +19650 +2386 +4220 +4701 +4004 +17718 +2989 +6924 +477 +5937 +9535 +4961 +16542 +12774 +1691 +10135 +14188 +17597 +15935 +14882 +4357 +13662 +18206 +8885 +5504 +7217 +5111 +5964 +11358 +3451 +8604 +8519 +15887 +12545 +982 +16497 +11323 +11385 +18188 +19645 +10984 +15886 +2551 +6679 +12606 +10824 +3956 +11180 +7574 +4028 +16212 +3914 +7677 +14076 +13885 +11514 +1767 +10012 +12423 +9778 +2821 +2811 +7956 +769 +14842 +19029 +13439 +18214 +2223 +3792 +10346 +9860 +517 +15205 +19674 +15361 +16625 +14219 +16820 +19101 +4351 +640 +18765 +11121 +4350 +16482 +6707 +6810 +14542 +2791 +8006 +12823 +689 +2929 +19232 +8752 +1342 +7597 +19672 +16838 +830 +14492 +16941 +14633 +3724 +989 +4184 +1453 +8029 +3327 +3677 +16206 +2457 +17398 +8335 +10118 +266 +5533 +4680 +11379 +17478 +9527 +19617 +1678 +10329 +2741 +18138 +2456 +16540 +12104 +11443 +1298 +2118 +17615 +5995 +6862 +3488 +15526 +11304 +4840 +8637 +15790 +775 +1137 +8983 +18397 +9798 +722 +5346 +15242 +12505 +15983 +19275 +15580 +13633 +13717 +11944 +12196 +10522 +13473 +2644 +10497 +17461 +17569 +6378 +8597 +16327 +1211 +4162 +6847 +4340 +19951 +1599 +7135 +15964 +5626 +14662 +11134 +5105 +12657 +17545 +3080 +18801 +11012 +16064 +4083 +2642 +18572 +977 +12963 +1483 +9630 +11837 +71 +10721 +3758 +18526 +10761 +8113 +4746 +9448 +3092 +16557 +17825 +18446 +8252 +392 +18106 +2957 +14830 +13937 +13308 +3804 +755 +7778 +17863 +8619 +13266 +17882 +16879 +19924 +11389 +11255 +14986 +15788 +1779 +7856 +19252 +11341 +805 +3380 +9491 +5805 +10094 +15694 +17089 +3040 +11579 +3307 +15278 +10400 +6058 +9593 +13908 +17052 +7700 +4979 +11938 +16412 +18864 +4819 +12537 +4342 +8687 +12683 +12049 +13008 +15497 +992 +6943 +19289 +12947 +1456 +14316 +8409 +16042 +13950 +13145 +14558 +14961 +10435 +15751 +2927 +15037 +13411 +13988 +16928 +12420 +14982 +3404 +15797 +14812 +19720 +11288 +1366 +4130 +9898 +998 +14385 +2657 +12093 +18980 +12937 +15151 +224 +10603 +2698 +3295 +6210 +10846 +11517 +11958 +7695 +9031 +2278 +17046 +15210 +7702 +11565 +2208 +877 +14359 +14139 +14411 +9053 +12569 +5575 +14752 +6876 +10816 +9560 +3562 +3894 +11237 +12842 +3335 +17299 +5688 +16983 +12775 +8516 +18919 +5076 +6027 +11966 +18406 +13103 +18220 +7896 +14930 +1302 +8829 +8453 +314 +10673 +4087 +7300 +19948 +800 +10413 +8825 +11842 +17749 +8810 +4490 +17681 +836 +12925 +11007 +9020 +5874 +9433 +7709 +8667 +4530 +1106 +9895 +17803 +16113 +8158 +1187 +19264 +6286 +19216 +16332 +2287 +19260 +15395 +17989 +17056 +4257 +6020 +12588 +11996 +9770 +6531 +5827 +1785 +12064 +10807 +10294 +19059 +4504 +15596 +6438 +4825 +7552 +9057 +18677 +8279 +17911 +19664 +4538 +17226 +19027 +9047 +17028 +14974 +14105 +2303 +11840 +9568 +10111 +3082 +10249 +13152 +5532 +14213 +16516 +18014 +19653 +8949 +10972 +14520 +16575 +3656 +14027 +15027 +9694 +12890 +10080 +18771 +16314 +10556 +12661 +19972 +18649 +699 +13948 +7751 +3131 +6784 +2676 +275 +2888 +16062 +7163 +19120 +12723 +17616 +18763 +10595 +15530 +2995 +783 +13107 +3416 +8081 +8176 +17959 +1728 +10932 +19737 +12468 +15440 +10682 +10281 +10498 +11967 +9314 +9189 +11332 +17661 +18196 +14557 +786 +17267 +8999 +4686 +1022 +7812 +18280 +456 +4312 +10302 +2906 +8606 +19544 +14775 +730 +16858 +19191 +6550 +13128 +17408 +18023 +9971 +19711 +11312 +4487 +4085 +15128 +14255 +8297 +18539 +787 +331 +6999 +12733 +10978 +14555 +16517 +15097 +8241 +3234 +15937 +13922 +17459 +1870 +17768 +14584 +19259 +5116 +5727 +931 +3767 +8005 +2607 +11446 +16883 +13893 +17149 +6018 +13261 +19066 +4685 +13725 +10893 +8622 +19603 +3692 +7888 +10635 +14670 +14441 +16672 +10622 +10074 +11786 +2699 +19206 +8367 +18976 +3421 +10434 +4451 +7403 +3857 +1799 +13049 +18178 +8514 +10714 +6771 +3152 +18337 +10109 +1083 +8326 +19805 +5950 +19847 +4527 +5855 +11501 +19808 +6397 +15479 +19244 +4186 +2528 +12598 +7043 +17510 +15752 +11162 +6982 +6216 +6264 +13484 +2039 +19015 +2433 +2694 +1544 +8215 +10538 +16695 +118 +16077 +11429 +1496 +11057 +13818 +14005 +12615 +5221 +12602 +3674 +301 +14175 +13343 +600 +14983 +524 +820 +15281 +11539 +4133 +4198 +19963 +5947 +10165 +15234 +11491 +4135 +4983 +1876 +7528 +7304 +1562 +15212 +1043 +19677 +4850 +16854 +8897 +18780 +14903 +3881 +5463 +5893 +15405 +16276 +1209 +13525 +6806 +10732 +7767 +13288 +15528 +6372 +12709 +6129 +12409 +9811 +18287 +2895 +15777 +401 +3278 +8573 +1554 +6500 +15252 +13647 +11399 +2242 +12502 +12507 +3477 +17207 +10341 +15525 +4823 +9161 +6308 +13294 +10629 +16245 +3296 +3538 +2801 +10584 +9403 +12498 +8714 +5198 +1338 +19990 +3574 +7314 +16620 +7569 +3494 +1 +14065 +3145 +17457 +9284 +7180 +10892 +7111 +8180 +1821 +2014 +16794 +19005 +12917 +8470 +17343 +1518 +7529 +6587 +11100 +1760 +16159 +6964 +19509 +8222 +11116 +15229 +13559 +6183 +709 +2098 +15239 +6169 +19667 +17363 +13213 +3144 +10767 +11387 +5134 +2003 +12185 +19682 +9113 +4577 +19384 +8926 +537 +11326 +11783 +16912 +4178 +16291 +10344 +17866 +5561 +2746 +10562 +11578 +5294 +14800 +19925 +12412 +15835 +11577 +9481 +14515 +17350 +14493 +7078 +6986 +18146 +9531 +16411 +5954 +2511 +5791 +11662 +13463 +14537 +6179 +2025 +4119 +6428 +7919 +12301 +741 +17003 +11520 +2322 +18545 +2816 +5994 +6105 +16107 +1073 +14622 +13342 +18050 +10078 +17345 +17554 +1543 +18 +7660 +5438 +9943 +3594 +3703 +5395 +11370 +16581 +15418 +3400 +10412 +14131 +13197 +13665 +12372 +18872 +12839 +2460 +16996 +12585 +13902 +15263 +9686 +16563 +1457 +13276 +947 +3589 +1135 +18197 +11713 +12163 +2204 +2919 +9626 +16423 +15582 +15949 +4205 +17938 +17413 +19576 +4993 +5283 +14579 +5309 +14546 +15726 +2849 +2142 +11410 +13810 +9479 +3820 +866 +9978 +16784 +11671 +6837 +16320 +11812 +12184 +8153 +6873 +7419 +7101 +3344 +19846 +9263 +1478 +10669 +12194 +4501 +10924 +7264 +13394 +18353 +1956 +1277 +12092 +12460 +5232 +19324 +2119 +16437 +9390 +11362 +7475 +2597 +15271 +10529 +6995 +19228 +4045 +4951 +8139 +4466 +18481 +4561 +16783 +10099 +6860 +17063 +927 +2033 +12648 +17781 +2898 +1320 +9871 +6326 +17469 +10601 +14705 +9870 +19192 +7219 +8847 +2792 +15438 +6422 +12590 +11420 +2627 +6361 +18901 +19204 +16903 +15181 +11050 +18231 +2536 +1986 +9228 +19402 +2165 +6001 +7311 +4519 +11158 +16208 +13691 +1942 +7375 +6005 +19039 +12972 +16462 +12515 +9276 +6880 +11200 +9931 +5623 +2862 +9716 +50 +9944 +8391 +12853 +11878 +7254 +15717 +14919 +19413 +7322 +4016 +2527 +16907 +2524 +5135 +17276 +10647 +13279 +10015 +9776 +12604 +3113 +16471 +14999 +11702 +19608 +10787 +8154 +5807 +13462 +604 +8939 +6273 +8979 +12619 +10386 +13069 +15727 +11689 +4536 +16075 +13938 +13580 +15599 +18962 +10735 +11802 +10383 +1399 +4410 +3535 +17480 +14985 +2814 +3123 +2724 +6224 +18865 +8240 +17731 +19163 +11893 +3245 +12224 +10103 +2013 +3435 +5017 +19932 +18568 +2339 +13 +3243 +17153 +10233 +4851 +2574 +17494 +10453 +8242 +9960 +372 +3410 +11084 +5357 +9627 +3896 +12304 +15424 +4673 +3841 +3832 +17147 +8123 +13614 +19935 +18746 +8343 +2887 +11414 +2347 +7245 +589 +13837 +5735 +18374 +5222 +2040 +10839 +15184 +16493 +1697 +8528 +3958 +15919 +14802 +14478 +12316 +47 +3814 +15941 +17556 +5025 +6505 +2599 +4807 +16573 +9026 +4753 +13330 +17961 +6951 +4176 +18581 +6280 +482 +7481 +847 +5703 +6686 +12430 +9343 +15955 +14406 +1063 +5293 +1944 +10734 +14286 +17833 +9802 +6627 +4307 +11310 +7206 +4848 +13644 +17175 +11330 +4675 +8977 +15247 +14724 +535 +16143 +3270 +4548 +6938 +6677 +9005 +7020 +9088 +15010 +17070 +704 +119 +12456 +15356 +13788 +9116 +7416 +237 +15198 +59 +13761 +10205 +9325 +10372 +1454 +12134 +17095 +13936 +11049 +502 +4606 +6182 +3863 +15307 +4583 +11554 +15812 +14138 +3258 +6628 +2885 +16888 +641 +11710 +5554 +14854 +9242 +16270 +11171 +19856 +18552 +15074 +17754 +2806 +7711 +4314 +19993 +3468 +12255 +6787 +15711 +7231 +4434 +10023 +9285 +5031 +19987 +13934 +12706 +5672 +9465 +7875 +10303 +19459 +4489 +6807 +6819 +11240 +5921 +12952 +3370 +9733 +10462 +12708 +4175 +5746 +5906 +4485 +10783 +12094 +9690 +18895 +10977 +880 +15847 +8134 +7030 +2154 +18109 +13728 +5571 +11363 +13663 +4131 +8656 +10532 +14433 +17810 +15439 +17251 +4456 +14559 +17505 +1586 +8790 +12115 +3177 +6671 +670 +8561 +17786 +18107 +16930 +14761 +1432 +5153 +6021 +111 +14955 +4624 +16076 +14237 +9844 +1270 +17917 +17015 +15227 +19331 +18072 +7003 +1960 +10914 +6799 +15138 +15068 +13651 +19053 +8900 +16740 +12291 +2503 +5685 +6639 +8718 +7792 +8309 +12079 +45 +84 +13028 +19830 +17453 +10848 +18701 +3321 +2212 +8817 +19290 +19602 +3811 +13789 +144 +7167 +7729 +2717 +6197 +13454 +13704 +14757 +17324 +15910 +9469 +14907 +4555 +708 +1123 +7625 +13782 +13126 +17596 +13957 +9220 +6709 +8019 +11503 +2173 +4388 +16776 +7399 +18430 +6831 +4496 +642 +7723 +5530 +19442 +3778 +2952 +11253 +8700 +5520 +4446 +12978 +1215 +7685 +9872 +15951 +3934 +9149 +1390 +2471 +12160 +14399 +2434 +18415 +1218 +32 +7982 +6812 +4124 +16855 +18941 +2465 +2734 +16235 +6157 +11791 +2737 +4717 +6371 +13109 +7912 +11730 +3555 +16654 +6456 +15368 +7580 +8096 +10407 +14311 +10043 +14424 +369 +12222 +930 +11154 +10068 +4544 +5456 +19536 +4857 +17200 +8804 +13532 +8733 +422 +1506 +18343 +14719 +14153 +3048 +10267 +14506 +5029 +19942 +5898 +2382 +3445 +9501 +18362 +15809 +12221 +17889 +1979 +2217 +16234 +5817 +10847 +16634 +18902 +1743 +4247 +8945 +19623 +10002 +4438 +12048 +6623 +9521 +13864 +17763 +9245 +17975 +11607 +1909 +17851 +10919 +18575 +9464 +10642 +6247 +2061 +18940 +6649 +2106 +14928 +9727 +5719 +8118 +11365 +19383 +4681 +1252 +5370 +9341 +932 +19183 +2420 +11824 +15570 +3993 +15431 +12494 +6548 +16249 +9922 +8387 +17677 +1629 +11720 +3636 +16336 +14087 +8133 +3755 +960 +3946 +16751 +8827 +4885 +11718 +17113 +2463 +17058 +2197 +11153 +17542 +13968 +1692 +9510 +1598 +17488 +19609 +3440 +173 +1849 +6383 +13880 +5226 +8374 +7870 +4086 +12667 +7468 +6736 +10201 +2125 +2589 +6608 +5270 +7683 +3717 +14243 +10644 +11638 +1189 +8767 +19540 +9812 +3980 +13301 +13319 +16386 +14660 +6190 +19137 +18090 +6845 +9849 +7178 +9816 +308 +9653 +11880 +4949 +1903 +7656 +2818 +15129 +13426 +14721 +12555 +7133 +9304 +1140 +5193 +17641 +19302 +2755 +9232 +2290 +12070 +14373 +18000 +6573 +1429 +18199 +12099 +9266 +19799 +19842 +14630 +15890 +5400 +1168 +1334 +678 +6820 +19767 +9867 +3627 +15482 +4219 +18098 +11759 +6569 +1522 +5971 +14975 +19122 +3102 +10832 +14152 +15486 +11430 +14481 +10335 +3613 +19350 +4041 +3053 +8884 +8173 +14611 +5180 +9204 +3476 +18133 +7054 +15400 +17218 +2182 +5252 +13778 +10306 +8472 +17474 +13635 +15264 +8964 +19173 +13839 +5596 +12642 +2008 +6478 +4278 +10186 +6869 +984 +4803 +3481 +18944 +14282 +13615 +15811 +19363 +11136 +7189 +15192 +1759 +3303 +7237 +13518 +11633 +14817 +14946 +16346 +5785 +4870 +8768 +4729 +14672 +13143 +12444 +7398 +14648 +16298 +8079 +14285 +7397 +14078 +13859 +4581 +14442 +16714 +9176 +8370 +188 +12186 +11185 +2192 +556 +6540 +17373 +7988 +7629 +8772 +15852 +1841 +7050 +3644 +1975 +16088 +10223 +3437 +5406 +398 +9409 +8221 +11022 +4599 +17923 +19212 +8525 +13827 +13178 +13063 +13568 +597 +8534 +12787 +9439 +12550 +17022 +7335 +4231 +14528 +17725 +4976 +14923 +15675 +12650 +747 +13745 +4043 +10501 +10969 +4941 +4153 +2195 +12187 +11124 +447 +15401 +18301 +10561 +6296 +1363 +3414 +12037 +4513 +9714 +4258 +4202 +1567 +9251 +3195 +2028 +13727 +10188 +17434 +5451 +393 +10472 +18208 +5378 +3288 +15950 +3389 +16984 +10052 +7082 +1509 +16150 +11193 +3482 +18241 +15206 +12694 +8589 +19453 +14117 +8083 +18296 +15671 +903 +16727 +14083 +19159 +10439 +493 +5493 +17086 +15611 +1575 +16574 +11052 +2315 +19831 +1676 +14627 +19279 +9199 +7674 +5230 +15390 +15977 +19765 +11356 +12465 +18800 +5462 +2300 +8322 +4146 +16519 +16998 +4393 +7958 +3184 +13159 +2890 +1996 +14181 +637 +13125 +13038 +3623 +3067 +11766 +12208 +5955 +12813 +16951 +8522 +3119 +15340 +19563 +19939 +4996 +19335 +1037 +18714 +17876 +18389 +13231 +15917 +7782 +14898 +18670 +11630 +17753 +8603 +3584 +10272 +18520 +19060 +15779 +6553 +2105 +3022 +6160 +11143 +15697 +13675 +220 +13874 +6529 +13783 +8706 +1262 +10317 +12012 +12435 +18774 +19300 +1419 +11201 +16096 +12869 +4769 +14310 +1364 +16991 +6117 +2631 +6040 +3103 +6942 +11453 +7901 +6972 +14364 +12145 +417 +9965 +12878 +2626 +17576 +19338 +15036 +13574 +12971 +12247 +13085 +2336 +19684 +5237 +5187 +11919 +562 +13007 +16523 +12939 +5250 +5588 +16600 +10405 +9663 +12558 +19723 +16362 +8135 +19763 +9391 +2427 +7476 +2029 +18293 +15895 +2385 +1489 +636 +981 +1156 +13282 +15115 +9598 +1213 +16544 +1296 +15737 +2784 +5932 +11104 +13888 +2530 +9706 +11081 +3524 +8956 +13166 +18557 +6065 +163 +6033 +516 +18459 +16866 +11518 +5428 +478 +11492 +10280 +7532 +7002 +14327 +3220 +14828 +7036 +16393 +13768 +2930 +1117 +6726 +2318 +13211 +4689 +3342 +10203 +12398 +16526 +8782 +4418 +8237 +3169 +19441 +10112 +18812 +13734 +4102 +17219 +5543 +1906 +14728 +19262 +15429 +7648 +7893 +17037 +4627 +16891 +11123 +5327 +6657 +2501 +1812 +13229 +3099 +3375 +5468 +3815 +4459 +860 +11151 +4140 +15652 +11792 +7192 +1686 +19348 +13925 +19637 +7427 +18068 +2010 +13216 +10431 +17501 +17030 +9687 +18018 +13730 +19817 +18679 +8473 +10827 +341 +4169 +1846 +9755 +14042 +1180 +13438 +7194 +15654 +9099 +15454 +12018 +19197 +1755 +4150 +15169 +10857 +6352 +7006 +10408 +13355 +19450 +15119 +7004 +257 +10915 +6684 +8840 +18930 +4641 +10877 +229 +7166 +9896 +5905 +7545 +344 +15920 +9152 +246 +9058 +19520 +4300 +4792 +19691 +3504 +817 +8737 +12905 +2415 +6907 +8860 +5027 +8024 +3888 +16163 +11647 +14006 +12023 +4877 +7644 +1563 +11462 +18219 +12019 +1989 +8497 +6763 +3996 +181 +11278 +11703 +554 +6004 +16785 +5024 +17144 +11675 +2857 +2485 +13659 +17741 +11130 +12704 +19747 +6166 +778 +6670 +19756 +8796 +19227 +385 +10731 +11961 +4047 +15990 +19669 +12521 +16944 +16474 +13432 +10829 +6960 +19887 +2559 +18752 +8361 +13833 +17824 +13186 +8008 +10746 +9302 +12298 +19491 +17194 +94 +2726 +9562 +17936 +14229 +17211 +1264 +2388 +7536 +9309 +1090 +4704 +2987 +16669 +1069 +5974 +666 +4110 +12960 +7011 +3430 +3658 +7957 +13867 +2305 +6814 +9642 +18184 +6066 +10263 +6331 +6583 +11401 +18710 +7761 +9120 +18732 +7057 +15086 +13461 +9607 +620 +357 +10244 +16750 +12518 +14047 +13311 +1637 +8493 +9688 +3472 +12299 +5998 +13924 +6504 +7669 +1921 +9307 +3126 +6080 +18350 +17884 +19096 +19377 +1765 +3605 +16444 +6057 +19150 +7336 +745 +12446 +18967 +17715 +954 +6207 +12627 +3154 +11384 +14172 +15881 +10026 +2786 +18142 +18848 +14487 +19946 +14489 +12259 +1326 +10541 +1739 +7035 +13278 +10706 +1226 +352 +7017 +19211 +1966 +5756 +19780 +18829 +34 +11245 +2462 +4457 +14180 +2692 +14910 +13246 +6710 +10638 +7946 +7282 +8727 +1368 +3519 +5185 +6794 +8042 +5645 +5808 +4835 +17102 +3921 +11209 +12136 +10628 +3576 +4998 +7242 +18183 +4817 +1796 +6850 +16380 +8288 +16177 +12374 +2774 +12953 +19135 +17368 +12202 +2671 +12030 +3502 +13272 +18318 +2947 +9429 +17981 +13291 +16647 +9353 +13506 +1345 +1606 +17865 +15331 +3223 +9213 +2329 +6693 +15916 +2748 +1597 +12141 +14140 +10183 +17864 +15681 +10661 +17526 +3179 +2682 +9129 +17455 +9142 +7890 +908 +4428 +1257 +6910 +4718 +19099 +15214 +168 +15250 +19414 +6957 +7013 +3617 +15080 +2637 +19908 +7213 +2778 +8424 +6742 +13447 +1388 +8942 +6449 +13283 +6520 +14973 +1632 +9919 +3172 +10166 +15706 +1954 +12442 +6048 +265 +884 +19234 +2575 +19868 +18708 +1191 +3020 +14077 +15453 +1936 +2761 +13577 +17654 +9508 +15423 +12865 +3808 +2055 +13652 +5522 +8046 +17315 +11752 +2304 +4058 +7626 +9317 +10968 +6605 +18609 +12029 +9514 +12933 +6311 +6106 +4719 +10381 +6532 +12583 +14195 +11685 +4255 +15557 +6704 +15840 +15208 +6497 +8632 +3696 +7840 +12973 +18856 +9969 +19613 +18563 +6743 +13422 +6575 +12637 +3962 +14827 +1112 +19268 +5240 +5901 +13009 +5481 +15268 +13265 +16451 +18319 +1837 +8355 +8598 +4207 +983 +12961 +13919 +14456 +1836 +1239 +3682 +2158 +15971 +12416 +17843 +12431 +5091 +2773 +11993 +10113 +10676 +17265 +115 +11344 +13027 +6813 +17885 +15398 +5191 +16829 +7964 +13565 +2469 +15284 +15334 +15636 +16577 +17943 +13522 +13595 +15555 +2598 +17872 +10530 +17790 +5792 +4328 +19037 +4015 +14635 +1783 +3928 +17277 +10138 +10764 +21 +7171 +5579 +2956 +15658 +4832 +9159 +12366 +2086 +1677 +1346 +13813 +14918 +19250 +3229 +5583 +15552 +17391 +19445 +19064 +19222 +8963 +10540 +14799 +9 +11515 +12578 +13606 +8198 +18794 +4379 +3132 +19960 +18979 +631 +5846 +12072 +15204 +5022 +7193 +12746 +8665 +4090 +8310 +4594 +11466 +15411 +13457 +5418 +16502 +10360 +13018 +1912 +2683 +19853 +2078 +3221 +17284 +6288 +14554 +7948 +16763 +3317 +3633 +3568 +6613 +16070 +17081 +15908 +12464 +8082 +13060 +2555 +3439 +8477 +12271 +5038 +19860 +4829 +11706 +7998 +8384 +1595 +1805 +1640 +474 +14984 +3614 +10814 +14329 +419 +4940 +10881 +9506 +13006 +10918 +8530 +17562 +5725 +17235 +1167 +17756 +462 +11525 +8492 +2843 +6761 +11361 +6935 +11226 +8856 +16721 +17026 +57 +5769 +14925 +9033 +10129 +2837 +8439 +1616 +6434 +11611 +14205 +6075 +11770 +5291 +2326 +7364 +586 +12935 +14640 +9955 +335 +18730 +1813 +11972 +4690 +672 +5793 +13220 +5178 +13492 +19396 +15145 +8657 +11858 +19994 +19162 +1033 +7147 +2330 +19757 +572 +643 +384 +9925 +1125 +16239 +1468 +14128 +11184 +17656 +19701 +15014 +2804 +19155 +18053 +16216 +4912 +17608 +12940 +9707 +16399 +9962 +1229 +2002 +9092 +4211 +14353 +15441 +18468 +9370 +16688 +987 +9588 +8286 +7260 +40 +2980 +12044 +3622 +16853 +6776 +1292 +11037 +14858 +11338 +3271 +6792 +5869 +17176 +4935 +9850 +3133 +530 +5284 +11481 +15789 +14037 +1792 +10600 +16448 +16402 +7271 +10259 +18759 +8559 +16204 +16052 +3246 +12904 +15531 +10970 +4450 +483 +12566 +15607 +588 +15992 +5443 +1447 +12752 +2411 +166 +19825 +16716 +6513 +18826 +12041 +10452 +16902 +829 +4188 +4493 +3208 +17099 +18648 +13847 +8111 +18607 +13743 +18881 +3827 +6130 +4139 +4965 +6177 +10051 +12253 +13041 +9234 +18908 +8614 +1776 +14967 +18358 +3366 +11589 +413 +18726 +18555 +17591 +2060 +8879 +3791 +18548 +2665 +7895 +15282 +19893 +2338 +14473 +11013 +207 +13584 +19911 +18401 +16144 +13453 +5700 +10181 +13519 +4557 +16640 +10933 +8755 +6884 +10786 +8921 +8348 +8360 +3935 +7072 +11688 +18657 +16435 +16612 +9620 +15485 +12302 +1287 +4750 +17816 +6462 +19313 +13429 +10803 +19658 +1811 +2333 +2788 +12509 +2391 +13374 +756 +10119 +526 +11502 +4846 +16478 +6897 +18228 +14718 +9334 +1484 +16094 +14722 +17019 +15695 +18441 +11231 +12369 +6240 +9002 +3631 +615 +7153 +4790 +10958 +17839 +10995 +9926 +6795 +12125 +1982 +4400 +9387 +1529 +5381 +18595 +17504 +2169 +4636 +2246 +18988 +17023 +11444 +16361 +13146 +17374 +14877 +18345 +300 +18462 +12824 +564 +134 +17523 +1134 +2452 +1038 +18878 +5092 +18926 +451 +11879 +15764 +13985 +5625 +8596 +12546 +12755 +10421 +3777 +10152 +11441 +19910 +12031 +16841 +7655 +10693 +5392 +18506 +12563 +18995 +15619 +11364 +14263 +13044 +12997 +13346 +13160 +11672 +17259 +2736 +319 +9151 +10928 +1832 +7854 +15517 +3459 +2878 +17775 +19969 +6304 +18289 +37 +7244 +13589 +3371 +8941 +13784 +7754 +5591 +89 +18899 +16011 +1249 +7622 +8592 +16098 +7570 +12850 +7579 +13637 +6854 +11655 +14479 +3180 +18981 +19456 +4523 +10036 +504 +8811 +12882 +14072 +4101 +602 +1818 +8398 +2965 +4771 +14349 +619 +3381 +9045 +17723 +11003 +551 +11987 +6641 +10576 +5382 +4609 +2232 +7400 +14606 +17782 +1882 +11952 +12883 +1013 +1045 +2239 +4588 +12565 +9769 +15195 +15723 +19314 +14132 +8295 +942 +5104 +16908 +10156 +4667 +13361 +13593 +17199 +17751 +15670 +16848 +4233 +7844 +13082 +18753 +16815 +5325 +9544 +18949 +15867 +18450 +1523 +13507 +3925 +9375 +8022 +16922 +19002 +18483 +6997 +9114 +869 +6463 +18983 +1099 +7611 +15944 +19025 +11257 +19088 +16057 +4747 +3210 +12362 +8623 +553 +7619 +8730 +13142 +12287 +17628 +5674 +2143 +2667 +19194 +14067 +14994 +5916 +6983 +11142 +18968 +9128 +234 +16008 +13918 +14901 +8770 +7161 +7706 +8783 +4742 +7058 +16184 +14926 +1098 +2953 +13416 +9591 +13168 +6290 +2187 +13489 +12669 +3316 +1603 +14778 +11423 +17530 +11102 +13092 +10690 +11992 +15793 +7483 +3486 +14589 +17624 +3801 +13150 +6451 +9171 +17302 +4744 +12516 +7190 +17853 +17486 +15132 +575 +16293 +10909 +19815 +7396 +16384 +18258 +4260 +9017 +1057 +418 +15588 +18699 +7408 +1669 +9855 +17928 +4332 +19007 +7179 +7097 +10636 +8254 +15571 +17880 +17858 +2467 +4824 +8630 +5507 +7748 +13207 +7708 +9601 +13215 +9756 +16026 +7734 +12007 +3985 +3975 +24 +8499 +19781 +16997 +19931 +7498 +18003 +898 +3774 +5267 +19904 +1141 +3236 +3997 +340 +8277 +10219 +19621 +9143 +17010 +11669 +12691 +8169 +9079 +14583 +14459 +12061 +11205 +14789 +3757 +4913 +4123 +1307 +687 +8143 +5783 +6087 +3116 +6893 +18700 +1126 +18939 +14849 +19168 +13694 +3354 +4732 +7220 +9715 +2729 +9476 +7883 +3283 +18448 +7298 +19525 +6913 +10908 +17048 +9894 +3151 +1736 +12383 +4842 +18693 +11914 +7090 +7865 +5521 +4669 +12482 +19952 +8529 +15462 +2518 +327 +10523 +1055 +13857 +19428 +11179 +2896 +15066 +11934 +935 +4289 +11899 +568 +1028 +3057 +1623 +12628 +9328 +9278 +19555 +8131 +10592 +16847 +11519 +4304 +18582 +5299 +2381 +2830 +3465 +14862 +2112 +3199 +15457 +9221 +15445 +6534 +10946 +1244 +4061 +14787 +19829 +10695 +19455 +5951 +3522 +14155 +18282 +4454 +13337 +16782 +9130 +8003 +1143 +6838 +6755 +16851 +6626 +15090 +5895 +18185 +9123 +19638 +11475 +1588 +5718 +8144 +12244 +7806 +9909 +13328 +12618 +16186 +14803 +3571 +1175 +6242 +13141 +4981 +16953 +15739 +15888 +15825 +8584 +12073 +12181 +6668 +13546 +17247 +7490 +5641 +18695 +17270 +19130 +15593 +9991 +8480 +19426 +17306 +3868 +2172 +4648 +16842 +7443 +12297 +16167 +16153 +8206 +17653 +16223 +18306 +8011 +13258 +8749 +11413 +17355 +18821 +2392 +7736 +1065 +3762 +11127 +13274 +14848 +7261 +11626 +14264 +10701 +9139 +6803 +7406 +16136 +15216 +18770 +8147 +10579 +5432 +1354 +10479 +8928 +13710 +2474 +11118 +13383 +15609 +5990 +19489 +15815 +8567 +16068 +2522 +3666 +11847 +16138 +7991 +12520 +15287 +18644 +4712 +9022 +14031 +1952 +10045 +2917 +13650 +2881 +19104 +3556 +5163 +6530 +16196 +3566 +9300 +5245 +2996 +18038 +2647 +3943 +13251 +13680 +17217 +1077 +6341 +15865 +1918 +437 +4834 +2572 +7862 +5711 +8408 +8924 +3418 +19594 +13314 +16731 +18513 +3075 +19643 +11476 +18781 +11249 +16940 +13425 +8467 +10608 +5912 +4692 +6496 +17159 +18817 +9774 +17707 +10887 +2646 +3101 +15565 +13814 +7910 +10234 +7672 +18593 +16125 +3872 +4688 +13502 +15285 +10258 +15956 +16674 +17513 +16734 +18127 +283 +16584 +3550 +2235 +19977 +4930 +2725 +2122 +13871 +5420 +13472 +5896 +17210 +9381 +2334 +15279 +7181 +3162 +10175 +2835 +6150 +13023 +1663 +5737 +5310 +2815 +7940 +12773 +16395 +11629 +15496 +2439 +17094 +5132 +3999 +17160 +10801 +3114 +4724 +1838 +4275 +19750 +10670 +7456 +12380 +15744 +14920 +4978 +5790 +9529 +8616 +12128 +4683 +6244 +1403 +3531 +360 +13158 +11178 +1997 +14972 +3992 +1034 +10672 +5935 +5578 +19888 +6243 +6653 +2371 +9222 +18521 +2413 +16275 +785 +3406 +16603 +9376 +4375 +12899 +17609 +8678 +13468 +16027 +14774 +14184 +3171 +2946 +15157 +6421 +18315 +18057 +13759 +14435 +17583 +11371 +4966 +15100 +1712 +8162 +13435 +7357 +11300 +17969 +16426 +13370 +6748 +10065 +6604 +5001 +9209 +3874 +14068 +5079 +14734 +434 +6630 +6747 +1394 +1085 +4425 +3478 +2053 +1367 +1025 +1560 +7981 +7309 +993 +15315 +9454 +7358 +11342 +7099 +7703 +12000 +4024 +14266 +9311 +2993 +15389 +19584 +18740 +8699 +11727 +6541 +11947 +3186 +7348 +13898 +693 +3189 +3074 +2425 +18658 +3065 +2567 +12010 +16450 +13757 +13022 +14769 +14444 +15466 +17832 +8016 +7075 +9185 +4847 +16771 +10268 +9578 +6919 +19624 +13031 +13380 +5124 +13554 +15078 +19046 +470 +3930 +655 +4019 +8440 +19527 +194 +9411 +9840 +7478 +6682 +4887 +4617 +17055 +759 +16034 +14466 +684 +16571 +2455 +16807 +6394 +5985 +15965 +10079 +2847 +13081 +7466 +15162 +17571 +13721 +6698 +11339 +14030 +14447 +10426 +13351 +8454 +18388 +4572 +13310 +13512 +6676 +2730 +18079 +15177 +895 +14003 +16172 +3572 +14307 +7807 +8842 +3323 +6491 +8751 +18445 +7376 +1592 +7774 +18987 +9532 +12636 +8777 +7540 +4054 +17548 +16481 +19598 +19357 +6078 +9000 +11767 +190 +2819 +17894 +3474 +4462 +13444 +8448 +15973 +10837 +13252 +12071 +15238 +5285 +7605 +7234 +7421 +8906 +11564 +1479 +14953 +19400 +5465 +15855 +2019 +16192 +10889 +8776 +15519 +6817 +345 +16653 +13688 +2403 +14018 +16537 +19797 +19997 +4634 +14400 +8655 +9483 +18683 +13841 +9504 +16741 +5014 +2577 +4225 +9013 +14064 +16518 +10623 +387 +17050 +6189 +999 +12600 +13726 +5888 +6007 +11479 +16718 +12795 +19897 +16182 +18458 +13772 +7673 +19890 +7047 +5618 +5959 +7185 +15330 +2138 +8591 +2605 +7691 +18431 +9186 +17634 +15342 +11687 +919 +8615 +10055 +2690 +11161 +2548 +10916 +8538 +5012 +1559 +5083 +8487 +2702 +18625 +9752 +6730 +11701 +10378 +7582 +5144 +16250 +17020 +18819 +17867 +2077 +10760 +6501 +16111 +5174 +1236 +8417 +15243 +15428 +4185 +17971 +11150 +2270 +4076 +10418 +9616 +14304 +9739 +14354 +11173 +9077 +2553 +17544 +16796 +7414 +5750 +16219 +11777 +3621 +12054 +6991 +4516 +3866 +13047 +17679 +2416 +13399 +337 +14655 +18382 +16054 +18064 +17422 +17215 +17498 +1750 +7126 +3127 +18565 +6762 +18960 +10966 +6662 +6082 +3070 +15563 +12371 +7905 +4700 +11434 +3014 +18514 +12815 +17114 +14966 +3384 +2723 +12173 +291 +1462 +3643 +17993 +1139 +7562 +8067 +8759 +8205 +14015 +3287 +1738 +10139 +1963 +15196 +7513 +5494 +15932 +17383 +6751 +7542 +5886 +15038 +3784 +2253 +7851 +2779 +12151 +7157 +19881 +11373 +17650 +174 +4010 +5259 +18803 +11562 +7188 +11876 +17734 +6949 +10287 +14157 +18143 +8517 +1622 +16646 +6088 +12780 +5103 +3603 +13777 +11604 +16943 +2542 +4967 +1369 +6433 +11825 +12008 +6494 +7531 +15518 +11926 +1104 +18507 +2332 +2257 +4511 +1230 +7978 +958 +10757 +4564 +15232 +5841 +6680 +10704 +17564 +15669 +5712 +2926 +3964 +3324 +15679 +15296 +5331 +3663 +1717 +17708 +5943 +11395 +16378 +5660 +4341 +1166 +17091 +1582 +6016 +216 +18784 +4639 +17240 +18603 +3597 +6350 +17348 +12607 +10364 +3058 +10709 +7744 +2871 +6905 +4195 +17230 +6343 +10021 +16126 +15597 +17983 +5405 +16319 +885 +15877 +14568 +3527 +7602 +13623 +1531 +14019 +12641 +157 +4128 +2733 +12364 +18822 +1283 +15255 +8284 +11428 +4062 +15606 +7132 +16755 +2863 +16252 +17191 +10100 +10459 +13758 +13200 +3710 +7159 +13800 +15040 +5271 +6711 +13654 +9837 +5173 +19220 +10368 +11398 +6330 +4006 +12476 +6586 +5797 +4242 +15703 +1665 +8427 +14178 +5244 +1968 +5818 +12107 +15505 +13285 +11529 +8822 +6603 +16260 +19339 +7627 +5654 +14824 +4389 +17580 +12135 +3680 +889 +10104 +15702 +13504 +1214 +7198 +19579 +15374 +12365 +12999 +11772 +14776 +9617 +6293 +9577 +13967 +18943 +16729 +7577 +14436 +19298 +6622 +10665 +8431 +19395 +19984 +10662 +14632 +16580 +16368 +12239 +6318 +13228 +8861 +7813 +6366 +1645 +13066 +2491 +11844 +15839 +12306 +14755 +19548 +18460 +2393 +5422 +364 +17185 +7575 +16636 +4921 +11833 +3376 +3818 +3752 +5026 +14107 +18861 +19157 +9505 +271 +17577 +2479 +9984 +2376 +15784 +14631 +6083 +957 +3039 +15020 +18999 +1508 +6772 +10931 +17855 +6992 +19287 +12666 +14170 +1740 +12923 +6858 +2309 +9440 +18070 +2886 +10054 +15402 +4626 +4196 +9897 +5702 +13410 +8233 +12649 +590 +19261 +238 +1938 +12592 +12402 +11487 +1732 +3452 +1752 +6939 +9524 +16769 +9060 +1911 +5708 +2742 +697 +13692 +17972 +10130 +12765 +6963 +1276 +7169 +9722 +3860 +9525 +6254 +5355 +8312 +12798 +8280 +1371 +18400 +15762 +1407 +4048 +9087 +15999 +2369 +6100 +7596 +1131 +19386 +3638 +1032 +12675 +18835 +812 +6274 +17032 +386 +2167 +3678 +10792 +13541 +10961 +7152 +18589 +11442 +7770 +2065 +9704 +5282 +6167 +16058 +5089 +18413 +6249 +8785 +1896 +171 +5035 +12241 +1190 +17145 +7144 +17135 +8142 +9241 +4830 +18909 +2636 +14302 +8987 +18964 +17752 +12531 +19686 +16553 +8709 +14063 +12538 +7511 +10082 +2968 +1893 +12781 +13845 +2048 +10552 +6937 +9259 +15372 +4901 +10380 +240 +19041 +17426 +9757 +244 +13914 +2372 +3036 +6899 +4460 +14836 +11513 +98 +2310 +12522 +7953 +531 +19399 +19557 +6452 +18372 +2159 +15351 +4288 +2639 +8950 +7120 +3454 +14580 +19091 +13984 +8855 +8837 +5926 +13956 +9366 +15757 +12812 +10755 +14921 +5929 +14512 +15133 +7920 +6593 +5687 +8889 +15860 +2928 +19630 +6427 +4721 +16681 +11324 +19844 +17817 +10370 +1708 +16101 +347 +10038 +8229 +315 +16837 +17819 +15710 +15957 +1594 +4849 +988 +14540 +14061 +466 +10409 +1590 +13376 +14016 +5551 +2128 +7123 +5960 +2971 +2894 +2673 +113 +18841 +19657 +943 +17769 +10736 +12748 +3823 +10505 +1441 +6012 +5093 +5732 +8689 +4642 +7535 +11738 +12190 +2754 +15430 +390 +6266 +12967 +3068 +18040 +14457 +19114 +17612 +1231 +14044 +17221 +6801 +6131 +763 +9072 +2323 +18702 +11260 +16132 +4616 +3505 +14452 +10756 +191 +8073 +479 +13987 +14278 +10273 +9516 +13779 +11450 +10853 +6560 +10506 +19086 +18039 +9299 +11353 +5320 +16870 +14867 +167 +4794 +17329 +14958 +10076 +17110 +6788 +18190 +16720 +8224 +795 +4646 +6493 +1161 +5263 +10666 +748 +10573 +9966 +6233 +17645 +6417 +16039 +10192 +17280 +6430 +18198 +8044 +4755 +7504 +2203 +6848 +16849 +7324 +7591 +5536 +2021 +18495 +17862 +3744 +11217 +7378 +9632 +10720 +11790 +7906 +8422 +16003 +9140 +11526 +7747 +1148 +17188 +15170 +14893 +2288 +1242 +1044 +15730 +2828 +17845 +15822 +8368 +18914 +3357 +16524 +3520 +7310 +8211 +9024 +6767 +9319 +7926 +7467 +8259 +2377 +2483 +15244 +4914 +17406 +8216 +17683 +19085 +1387 +6646 +14486 +10510 +18046 +11070 +13360 +16754 +15081 +9737 +3911 +4777 +10278 +15289 +14511 +17201 +7858 +18664 +12469 +18097 +6663 +10134 +3399 +3912 +10869 +3442 +17538 +17850 +17631 +2782 +326 +16022 +18134 +9553 +16702 +7882 +7788 +7816 +1877 +1951 +1047 +5321 +11269 +1555 +11555 +10739 +8981 +16342 +13822 +979 +17702 +9247 +921 +18937 +16591 +1535 +13921 +4600 +12206 +12042 +19793 +10602 +19418 +7187 +9416 +4614 +16415 +14096 +13803 +226 +16737 +19230 +13198 +18116 +10277 +14249 +17439 +19986 +13236 +17136 +12900 +5341 +7985 +2679 +5627 +9312 +10199 +16861 +2942 +1852 +10466 +7838 +11922 +8420 +15176 +2986 +17555 +5978 +7507 +4112 +1992 +1988 +6446 +10154 +6176 +16089 +9104 +11211 +18823 +18351 +8555 +11458 +10327 +15240 +3768 +1397 +16753 +1435 +19820 +2920 +17298 +14659 +9014 +16372 +7697 +17405 +11495 +14499 +7238 +2422 +3904 +13281 +17674 +10891 +2231 +16960 +7274 +12581 +14323 +16416 +15765 +724 +17605 +2601 +16006 +4423 +17675 +16772 +15137 +12001 +12449 +14088 +2348 +19239 +19610 +12057 +18615 +11643 +5915 +19006 +9876 +17123 +18082 +17785 +4026 +17663 +17042 +17964 +11624 +5976 +15444 +10059 +15667 +15786 +12511 +11528 +7769 +17282 +12557 +10398 +18996 +2584 +6022 +16775 +14244 +5590 +7363 +875 +6815 +15076 +14959 +557 +2437 +9090 +2020 +19675 +405 +18129 +12453 +18373 +8859 +16864 +17115 +14085 +17182 +11106 +12069 +12909 +7080 +11712 +16142 +217 +7297 +11393 +18678 +14908 +8633 +8695 +10844 +6283 +527 +17049 +8815 +18650 +18884 +13586 +15995 +19789 +4881 +7954 +19487 +12038 +2817 +19863 +9280 +8054 +19464 +13003 +3267 +8071 +16069 +10342 +6306 +11396 +6477 +8864 +5704 +2710 +12199 +19054 +8150 +18916 +598 +1798 +10862 +3564 +2493 +11425 +19355 +43 +2000 +6381 +7100 +11343 +863 +13642 +17638 +268 +13767 +4498 +5229 +3469 +11367 +14847 +4946 +11731 +13604 +11640 +402 +15347 +19050 +10879 +2674 +13005 +15778 +10391 +11884 +3160 +7852 +14628 +18986 +1357 +10049 +15644 +12059 +17044 +10057 +11641 +5467 +19892 +11377 +9988 +4316 +12799 +18538 +1474 +13114 +14950 +5039 +16025 +9250 +10657 +7118 +4222 +19918 +14667 +15052 +16492 +5938 +900 +7900 +7710 +5822 +18875 +8196 +18882 +2880 +1152 +12659 +5037 +10089 +10221 +4065 +13978 +926 +16585 +18992 +12586 +2191 +9141 +528 +19659 +7864 +12705 +19187 +17454 +5459 +794 +2685 +3822 +14642 +12897 +12235 +18761 +7972 +18561 +15638 +3558 +3281 +11239 +12014 +81 +14644 +6095 +7307 +19241 +11026 +19273 +4402 +9027 +17908 +13087 +9145 +5881 +13560 +12142 +15568 +7465 +5444 +16213 +19698 +2972 +6944 +17487 +19328 +2036 +9863 +13626 +18917 +11146 +9070 +19393 +7971 +11890 +5695 +635 +10250 +295 +15996 +17229 +9818 +17134 +15200 +3936 +1634 +12688 +3340 +7352 +18614 +16175 +12743 +12930 +17127 +15322 +11875 +7639 +13703 +17626 +17535 +19031 +6930 +14697 +1825 +16979 +3705 +18768 +8208 +16300 +4000 +11325 +16131 +15139 +12729 +13528 +332 +18635 +16676 +10965 +15507 +7223 +639 +13513 +856 +8973 +9929 +9743 +19521 +19061 +11018 +3185 +11891 +16895 +2198 +19403 +3970 +10820 +9093 +4773 +15782 +10687 +12854 +5884 +11595 +15030 +2711 +5526 +12793 +6369 +6740 +14772 +18938 +3802 +19193 +6108 +4678 +907 +3461 +16201 +5374 +11025 +539 +11369 +2622 +12082 +11433 +9720 +11321 +12455 +2353 +3790 +6902 +449 +7641 +5161 +19164 +14956 +8586 +2591 +16662 +19906 +2380 +12652 +9882 +14462 +779 +8482 +2800 +16037 +15024 +11349 +12814 +9198 +4722 +2446 +1556 +12784 +8673 +16469 +415 +7447 +11195 +10674 +1046 +12767 +18816 +14060 +6056 +4578 +10502 +12510 +2802 +10838 +3637 +1690 +3156 +8481 +1662 +7583 +6152 +1862 +9289 +7438 +7598 +5326 +13903 +16604 +17475 +9371 +3069 +4759 +19035 +16499 +11168 +2194 +10852 +8599 +8552 +4281 +18637 +18012 +375 +15003 +5165 +5574 +16872 +12433 +10637 +5673 +653 +10539 +10521 +8807 +12451 +19728 +10072 +5563 +13953 +8904 +15337 +16685 +19478 +7023 +9759 +616 +15171 +2579 +2117 +10525 +3776 +8050 +12265 +2659 +10955 +4871 +8826 +5215 +8556 +7636 +5354 +9040 +16536 +2478 +4610 +7428 +1310 +14804 +15627 +5729 +13802 +4533 +7916 +17655 +9218 +9878 +8901 +14109 +15473 +18424 +14516 +17854 +922 +9809 +777 +15617 +1328 +6959 +16227 +11101 +16684 +1801 +5166 +3937 +8975 +19858 +5359 +4985 +11760 +5918 +16780 +10560 +15954 +7804 +3155 +6148 +12639 +6777 +120 +15463 +11542 +3639 +1391 +19390 +8181 +7014 +10253 +3250 +15768 +1224 +3635 +17809 +1016 +18230 +8803 +11834 +14932 +9345 +8653 +9225 +17041 +213 +1541 +13991 +16261 +12413 +19838 +8918 +809 +13016 +5260 +5055 +10934 +10983 +19317 +3855 +18375 +13094 +17275 +3345 +582 +8155 +15791 +2259 +9749 +3612 +13735 +1619 +18673 +10429 +7055 +3676 +3787 +11307 +8421 +1385 +5837 +11412 +8363 +9380 +5042 +8496 +10106 +6121 +4167 +18201 +13748 +12307 +109 +6346 +13306 +10480 +12215 +17004 +14747 +13535 +16264 +1172 +14846 +5114 +19359 +11684 +10032 +8835 +12411 +4147 +19345 +2363 +10354 +9495 +9197 +15041 +1943 +19543 +13605 +2240 +7746 +4005 +15943 +2549 +1421 +13992 +15064 +12013 +5570 +18797 +16442 +13196 +6916 +9235 +137 +7200 +5527 +6624 +16927 +13187 +17080 +12312 +5464 +8951 +6445 +9958 +12928 +1917 +14677 +13004 +2429 +4643 +15183 +12254 +19921 +6191 +11831 +13834 +16233 +19879 +4656 +14240 +5820 +13572 +5939 +13868 +4798 +9814 +10744 +4417 +15897 +11917 +12437 +3197 +14604 +646 +17778 +6926 +7007 +18540 +4762 +11729 +6453 +16615 +15672 +8745 +8164 +1053 +10646 +18766 +7477 +14917 +19578 +4236 +6537 +10230 +18156 +10047 +16229 +497 +18798 +9853 +2838 +1259 +8518 +9677 +10997 +18749 +19570 +13151 +13964 +9652 +14081 +2596 +8611 +10570 +10854 +5200 +2944 +951 +12686 +16778 +2324 +17719 +8357 +489 +18662 +18101 +9890 +8036 +1729 +443 +1289 +3196 +12816 +12016 +559 +13561 +5773 +2018 +12529 +19632 +2992 +7877 +8451 +2213 +10654 +1527 +11087 +18384 +9763 +6650 +11809 +1609 +4467 +10204 +1488 +15738 +14701 +15455 +2079 +3456 +11029 +15574 +7377 +9987 +2948 +5175 +14345 +7366 +5213 +7608 +19866 +10247 +3374 +11014 +5775 +8122 +2564 +11404 +10184 +12264 +7048 +18945 +19704 +8325 +18305 +13815 +10132 +6797 +10936 +1702 +10350 +3315 +1039 +19438 +7141 +499 +4415 +17139 +12106 +15573 +13098 +9349 +2740 +6011 +17074 +14704 +13188 +12325 +5268 +128 +3117 +17471 +7712 +19416 +6805 +5840 +18755 +1747 +12198 +2297 +6940 +7928 +3557 +16605 +9100 +3284 +929 +2991 +5171 +9105 +6612 +8201 +17441 +10795 +7211 +9938 +4213 +9610 +15608 +6395 +14637 +18330 +11331 +13876 +9594 +2243 +13102 +12866 +13537 +8418 +15277 +16949 +11213 +19616 +8513 +15023 +14446 +6948 +8498 +10269 +9670 +14458 +6901 +11357 +19957 +10534 +16712 +18348 +1383 +5275 +16251 +333 +2412 +14927 +7276 +1858 +4349 +3275 +648 +5278 +9666 +6384 +7228 +17464 +4764 +16289 +2921 +19010 +7815 +3736 +11913 +19719 +16259 +8890 +18025 +1886 +18121 +4821 +7384 +5276 +11473 +6379 +12858 +13407 +2561 +19861 +7675 +15363 +16265 +10215 +15396 +17059 +14382 +9101 +13831 +17112 +1118 +19648 +12896 +15509 +1621 +18998 +17789 +14808 +2200 +17934 +9253 +16813 +8397 +9695 +17162 +12788 +15149 +4886 +9657 +17540 +5440 +6871 +446 +7966 +3719 +18020 +8594 +19427 +4106 +1655 +11974 +17266 +18550 +5482 +4944 +17868 +8149 +13808 +1459 +677 +4943 +3369 +13600 +12436 +4788 +16414 +13820 +12613 +17079 +9685 +1791 +19696 +11232 +8813 +6279 +13149 +4003 +1331 +4751 +13718 +17326 +10718 +4988 +8720 +13244 +12894 +4469 +19523 +4164 +574 +3262 +3121 +15197 +12488 +14422 +3394 +15618 +12647 +5404 +8148 +18660 +6270 +6769 +12942 +16017 +4344 +14650 +2435 +4241 +18303 +9680 +15286 +13701 +16994 +11131 +7553 +2568 +9968 +6309 +11711 +8818 +14293 +13980 +1157 +6691 +17351 +801 +17557 +1268 +10067 +18697 +15903 +11645 +7544 +7831 +10738 +18610 +19076 +10609 +7455 +12919 +412 +4731 +15175 +9352 +4903 +3856 +12088 +7429 +16924 +5427 +15464 +10176 +11120 +19541 +1626 +17762 +17228 +3076 +17289 +7974 +15069 +17047 +11409 +7799 +2707 +7794 +15153 +16613 +3139 +14179 +12875 +17143 +9761 +12067 +6544 +4249 +6828 +11108 +17891 +13329 +6349 +1526 +14859 +3882 +14876 +13375 +1311 +512 +19351 +1517 +10942 +19710 +12323 +3382 +16148 +9603 +15167 +7444 +1961 +17744 +18475 +6961 +11835 +12011 +13212 +19177 +15521 +16905 +8607 +13854 +905 +4023 +19920 +1113 +665 +888 +16595 +10604 +10613 +6549 +4809 +12713 +6988 +7495 +4539 +1238 +9551 +14471 +13112 +18767 +17729 +10282 +14074 +9360 +12943 +116 +10781 +18042 +8436 +14543 +5234 +5963 +14405 +19971 +1195 +1694 +19717 +16271 +9803 +2931 +3670 +5140 +12918 +9154 +15316 +4203 +5170 +13130 +12889 +18632 +12169 +5109 +4927 +13882 +1892 +9167 +5772 +11017 +7604 +8626 +254 +17357 +519 +11928 +610 +19953 +8246 +1815 +18859 +19000 +11059 +13451 +12572 +7127 +5540 +2675 +12233 +11916 +19785 +5499 +18869 +13170 +14185 +7098 +17890 +7125 +3362 +4796 +12872 +13299 +18437 +1515 +16028 +924 +15969 +13406 +6335 +19083 +19680 +8405 +6974 +14150 +18472 +175 +17704 +6026 +10359 +125 +9949 +323 +16066 +8426 +9705 +17840 +11618 +16405 +17831 +5495 +7925 +5446 +13136 +16856 +501 +5798 +5046 +18804 +2436 +15071 +1789 +3595 +9365 +573 +14080 +6014 +1401 +10190 +7089 +1300 +15946 +5040 +3788 +7285 +10547 +8250 +13270 +19474 +5254 +18304 +3166 +8157 +14246 +1905 +18877 +1826 +14292 +2116 +14290 +14303 +19315 +3500 +1440 +17336 +4125 +5832 +13656 +7446 +1578 +18255 +13466 +2394 +10357 +10322 +16599 +484 +14238 +16871 +576 +19975 +13303 +3587 +11715 +3858 +19108 +13193 +6484 +16913 +13746 +3689 +7785 +11274 +3226 +867 +3521 +9875 +7548 +11683 +2938 +2808 +10639 +5637 +12182 +14113 +8393 +19388 +14595 +2186 +13448 +16746 +4338 +14350 +6545 +15318 +11072 +12553 +189 +5154 +19178 +14990 +7640 +16551 +114 +1718 +17988 +16207 +18663 +8784 +19889 +15104 +14822 +10831 +1235 +3859 +6637 +8056 +15442 +3255 +12219 +1651 +13557 +2588 +674 +4427 +1533 +790 +4406 +19996 +6344 +6631 +2247 +5894 +5984 +9042 +18361 +16707 +1207 +10035 +2604 +9086 +9038 +2093 +9184 +12278 +14256 +475 +18083 +376 +16078 +17109 +16982 +6483 +14280 +18418 +18093 +16337 +12183 +11093 +11437 +16118 +14962 +13543 +11745 +4113 +5099 +13806 +19115 +15015 +16645 +10805 +16214 +19047 +12324 +16211 +18511 +16564 +14116 +7889 +1392 +16836 +764 +5843 +6642 +10019 +652 +9520 +12063 +12796 +5614 +3854 +13715 +1967 +6234 +19116 +17506 +5372 +1757 +9779 +1602 +17388 +1426 +10242 +15945 +15458 +16226 +3591 +1600 +8249 +19284 +11497 +19087 +19865 +1171 +13498 +9789 +11682 +10949 +1897 +16649 +6389 +17446 +4783 +6917 +7019 +16224 +11046 +6236 +15851 +1965 +6323 +15120 +13774 +1913 +668 +6193 +11617 +18867 +1839 +1507 +19433 +18326 +5922 +15468 +14764 +19434 +15915 +17531 +2201 +9970 +8108 +6647 +3871 +15044 +198 +13071 +13836 +2234 +3248 +7027 +18630 +6894 +8504 +14268 +13542 +12579 +4510 +14641 +11348 +10437 +411 +9682 +13850 +12554 +2215 +4432 +19754 +6577 +6692 +5885 +13896 +13530 +8995 +8984 +6713 +1638 +13610 +12847 +8177 +17639 +5061 +7959 +16560 +5002 +5882 +10017 +7224 +2383 +395 +10415 +14874 +10808 +19361 +1853 +5595 +2833 +2630 +4200 +9069 +953 +1704 +2651 +5490 +15447 +6469 +18785 +15647 +8618 +18836 +8001 +6054 +12794 +8130 +11472 +14941 +12022 +5015 +14748 +19796 +10336 +16788 +9751 +8247 +16711 +5665 +480 +9231 +19950 +9124 +5067 +12568 +8320 +16221 +12513 +4007 +11214 +7084 +18858 +15861 +5809 +3408 +12988 +18103 +2228 +2414 +11002 +11463 +487 +2084 +873 +1499 +6827 +8902 +19125 +1699 +6154 +29 +14125 +9699 +16418 +3683 +6248 +7809 +2144 +9001 +8404 +8930 +6509 +10207 +2609 +14163 +15598 +5857 +731 +6295 +16312 +13307 +8062 +18874 +6720 +14190 +9457 +18814 +18570 +4360 +18309 +6782 +9548 +14992 +17017 +16082 +2331 +6703 +13941 +16808 +6596 +18048 +12268 +2472 +11044 +19068 +18961 +3910 +14000 +9847 +2769 +4774 +15335 +2071 +7173 +13524 +16700 +1006 +4290 +9556 From 2b8a6e98eeed5d86a5af8a0a87df2d04dca55a0a Mon Sep 17 00:00:00 2001 From: electricboogie <32370782+electricboogie@users.noreply.github.com> Date: Sun, 25 Apr 2021 00:20:56 -0500 Subject: [PATCH 35/55] Working ExtSort --- Cargo.lock | 2 +- src/uu/sort/Cargo.toml | 2 +- src/uu/sort/src/ext_sorter/LICENSE | 202 --------------------- src/uu/sort/src/ext_sorter/NOTICE | 9 - src/uu/sort/src/ext_sorter/mod.rs | 277 ----------------------------- src/uu/sort/src/sort.rs | 108 +++++------ 6 files changed, 47 insertions(+), 553 deletions(-) delete mode 100644 src/uu/sort/src/ext_sorter/LICENSE delete mode 100644 src/uu/sort/src/ext_sorter/NOTICE delete mode 100644 src/uu/sort/src/ext_sorter/mod.rs diff --git a/Cargo.lock b/Cargo.lock index eb99af34b..d5dbf3508 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2305,7 +2305,7 @@ dependencies = [ "serde", "serde_json", "smallvec 1.6.1", - "tempfile", + "tempdir", "uucore", "uucore_procs", ] diff --git a/src/uu/sort/Cargo.toml b/src/uu/sort/Cargo.toml index f29df6ab8..12c685e23 100644 --- a/src/uu/sort/Cargo.toml +++ b/src/uu/sort/Cargo.toml @@ -26,7 +26,7 @@ semver = "0.9.0" smallvec = { version = "1.6.1", features = ["serde"] } uucore = { version=">=0.0.8", package="uucore", path="../../uucore", features=["fs"] } uucore_procs = { version=">=0.0.5", package="uucore_procs", path="../../uucore_procs" } -tempfile = "3.1.0" +tempdir = "0.3.7" [[bin]] name = "sort" diff --git a/src/uu/sort/src/ext_sorter/LICENSE b/src/uu/sort/src/ext_sorter/LICENSE deleted file mode 100644 index fe647bd7f..000000000 --- a/src/uu/sort/src/ext_sorter/LICENSE +++ /dev/null @@ -1,202 +0,0 @@ - - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - -TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - -1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - -2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - -3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - -4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - -5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - -6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - -7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - -8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - -9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - -END OF TERMS AND CONDITIONS - -APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - -Copyright [yyyy] [name of copyright owner] - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. \ No newline at end of file diff --git a/src/uu/sort/src/ext_sorter/NOTICE b/src/uu/sort/src/ext_sorter/NOTICE deleted file mode 100644 index fdfc6f04f..000000000 --- a/src/uu/sort/src/ext_sorter/NOTICE +++ /dev/null @@ -1,9 +0,0 @@ -ext_sorter -Copyright 2018 Andre-Philippe Paquet -Modifications copyright 2021 Robert Swinford - -This ext_sorter module includes software developed by Andre-Philippe Paquet as extsort. - -The sorter.rs file was copied and modified for use in the uutils' coreutils subproject, sort. - -sort is licensed according to the term of the LICENSE file found in root directory of the uutils' coreutils project. \ No newline at end of file diff --git a/src/uu/sort/src/ext_sorter/mod.rs b/src/uu/sort/src/ext_sorter/mod.rs deleted file mode 100644 index a90be6bb0..000000000 --- a/src/uu/sort/src/ext_sorter/mod.rs +++ /dev/null @@ -1,277 +0,0 @@ -// Copyright 2018 Andre-Philippe Paquet -// Modifications copyright 2021 Robert Swinford -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -// This file has been modified for use in the uutils' coreutils subproject, sort. - -use rayon::prelude::*; -use std::{ - cmp::Ordering, - collections::VecDeque, - fs::{File, OpenOptions}, - io::{BufReader, BufWriter, Error, Read, Seek, SeekFrom, Write}, - path::{Path, PathBuf}, -}; - -/// Exposes external sorting (i.e. on disk sorting) capability on arbitrarily -/// sized iterator, even if the generated content of the iterator doesn't fit in -/// memory. -/// -/// It uses an in-memory buffer sorted and flushed to disk in segment files when -/// full. Once sorted, it returns a new sorted iterator with all items. In order -/// to remain efficient for all implementations, the crate doesn't handle -/// serialization, but leaves that to the user. -pub struct ExternalSorter { - segment_size: usize, - sort_dir: Option, - parallel: bool, -} - -impl ExternalSorter { - pub fn new() -> ExternalSorter { - ExternalSorter { - // Default is 16G - But we never use it, - // because we always set or ignore - segment_size: 16000000000, - sort_dir: None, - parallel: false, - } - } - - /// Sets the maximum size of each segment in number of sorted items. - /// - /// This number of items needs to fit in memory. While sorting, a - /// in-memory buffer is used to collect the items to be sorted. Once - /// it reaches the maximum size, it is sorted and then written to disk. - /// - /// Using a higher segment size makes sorting faster by leveraging - /// faster in-memory operations. - pub fn with_segment_size(mut self, size: usize) -> Self { - self.segment_size = size; - self - } - - /// Sets directory in which sorted segments will be written (if it doesn't - /// fit in memory). - pub fn with_sort_dir(mut self, path: PathBuf) -> Self { - self.sort_dir = Some(path); - self - } - - /// Uses Rayon to sort the in-memory buffer. - /// - /// This may not be needed if the buffer isn't big enough for parallelism to - /// be gainful over the overhead of multithreading. - pub fn with_parallel_sort(mut self) -> Self { - self.parallel = true; - self - } - - /// Sorts a given iterator with a comparator function, returning a new iterator with items - pub fn sort_by(&self, iterator: I, cmp: F) -> Result, Error> - where - T: Sortable, - I: Iterator, - F: Fn(&T, &T) -> Ordering + Send + Sync, - { - let mut tempdir: Option = None; - let mut sort_dir: Option = None; - - let mut count = 0; - let mut segments_file: Vec = Vec::new(); - - let size_of_items = std::mem::size_of::(); - // Get size of iterator - let (_, upper_bound) = iterator.size_hint(); - // Buffer size specified + minimum overhead of struct / size of items - let initial_capacity = (self.segment_size + (upper_bound.unwrap() * size_of_items)) / size_of_items; - let mut buffer: Vec = Vec::with_capacity(initial_capacity); - - for next_item in iterator { - count += 1; - buffer.push(next_item); - // if after push, number of elements in vector > initial capacity - if buffer.len() > initial_capacity { - let sort_dir = self.lazy_create_dir(&mut tempdir, &mut sort_dir)?; - self.sort_and_write_segment(sort_dir, &mut segments_file, &mut buffer, &cmp)?; - // Truncate buffer back to initial capacity - buffer.truncate(initial_capacity); - } - } - - // Write any items left in buffer, but only if we had at least 1 segment - // written. Otherwise we use the buffer itself to iterate from memory - let pass_through_queue = if !buffer.is_empty() && !segments_file.is_empty() { - let sort_dir = self.lazy_create_dir(&mut tempdir, &mut sort_dir)?; - self.sort_and_write_segment(sort_dir, &mut segments_file, &mut buffer, &cmp)?; - None - } else { - buffer.sort_by(&cmp); - Some(VecDeque::from(buffer)) - }; - - SortedIterator::new(tempdir, pass_through_queue, segments_file, count, cmp) - } - - /// We only want to create directory if it's needed (i.e. if the dataset - /// doesn't fit in memory) to prevent filesystem latency - fn lazy_create_dir<'a>( - &self, - tempdir: &mut Option, - sort_dir: &'a mut Option, - ) -> Result<&'a Path, Error> { - if let Some(sort_dir) = sort_dir { - return Ok(sort_dir); - } - - *sort_dir = if let Some(ref sort_dir) = self.sort_dir { - Some(sort_dir.to_path_buf()) - } else { - *tempdir = Some(tempfile::TempDir::new()?); - Some(tempdir.as_ref().unwrap().path().to_path_buf()) - }; - - Ok(sort_dir.as_ref().unwrap()) - } - - fn sort_and_write_segment( - &self, - sort_dir: &Path, - segments: &mut Vec, - buffer: &mut Vec, - cmp: F, - ) -> Result<(), Error> - where - T: Sortable, - F: Fn(&T, &T) -> Ordering + Send + Sync, - { - if self.parallel { - buffer.par_sort_by(|a, b| cmp(a, b)); - } else { - buffer.sort_by(|a, b| cmp(a, b)); - } - - let segment_path = sort_dir.join(format!("{}", segments.len())); - let segment_file = OpenOptions::new() - .create(true) - .truncate(true) - .read(true) - .write(true) - .open(&segment_path)?; - let mut buf_writer = BufWriter::new(segment_file); - - // Possible panic here. - // Why use drain here, if we want to dump the entire buffer? - // Was "buffer.drain(0..)" - for item in buffer { - item.encode(&mut buf_writer); - } - - let file = buf_writer.into_inner()?; - segments.push(file); - - Ok(()) - } -} - -impl Default for ExternalSorter { - fn default() -> Self { - ExternalSorter::new() - } -} - -pub trait Sortable: Sized + Send { - fn encode(&self, writer: &mut W); - fn decode(reader: &mut R) -> Option; -} - -pub struct SortedIterator { - _tempdir: Option, - pass_through_queue: Option>, - segments_file: Vec>, - next_values: Vec>, - count: u64, - cmp: F, -} - -impl Ordering + Send + Sync> SortedIterator { - fn new( - tempdir: Option, - pass_through_queue: Option>, - mut segments_file: Vec, - count: u64, - cmp: F, - ) -> Result, Error> { - for segment in &mut segments_file { - segment.seek(SeekFrom::Start(0))?; - } - - let next_values = segments_file - .iter_mut() - .map(|file| T::decode(file)) - .collect(); - - let segments_file_buffered = segments_file.into_iter().map(BufReader::new).collect(); - - Ok(SortedIterator { - _tempdir: tempdir, - pass_through_queue, - segments_file: segments_file_buffered, - next_values, - count, - cmp, - }) - } - - pub fn sorted_count(&self) -> u64 { - self.count - } -} - -impl Ordering> Iterator for SortedIterator { - type Item = T; - - fn next(&mut self) -> Option { - // if we have a pass through, we dequeue from it directly - if let Some(ptb) = self.pass_through_queue.as_mut() { - return ptb.pop_front(); - } - - // otherwise, we iter from segments on disk - let mut smallest_idx: Option = None; - { - let mut smallest: Option<&T> = None; - for idx in 0..self.segments_file.len() { - let next_value = self.next_values[idx].as_ref(); - if next_value.is_none() { - continue; - } - - if smallest.is_none() - || (self.cmp)(next_value.unwrap(), smallest.unwrap()) == Ordering::Less - { - smallest = Some(next_value.unwrap()); - smallest_idx = Some(idx); - } - } - } - - smallest_idx.map(|idx| { - let file = &mut self.segments_file[idx]; - let value = self.next_values[idx].take().unwrap(); - self.next_values[idx] = T::decode(file); - value - }) - } -} diff --git a/src/uu/sort/src/sort.rs b/src/uu/sort/src/sort.rs index 571541fc6..8c3a0cf7f 100644 --- a/src/uu/sort/src/sort.rs +++ b/src/uu/sort/src/sort.rs @@ -15,11 +15,11 @@ #[macro_use] extern crate uucore; -mod ext_sorter; mod numeric_str_cmp; +mod external_sort; +use external_sort::{ExternalSorter, ExternallySortable}; use clap::{App, Arg}; -use ext_sorter::{ExternalSorter, Sortable}; use fnv::FnvHasher; use itertools::Itertools; use numeric_str_cmp::{numeric_str_cmp, NumInfo, NumInfoParseSettings}; @@ -27,7 +27,7 @@ use rand::distributions::Alphanumeric; use rand::{thread_rng, Rng}; use rayon::prelude::*; use semver::Version; -use serde::{Deserialize, Serialize}; +use serde::{Deserializer, Deserialize, Serialize}; use smallvec::SmallVec; use std::borrow::Cow; use std::cmp::Ordering; @@ -103,7 +103,7 @@ enum SortMode { Version, Default, } - +#[derive(Clone)] struct GlobalSettings { mode: SortMode, ignore_blanks: bool, @@ -176,7 +176,7 @@ impl Default for GlobalSettings { } } } - +#[derive(Clone)] struct KeySettings { mode: SortMode, ignore_blanks: bool, @@ -201,7 +201,7 @@ impl From<&GlobalSettings> for KeySettings { } } -#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)] +#[derive(Debug, Serialize, Deserialize, Clone)] /// Represents the string selected by a FieldSelector. enum SelectionRange { /// If we had to transform this selection, we have to store a new string. @@ -232,13 +232,23 @@ impl SelectionRange { } } } -#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)] + +#[derive(Debug, Serialize, Deserialize, Clone)] enum NumCache { + #[serde(deserialize_with="bailout_parse_f64")] AsF64(f64), WithInfo(NumInfo), None, } +// Only used when serde can't parse a null value +fn bailout_parse_f64<'de, D>(d: D) -> Result where D: Deserializer<'de> { + Deserialize::deserialize(d) + .map(|x: Option<_>| { + x.unwrap_or(0f64) + }) +} + impl NumCache { fn as_f64(&self) -> f64 { match self { @@ -253,7 +263,7 @@ impl NumCache { } } } -#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)] +#[derive(Debug, Serialize, Deserialize, Clone)] struct Selection { range: SelectionRange, num_cache: NumCache, @@ -267,56 +277,29 @@ impl Selection { } type Field = Range; -#[derive(Debug, Serialize, Deserialize)] + +#[derive(Serialize, Deserialize, Clone)] struct Line { line: String, // The common case is not to specify fields. Let's make this fast. selections: SmallVec<[Selection; 1]>, } -impl Sortable for Line { - fn encode(&self, write: &mut W) { - let line = Line { - line: self.line.to_owned(), - selections: self.selections.to_owned(), - }; - let serialized = serde_json::to_string(&line).unwrap(); - // Each instance of valid JSON needs to be seperated by something, so here we use a newline - write - .write_all(format!("{}{}", serialized, "\n").as_bytes()) - .unwrap(); - } - - // This crate asks us to write one Line struct at a time, but then returns multiple Lines to us at once. - // We concatanate them and return them as one big Line here. - fn decode(read: &mut R) -> Option { - let buf_reader = BufReader::new(read); - let result = { - let mut line_joined = String::new(); - // Return an empty vec for selections - let selections_joined = SmallVec::new(); - let mut p_iter = buf_reader.lines().peekable(); - while let Some(line) = p_iter.next() { - let deserialized_line: Line = - serde_json::from_str(&line.as_ref().unwrap()).unwrap(); - if let Some(_next_line) = p_iter.peek() { - line_joined = format!("{}\n{}\n", line_joined, deserialized_line.line) - } else { - line_joined = format!("{}\n{}", line_joined, deserialized_line.line) - } - // I think we've done our sorting already and these selctions are irrelevant? - // @miDeb what's your sense? Could we just return an empty vec? - //selections_joined.append(&mut deserialized_line.selections); - } - Some(Line { - line: line_joined, - selections: selections_joined, - }) - }; - result +impl ExternallySortable for Line { + fn get_size(&self) -> u64 { + // Currently 96 bytes, but that could change, so we get that size here + std::mem::size_of::() as u64 } } +impl PartialEq for Line { + fn eq(&self, other: &Self) -> bool { + self.line == other.line + } +} + +impl Eq for Line {} + impl Line { fn new(line: String, settings: &GlobalSettings) -> Self { let fields = if settings @@ -449,6 +432,7 @@ fn tokenize_with_separator(line: &str, separator: char) -> Vec { tokens } +#[derive(Clone)] struct KeyPosition { /// 1-indexed, 0 is invalid. field: usize, @@ -516,7 +500,7 @@ impl KeyPosition { } } } - +#[derive(Clone)] struct FieldSelector { from: KeyPosition, to: Option, @@ -1014,10 +998,10 @@ pub fn uumain(args: impl uucore::Args) -> i32 { }); } - exec(files, &settings) + exec(files, settings) } -fn exec(files: Vec, settings: &GlobalSettings) -> i32 { +fn exec(files: Vec, settings: GlobalSettings) -> i32 { let mut lines = Vec::new(); let mut file_merger = FileMerger::new(&settings); @@ -1059,7 +1043,7 @@ fn exec(files: Vec, settings: &GlobalSettings) -> i32 { // Probably faster that we don't create // an owned value each run if settings.buffer_size != DEFAULT_BUF_SIZE { - lines = ext_sort_by(lines, &settings); + lines = ext_sort_by(lines, settings.clone()); } else { sort_by(&mut lines, &settings); } @@ -1074,7 +1058,7 @@ fn exec(files: Vec, settings: &GlobalSettings) -> i32 { print_sorted( lines .into_iter() - .dedup_by(|a, b| compare_by(a, b, settings) == Ordering::Equal) + .dedup_by(|a, b| compare_by(a, b, &settings) == Ordering::Equal) .map(|line| line.line), &settings, ) @@ -1117,15 +1101,13 @@ fn exec_check_file(unwrapped_lines: &[Line], settings: &GlobalSettings) -> i32 { } } -fn ext_sort_by(lines: Vec, settings: &GlobalSettings) -> Vec { - let sorter = ExternalSorter::new() - .with_segment_size(settings.buffer_size) - .with_sort_dir(settings.tmp_dir.clone()) - .with_parallel_sort(); - sorter - .sort_by(lines.into_iter(), |a, b| compare_by(a, b, &settings)) - .unwrap() - .collect() +fn ext_sort_by(unsorted: Vec, settings: GlobalSettings) -> Vec { + let external_sorter = ExternalSorter::new(settings.buffer_size as u64, Some(settings.tmp_dir.clone()), settings.clone()); + let iter = external_sorter.sort_by(unsorted.into_iter(), settings.clone()).unwrap(); + let vec = iter.filter(|x| x.is_ok() ) + .map(|x| x.unwrap()) + .collect::>(); + vec } fn sort_by(lines: &mut Vec, settings: &GlobalSettings) { From 26fc8e57c7746305901f1933c9c14f53b4d32e32 Mon Sep 17 00:00:00 2001 From: electricboogie <32370782+electricboogie@users.noreply.github.com> Date: Sun, 25 Apr 2021 10:03:29 -0500 Subject: [PATCH 36/55] Fix NumCache and Serde JSON conflict by disabling NumCache during extsort general numeric compares --- src/uu/sort/src/external_sort/LICENSE | 19 ++ src/uu/sort/src/external_sort/mod.rs | 246 ++++++++++++++++++++++++++ src/uu/sort/src/sort.rs | 36 ++-- tests/by-util/test_sort.rs | 12 ++ 4 files changed, 294 insertions(+), 19 deletions(-) create mode 100644 src/uu/sort/src/external_sort/LICENSE create mode 100644 src/uu/sort/src/external_sort/mod.rs diff --git a/src/uu/sort/src/external_sort/LICENSE b/src/uu/sort/src/external_sort/LICENSE new file mode 100644 index 000000000..e26c89c9f --- /dev/null +++ b/src/uu/sort/src/external_sort/LICENSE @@ -0,0 +1,19 @@ +Copyright 2018 Battelle Memorial Institute + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. \ No newline at end of file diff --git a/src/uu/sort/src/external_sort/mod.rs b/src/uu/sort/src/external_sort/mod.rs new file mode 100644 index 000000000..9fcaadcc3 --- /dev/null +++ b/src/uu/sort/src/external_sort/mod.rs @@ -0,0 +1,246 @@ +use std::{clone::Clone}; +use std::cmp::Ordering::Less; +use std::collections::VecDeque; +use std::error::Error; +use std::fs::{File, OpenOptions}; +use std::io::SeekFrom::Start; +use std::io::{BufRead, BufReader, Seek, Write}; +use std::marker::PhantomData; +use std::path::PathBuf; + +use serde::de::DeserializeOwned; +use serde::Serialize; +use serde_json; +use tempdir::TempDir; + +use super::{GlobalSettings, Line}; + +/// Trait for types that can be used by +/// [ExternalSorter](struct.ExternalSorter.html). Must be sortable, cloneable, +/// serializeable, and able to report on it's size +pub trait ExternallySortable: Clone + Serialize + DeserializeOwned { + /// Get the size, in bytes, of this object (used to constrain the buffer + /// used in the external sort). + fn get_size(&self) -> u64; +} + +/// Iterator that provides sorted `T`s +pub struct ExtSortedIterator { + buffers: Vec>, + chunk_offsets: Vec, + max_per_chunk: u64, + chunks: u64, + tmp_dir: TempDir, + settings: GlobalSettings, + failed: bool, +} + +impl Iterator for ExtSortedIterator +where + Line: ExternallySortable, +{ + type Item = Result>; + + /// # Errors + /// + /// This method can fail due to issues reading intermediate sorted chunks + /// from disk, or due to serde deserialization issues + fn next(&mut self) -> Option { + if self.failed { + return None; + } + // fill up any empty buffers + let mut empty = true; + for chunk_num in 0..self.chunks { + if self.buffers[chunk_num as usize].is_empty() { + let mut f = match File::open(self.tmp_dir.path().join(chunk_num.to_string())) { + Ok(f) => f, + Err(e) => { + self.failed = true; + return Some(Err(Box::new(e))); + } + }; + match f.seek(Start(self.chunk_offsets[chunk_num as usize])) { + Ok(_) => (), + Err(e) => { + self.failed = true; + return Some(Err(Box::new(e))); + } + } + let bytes_read = + match fill_buff(&mut self.buffers[chunk_num as usize], f, self.max_per_chunk) { + Ok(bytes_read) => bytes_read, + Err(e) => { + self.failed = true; + return Some(Err(e)); + } + }; + self.chunk_offsets[chunk_num as usize] += bytes_read; + if !self.buffers[chunk_num as usize].is_empty() { + empty = false; + } + } else { + empty = false; + } + } + if empty { + return None; + } + + // find the next record to write + // check is_empty() before unwrap()ing + let mut idx = 0; + for chunk_num in 0..self.chunks as usize { + if !self.buffers[chunk_num].is_empty() { + if self.buffers[idx].is_empty() || (super::compare_by)( + self.buffers[chunk_num].front().unwrap(), + self.buffers[idx].front().unwrap(), + &self.settings + ) == Less + { + idx = chunk_num; + } + } + } + + // unwrap due to checks above + let r = self.buffers[idx].pop_front().unwrap(); + Some(Ok(r)) + } +} + +/// Perform an external sort on an unsorted stream of incoming data +pub struct ExternalSorter +where + Line: ExternallySortable, +{ + tmp_dir: Option, + buffer_bytes: u64, + phantom: PhantomData, + settings: GlobalSettings, +} + +impl ExternalSorter +where + Line: ExternallySortable, +{ + /// Create a new `ExternalSorter` with a specified memory buffer and + /// temporary directory + pub fn new(buffer_bytes: u64, tmp_dir: Option, settings: GlobalSettings) -> ExternalSorter { + ExternalSorter { + buffer_bytes, + tmp_dir, + phantom: PhantomData, + settings, + } + } + + /// Sort (based on `compare`) the `T`s provided by `unsorted` and return an + /// iterator + /// + /// # Errors + /// + /// This method can fail due to issues writing intermediate sorted chunks + /// to disk, or due to serde serialization issues + pub fn sort_by(&self, unsorted: I, settings: GlobalSettings) -> Result, Box> + where + I: Iterator, + { + let tmp_dir = match self.tmp_dir { + Some(ref p) => TempDir::new_in(p, "uutils_sort")?, + None => TempDir::new("uutils_sort")?, + }; + // creating the thing we need to return first due to the face that we need to + // borrow tmp_dir and move it out + let mut iter = ExtSortedIterator { + buffers: Vec::new(), + chunk_offsets: Vec::new(), + max_per_chunk: 0, + chunks: 0, + tmp_dir, + settings, + failed: false, + }; + + { + let mut total_read = 0; + let mut chunk = Vec::new(); + + // make the initial chunks on disk + for seq in unsorted { + total_read += seq.get_size(); + chunk.push(seq); + + if total_read >= self.buffer_bytes { + super::sort_by(&mut chunk, &self.settings); + self.write_chunk( + &iter.tmp_dir.path().join(iter.chunks.to_string()), + &mut chunk, + )?; + chunk.clear(); + total_read = 0; + iter.chunks += 1; + } + } + // write the last chunk + if chunk.len() > 0 { + super::sort_by(&mut chunk, &self.settings); + self.write_chunk( + &iter.tmp_dir.path().join(iter.chunks.to_string()), + &mut chunk, + )?; + iter.chunks += 1; + } + + // initialize buffers for each chunk + iter.max_per_chunk = self.buffer_bytes.checked_div(iter.chunks).unwrap_or(self.buffer_bytes); + iter.buffers = vec![VecDeque::new(); iter.chunks as usize]; + iter.chunk_offsets = vec![0 as u64; iter.chunks as usize]; + for chunk_num in 0..iter.chunks { + let offset = fill_buff( + &mut iter.buffers[chunk_num as usize], + File::open(iter.tmp_dir.path().join(chunk_num.to_string()))?, + iter.max_per_chunk, + )?; + iter.chunk_offsets[chunk_num as usize] = offset; + } + } + + Ok(iter) + } + + fn write_chunk(&self, file: &PathBuf, chunk: &mut Vec) -> Result<(), Box> { + let mut new_file = OpenOptions::new().create(true).append(true).open(file)?; + for s in chunk { + let mut serialized = serde_json::to_string(&s).expect("JSON write error: "); + serialized.push_str("\n"); + new_file.write_all(serialized.as_bytes())?; + } + + Ok(()) + } +} + +fn fill_buff(vec: &mut VecDeque, file: File, max_bytes: u64) -> Result> +where + Line: ExternallySortable, +{ + let mut total_read = 0; + let mut bytes_read = 0; + for line in BufReader::new(file).lines() { + let line_s = line?; + bytes_read += line_s.len() + 1; + // This is where the bad stuff happens usually + let deserialized: Line = match serde_json::from_str(&line_s) { + Ok(x) => x, + Err(err) => panic!("JSON read error: {}", err), + }; + total_read += deserialized.get_size(); + vec.push_back(deserialized); + if total_read > max_bytes { + break; + } + } + + Ok(bytes_read as u64) +} diff --git a/src/uu/sort/src/sort.rs b/src/uu/sort/src/sort.rs index 8c3a0cf7f..e77271557 100644 --- a/src/uu/sort/src/sort.rs +++ b/src/uu/sort/src/sort.rs @@ -91,7 +91,7 @@ static NEGATIVE: char = '-'; static POSITIVE: char = '+'; static DEFAULT_TMPDIR: &str = r"/tmp"; -// 16GB buffer for Vec before we dump to disk +// 16GB buffer for Vec before we dump to disk, never used static DEFAULT_BUF_SIZE: usize = 16000000000; #[derive(Eq, Ord, PartialEq, PartialOrd, Clone)] @@ -292,14 +292,6 @@ impl ExternallySortable for Line { } } -impl PartialEq for Line { - fn eq(&self, other: &Self) -> bool { - self.line == other.line - } -} - -impl Eq for Line {} - impl Line { fn new(line: String, settings: &GlobalSettings) -> Self { let fields = if settings @@ -343,7 +335,7 @@ impl Line { ); range.shorten(num_range); NumCache::WithInfo(info) - } else if selector.settings.mode == SortMode::GeneralNumeric { + } else if selector.settings.mode == SortMode::GeneralNumeric && settings.buffer_size == DEFAULT_BUF_SIZE { NumCache::AsF64(permissive_f64_parse(get_leading_gen(range.get_str(&line)))) } else { NumCache::None @@ -1103,11 +1095,12 @@ fn exec_check_file(unwrapped_lines: &[Line], settings: &GlobalSettings) -> i32 { fn ext_sort_by(unsorted: Vec, settings: GlobalSettings) -> Vec { let external_sorter = ExternalSorter::new(settings.buffer_size as u64, Some(settings.tmp_dir.clone()), settings.clone()); - let iter = external_sorter.sort_by(unsorted.into_iter(), settings.clone()).unwrap(); - let vec = iter.filter(|x| x.is_ok() ) - .map(|x| x.unwrap()) - .collect::>(); - vec + let iter = external_sorter + .sort_by(unsorted.into_iter(), settings.clone()) + .unwrap() + .map(|x| x.unwrap()) + .collect::>(); + iter } fn sort_by(lines: &mut Vec, settings: &GlobalSettings) { @@ -1130,10 +1123,15 @@ fn compare_by(a: &Line, b: &Line, global_settings: &GlobalSettings) -> Ordering (a_str, a_selection.num_cache.as_num_info()), (b_str, b_selection.num_cache.as_num_info()), ), - SortMode::GeneralNumeric => general_numeric_compare( - a_selection.num_cache.as_f64(), - b_selection.num_cache.as_f64(), - ), + // serde JSON has issues with f64 null values, so caching them won't work for us with ext sort + SortMode::GeneralNumeric => + if global_settings.buffer_size == DEFAULT_BUF_SIZE { + general_numeric_compare(a_selection.num_cache.as_f64(), + b_selection.num_cache.as_f64()) + } else { + general_numeric_compare(permissive_f64_parse(get_leading_gen(a_str)), + permissive_f64_parse(get_leading_gen(b_str))) + }, SortMode::Month => month_compare(a_str, b_str), SortMode::Version => version_compare(a_str, b_str), SortMode::Default => default_compare(a_str, b_str), diff --git a/tests/by-util/test_sort.rs b/tests/by-util/test_sort.rs index c76ab219a..63883cd63 100644 --- a/tests/by-util/test_sort.rs +++ b/tests/by-util/test_sort.rs @@ -51,6 +51,18 @@ fn test_human_numeric_whitespace() { test_helper("human-numeric-whitespace", "-h"); } +// This doesn't test the ext sort feature as such, just this codepath where +// ext sort can fail when reading back JSON if it finds a null value +#[test] +fn test_extsort_as64_bailout() { + new_ucmd!() + .arg("-g") + .arg("-S 10K") + .arg("multiple_decimals_general.txt") + .succeeds() + .stdout_is("\n\n\n\n\n\n\n\nCARAvan\n-2028789030\n-896689\n-8.90880\n-1\n-.05\n000\n00000001\n1\n1.040000000\n1.444\n1.58590\n8.013\n45\n46.89\n576,446.88800000\n576,446.890\n 4567.\n4567.1\n4567.34\n\t\t\t\t\t\t\t\t\t\t4567..457\n\t\t\t\t37800\n\t\t\t\t\t\t45670.89079.098\n\t\t\t\t\t\t45670.89079.1\n4798908.340000000000\n4798908.45\n4798908.8909800\n"); +} + #[test] fn test_multiple_decimals_general() { new_ucmd!() From cb0c667da5bdaa396921cb7c894dcae6b6be6a98 Mon Sep 17 00:00:00 2001 From: electricboogie <32370782+electricboogie@users.noreply.github.com> Date: Sun, 25 Apr 2021 10:12:03 -0500 Subject: [PATCH 37/55] Ran Rustfmt --- Cargo.lock | 3 --- 1 file changed, 3 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index d4aefd98c..fab7d57d1 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1432,9 +1432,6 @@ name = "smallvec" version = "1.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fe0f37c9e8f3c5a4a66ad655a93c74daac4ad00c441533bf5c6e7990bb42604e" -dependencies = [ - "serde", -] [[package]] name = "strsim" From 094d9a9e476882d6a437f3889681b3bda5ec9867 Mon Sep 17 00:00:00 2001 From: electricboogie <32370782+electricboogie@users.noreply.github.com> Date: Sun, 25 Apr 2021 12:27:11 -0500 Subject: [PATCH 38/55] Fix bug in human_numeric convert --- Cargo.lock | 3 ++ src/uu/sort/Cargo.toml | 2 +- src/uu/sort/src/sort.rs | 60 +++++++++++++++++--------------------- tests/by-util/test_sort.rs | 4 +-- 4 files changed, 33 insertions(+), 36 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index fab7d57d1..d4aefd98c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1432,6 +1432,9 @@ name = "smallvec" version = "1.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fe0f37c9e8f3c5a4a66ad655a93c74daac4ad00c441533bf5c6e7990bb42604e" +dependencies = [ + "serde", +] [[package]] name = "strsim" diff --git a/src/uu/sort/Cargo.toml b/src/uu/sort/Cargo.toml index 02ab385da..80ffc92c9 100644 --- a/src/uu/sort/Cargo.toml +++ b/src/uu/sort/Cargo.toml @@ -23,7 +23,7 @@ clap = "2.33" fnv = "1.0.7" itertools = "0.10.0" semver = "0.9.0" -smallvec = "1.6.1" +smallvec = { version="1.6.1", features=["serde"] } unicode-width = "0.1.8" uucore = { version=">=0.0.8", package="uucore", path="../../uucore", features=["fs"] } uucore_procs = { version=">=0.0.5", package="uucore_procs", path="../../uucore_procs" } diff --git a/src/uu/sort/src/sort.rs b/src/uu/sort/src/sort.rs index 049c09970..8d513b837 100644 --- a/src/uu/sort/src/sort.rs +++ b/src/uu/sort/src/sort.rs @@ -27,7 +27,7 @@ use rand::distributions::Alphanumeric; use rand::{thread_rng, Rng}; use rayon::prelude::*; use semver::Version; -use serde::{Deserializer, Deserialize, Serialize}; +use serde::{Deserialize, Serialize}; use smallvec::SmallVec; use std::cmp::Ordering; use std::collections::BinaryHeap; @@ -41,6 +41,7 @@ use std::ops::Range; use std::path::Path; use unicode_width::UnicodeWidthStr; use uucore::fs::is_stdin_interactive; // for Iterator::dedup() +use std::path::PathBuf; static NAME: &str = "sort"; static ABOUT: &str = "Display sorted concatenation of all FILE(s)."; @@ -133,22 +134,20 @@ impl GlobalSettings { // It's back to do conversions for command line opts! // Probably want to do through numstrcmp somehow now? fn human_numeric_convert(a: &str) -> usize { - let num_part = leading_num_common(a); - let (_, s) = a.split_at(num_part.len()); - let num_part = permissive_f64_parse(num_part); - let suffix = match s.parse().unwrap_or('\0') { + let num_str = &a[get_leading_gen(a)]; + let (_, suf_str) = a.split_at(num_str.len()); + let num_usize = num_str.parse::().expect("Error parsing buffer size: "); + let suf_usize: usize = match suf_str.to_uppercase().as_str() { // SI Units - 'K' | 'k' => 1E3, - 'M' => 1E6, - 'G' => 1E9, - 'T' => 1E12, - 'P' => 1E15, - 'E' => 1E18, - 'Z' => 1E21, - 'Y' => 1E24, - _ => 1f64, + "K" => 1000usize, + "M" => 1000000usize, + "G" => 1000000000usize, + "T" => 1000000000000usize, + "P" => 1000000000000000usize, + "E" => 1000000000000000000usize, + _ => 1usize, }; - num_part as usize * suffix as usize + num_usize * suf_usize } } @@ -236,22 +235,13 @@ impl SelectionRange { } } -#[derive(Debug, Serialize, Deserialize, Clone)] +#[derive(Serialize, Deserialize, Clone)] enum NumCache { - #[serde(deserialize_with="bailout_parse_f64")] AsF64(GeneralF64ParseResult), WithInfo(NumInfo), None, } -// Only used when serde can't parse a null value -fn bailout_parse_f64<'de, D>(d: D) -> Result where D: Deserializer<'de> { - Deserialize::deserialize(d) - .map(|x: Option<_>| { - x.unwrap_or(0f64) - }) -} - impl NumCache { fn as_f64(&self) -> GeneralF64ParseResult { match self { @@ -266,7 +256,7 @@ impl NumCache { } } } -#[derive(Debug, Serialize, Deserialize, Clone)] +#[derive(Serialize, Deserialize, Clone)] struct Selection { range: SelectionRange, num_cache: NumCache, @@ -1218,7 +1208,7 @@ fn exec(files: Vec, settings: GlobalSettings) -> i32 { if settings.merge { if settings.unique { print_sorted( - file_merger.dedup_by(|a, b| compare_by(a, b, settings) == Ordering::Equal), + file_merger.dedup_by(|a, b| compare_by(a, b, &settings) == Ordering::Equal), &settings, ) } else { @@ -1228,7 +1218,7 @@ fn exec(files: Vec, settings: GlobalSettings) -> i32 { print_sorted( lines .into_iter() - .dedup_by(|a, b| compare_by(a, b, settings) == Ordering::Equal), + .dedup_by(|a, b| compare_by(a, b, &settings) == Ordering::Equal), &settings, ) } else { @@ -1303,11 +1293,15 @@ fn compare_by(a: &Line, b: &Line, global_settings: &GlobalSettings) -> Ordering // serde JSON has issues with f64 null values, so caching them won't work for us with ext sort SortMode::GeneralNumeric => if global_settings.buffer_size == DEFAULT_BUF_SIZE { - general_numeric_compare(a_selection.num_cache.as_f64(), - b_selection.num_cache.as_f64()) + general_numeric_compare( + a_selection.num_cache.as_f64(), + b_selection.num_cache.as_f64() + ) } else { - general_numeric_compare(permissive_f64_parse(get_leading_gen(a_str)), - permissive_f64_parse(get_leading_gen(b_str))) + general_numeric_compare( + general_f64_parse(&a_str[get_leading_gen(a_str)]), + general_f64_parse(&b_str[get_leading_gen(b_str)]) + ) }, SortMode::Month => month_compare(a_str, b_str), SortMode::Version => version_compare(a_str, b_str), @@ -1385,7 +1379,7 @@ fn get_leading_gen(input: &str) -> Range { leading_whitespace_len..input.len() } -#[derive(Copy, Clone, PartialEq, PartialOrd)] +#[derive(Serialize, Deserialize, Copy, Clone, PartialEq, PartialOrd)] enum GeneralF64ParseResult { Invalid, NaN, diff --git a/tests/by-util/test_sort.rs b/tests/by-util/test_sort.rs index 894626c55..c5b63205f 100644 --- a/tests/by-util/test_sort.rs +++ b/tests/by-util/test_sort.rs @@ -33,7 +33,7 @@ fn test_helper(file_name: &str, args: &str) { fn test_larger_than_specified_segment() { new_ucmd!() .arg("-n") - .arg("-S 50K") + .arg("-S 50M") .arg("ext_sort.txt") .succeeds() .stdout_is_fixture(format!("{}", "ext_sort.expected")); @@ -67,7 +67,7 @@ fn test_extsort_as64_bailout() { .arg("-S 10K") .arg("multiple_decimals_general.txt") .succeeds() - .stdout_is("\n\n\n\n\n\n\n\nCARAvan\n-2028789030\n-896689\n-8.90880\n-1\n-.05\n000\n00000001\n1\n1.040000000\n1.444\n1.58590\n8.013\n45\n46.89\n576,446.88800000\n576,446.890\n 4567.\n4567.1\n4567.34\n\t\t\t\t\t\t\t\t\t\t4567..457\n\t\t\t\t37800\n\t\t\t\t\t\t45670.89079.098\n\t\t\t\t\t\t45670.89079.1\n4798908.340000000000\n4798908.45\n4798908.8909800\n"); + .stdout_is_fixture("multiple_decimals_general.expected"); } #[test] From f0a473f40e26a1dcd684244305e3a214c5e0552f Mon Sep 17 00:00:00 2001 From: electricboogie <32370782+electricboogie@users.noreply.github.com> Date: Sun, 25 Apr 2021 12:38:43 -0500 Subject: [PATCH 39/55] Fix tests --- src/uu/sort/src/external_sort/mod.rs | 32 ++++++++++++++++++------ src/uu/sort/src/sort.rs | 37 +++++++++++++++++----------- tests/by-util/test_sort.rs | 3 ++- 3 files changed, 49 insertions(+), 23 deletions(-) diff --git a/src/uu/sort/src/external_sort/mod.rs b/src/uu/sort/src/external_sort/mod.rs index 9fcaadcc3..e2595fc78 100644 --- a/src/uu/sort/src/external_sort/mod.rs +++ b/src/uu/sort/src/external_sort/mod.rs @@ -1,4 +1,4 @@ -use std::{clone::Clone}; +use std::clone::Clone; use std::cmp::Ordering::Less; use std::collections::VecDeque; use std::error::Error; @@ -92,10 +92,11 @@ where let mut idx = 0; for chunk_num in 0..self.chunks as usize { if !self.buffers[chunk_num].is_empty() { - if self.buffers[idx].is_empty() || (super::compare_by)( + if self.buffers[idx].is_empty() + || (super::compare_by)( self.buffers[chunk_num].front().unwrap(), self.buffers[idx].front().unwrap(), - &self.settings + &self.settings, ) == Less { idx = chunk_num; @@ -106,7 +107,7 @@ where // unwrap due to checks above let r = self.buffers[idx].pop_front().unwrap(); Some(Ok(r)) - } + } } /// Perform an external sort on an unsorted stream of incoming data @@ -126,7 +127,11 @@ where { /// Create a new `ExternalSorter` with a specified memory buffer and /// temporary directory - pub fn new(buffer_bytes: u64, tmp_dir: Option, settings: GlobalSettings) -> ExternalSorter { + pub fn new( + buffer_bytes: u64, + tmp_dir: Option, + settings: GlobalSettings, + ) -> ExternalSorter { ExternalSorter { buffer_bytes, tmp_dir, @@ -142,7 +147,11 @@ where /// /// This method can fail due to issues writing intermediate sorted chunks /// to disk, or due to serde serialization issues - pub fn sort_by(&self, unsorted: I, settings: GlobalSettings) -> Result, Box> + pub fn sort_by( + &self, + unsorted: I, + settings: GlobalSettings, + ) -> Result, Box> where I: Iterator, { @@ -193,7 +202,10 @@ where } // initialize buffers for each chunk - iter.max_per_chunk = self.buffer_bytes.checked_div(iter.chunks).unwrap_or(self.buffer_bytes); + iter.max_per_chunk = self + .buffer_bytes + .checked_div(iter.chunks) + .unwrap_or(self.buffer_bytes); iter.buffers = vec![VecDeque::new(); iter.chunks as usize]; iter.chunk_offsets = vec![0 as u64; iter.chunks as usize]; for chunk_num in 0..iter.chunks { @@ -221,7 +233,11 @@ where } } -fn fill_buff(vec: &mut VecDeque, file: File, max_bytes: u64) -> Result> +fn fill_buff( + vec: &mut VecDeque, + file: File, + max_bytes: u64, +) -> Result> where Line: ExternallySortable, { diff --git a/src/uu/sort/src/sort.rs b/src/uu/sort/src/sort.rs index 8d513b837..a519bece5 100644 --- a/src/uu/sort/src/sort.rs +++ b/src/uu/sort/src/sort.rs @@ -15,11 +15,11 @@ #[macro_use] extern crate uucore; -mod numeric_str_cmp; mod external_sort; +mod numeric_str_cmp; -use external_sort::{ExternalSorter, ExternallySortable}; use clap::{App, Arg}; +use external_sort::{ExternalSorter, ExternallySortable}; use fnv::FnvHasher; use itertools::Itertools; use numeric_str_cmp::{numeric_str_cmp, NumInfo, NumInfoParseSettings}; @@ -39,9 +39,9 @@ use std::io::{stdin, stdout, BufRead, BufReader, BufWriter, Lines, Read, Write}; use std::mem::replace; use std::ops::Range; use std::path::Path; +use std::path::PathBuf; use unicode_width::UnicodeWidthStr; use uucore::fs::is_stdin_interactive; // for Iterator::dedup() -use std::path::PathBuf; static NAME: &str = "sort"; static ABOUT: &str = "Display sorted concatenation of all FILE(s)."; @@ -136,7 +136,9 @@ impl GlobalSettings { fn human_numeric_convert(a: &str) -> usize { let num_str = &a[get_leading_gen(a)]; let (_, suf_str) = a.split_at(num_str.len()); - let num_usize = num_str.parse::().expect("Error parsing buffer size: "); + let num_usize = num_str + .parse::() + .expect("Error parsing buffer size: "); let suf_usize: usize = match suf_str.to_uppercase().as_str() { // SI Units "K" => 1000usize, @@ -323,7 +325,9 @@ impl Line { ); range.shorten(num_range); NumCache::WithInfo(info) - } else if selector.settings.mode == SortMode::GeneralNumeric && settings.buffer_size == DEFAULT_BUF_SIZE { + } else if selector.settings.mode == SortMode::GeneralNumeric + && settings.buffer_size == DEFAULT_BUF_SIZE + { let str = range.get_str(&line); NumCache::AsF64(general_f64_parse(&str[get_leading_gen(str)])) } else { @@ -1050,7 +1054,7 @@ pub fn uumain(args: impl uucore::Args) -> i32 { .value_of(OPT_BUF_SIZE) .map(String::from) .unwrap_or(format!("{}", DEFAULT_BUF_SIZE)); - + GlobalSettings::human_numeric_convert(&input) } } @@ -1261,13 +1265,17 @@ fn exec_check_file(unwrapped_lines: &[Line], settings: &GlobalSettings) -> i32 { } fn ext_sort_by(unsorted: Vec, settings: GlobalSettings) -> Vec { - let external_sorter = ExternalSorter::new(settings.buffer_size as u64, Some(settings.tmp_dir.clone()), settings.clone()); + let external_sorter = ExternalSorter::new( + settings.buffer_size as u64, + Some(settings.tmp_dir.clone()), + settings.clone(), + ); let iter = external_sorter .sort_by(unsorted.into_iter(), settings.clone()) .unwrap() .map(|x| x.unwrap()) .collect::>(); - iter + iter } fn sort_by(lines: &mut Vec, settings: &GlobalSettings) { @@ -1291,18 +1299,19 @@ fn compare_by(a: &Line, b: &Line, global_settings: &GlobalSettings) -> Ordering (b_str, b_selection.num_cache.as_num_info()), ), // serde JSON has issues with f64 null values, so caching them won't work for us with ext sort - SortMode::GeneralNumeric => + SortMode::GeneralNumeric => { if global_settings.buffer_size == DEFAULT_BUF_SIZE { general_numeric_compare( a_selection.num_cache.as_f64(), - b_selection.num_cache.as_f64() - ) + b_selection.num_cache.as_f64(), + ) } else { general_numeric_compare( general_f64_parse(&a_str[get_leading_gen(a_str)]), - general_f64_parse(&b_str[get_leading_gen(b_str)]) - ) - }, + general_f64_parse(&b_str[get_leading_gen(b_str)]), + ) + } + } SortMode::Month => month_compare(a_str, b_str), SortMode::Version => version_compare(a_str, b_str), SortMode::Default => default_compare(a_str, b_str), diff --git a/tests/by-util/test_sort.rs b/tests/by-util/test_sort.rs index c5b63205f..86951c1a4 100644 --- a/tests/by-util/test_sort.rs +++ b/tests/by-util/test_sort.rs @@ -33,7 +33,8 @@ fn test_helper(file_name: &str, args: &str) { fn test_larger_than_specified_segment() { new_ucmd!() .arg("-n") - .arg("-S 50M") + .arg("-S") + .arg("50K") .arg("ext_sort.txt") .succeeds() .stdout_is_fixture(format!("{}", "ext_sort.expected")); From 2f37b85426d12ff07828d063257515653fb90cf5 Mon Sep 17 00:00:00 2001 From: electricboogie <32370782+electricboogie@users.noreply.github.com> Date: Sun, 25 Apr 2021 12:58:04 -0500 Subject: [PATCH 40/55] unwrap_or_else can be an unwrap_or --- src/uu/sort/src/sort.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/uu/sort/src/sort.rs b/src/uu/sort/src/sort.rs index a519bece5..bf138e0c0 100644 --- a/src/uu/sort/src/sort.rs +++ b/src/uu/sort/src/sort.rs @@ -1063,7 +1063,7 @@ pub fn uumain(args: impl uucore::Args) -> i32 { let result = matches .value_of(OPT_TMP_DIR) .map(String::from) - .unwrap_or_else(|| DEFAULT_TMPDIR.to_owned()); + .unwrap_or(DEFAULT_TMPDIR.to_owned()); settings.tmp_dir = PathBuf::from(result); } else { for (key, value) in env::vars_os() { From ab594b7b4c8e0b103b336120076303d7da8fb8fa Mon Sep 17 00:00:00 2001 From: electricboogie <32370782+electricboogie@users.noreply.github.com> Date: Sun, 25 Apr 2021 13:08:55 -0500 Subject: [PATCH 41/55] Fix test comment --- tests/by-util/test_sort.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/by-util/test_sort.rs b/tests/by-util/test_sort.rs index 86951c1a4..865e2be21 100644 --- a/tests/by-util/test_sort.rs +++ b/tests/by-util/test_sort.rs @@ -59,8 +59,8 @@ fn test_human_numeric_whitespace() { test_helper("human-numeric-whitespace", "-h"); } -// This doesn't test the ext sort feature as such, just this codepath where -// ext sort can fail when reading back JSON if it finds a null value +// This tests the ext sort feature, but it also tests where +// serde might fail when reading back JSON if it finds a null value #[test] fn test_extsort_as64_bailout() { new_ucmd!() From 733949b2e7f4d1672fdcf8e0521d8b25b8ffab5a Mon Sep 17 00:00:00 2001 From: electricboogie <32370782+electricboogie@users.noreply.github.com> Date: Sun, 25 Apr 2021 15:13:27 -0500 Subject: [PATCH 42/55] Add dynamic buffer adjustment, fix test comment --- src/uu/sort/src/external_sort/mod.rs | 19 +++++++++++++------ src/uu/sort/src/sort.rs | 13 ++++++------- tests/by-util/test_sort.rs | 6 +++--- 3 files changed, 22 insertions(+), 16 deletions(-) diff --git a/src/uu/sort/src/external_sort/mod.rs b/src/uu/sort/src/external_sort/mod.rs index e2595fc78..f5a3a03af 100644 --- a/src/uu/sort/src/external_sort/mod.rs +++ b/src/uu/sort/src/external_sort/mod.rs @@ -174,13 +174,23 @@ where { let mut total_read = 0; let mut chunk = Vec::new(); + // Initial buffer is specified by user + let mut adjusted_buffer_size = self.buffer_bytes; // make the initial chunks on disk for seq in unsorted { - total_read += seq.get_size(); + let seq_size = seq.get_size(); + total_read += seq_size; + // Grow buffer size for a Line larger than buffer + adjusted_buffer_size = + if adjusted_buffer_size < seq_size { + seq_size + } else { + adjusted_buffer_size + }; chunk.push(seq); - if total_read >= self.buffer_bytes { + if total_read >= adjusted_buffer_size { super::sort_by(&mut chunk, &self.settings); self.write_chunk( &iter.tmp_dir.path().join(iter.chunks.to_string()), @@ -247,10 +257,7 @@ where let line_s = line?; bytes_read += line_s.len() + 1; // This is where the bad stuff happens usually - let deserialized: Line = match serde_json::from_str(&line_s) { - Ok(x) => x, - Err(err) => panic!("JSON read error: {}", err), - }; + let deserialized: Line = serde_json::from_str(&line_s).expect("JSON read error: "); total_read += deserialized.get_size(); vec.push_back(deserialized); if total_read > max_bytes { diff --git a/src/uu/sort/src/sort.rs b/src/uu/sort/src/sort.rs index bf138e0c0..0be91eef6 100644 --- a/src/uu/sort/src/sort.rs +++ b/src/uu/sort/src/sort.rs @@ -141,13 +141,12 @@ impl GlobalSettings { .expect("Error parsing buffer size: "); let suf_usize: usize = match suf_str.to_uppercase().as_str() { // SI Units - "K" => 1000usize, - "M" => 1000000usize, - "G" => 1000000000usize, - "T" => 1000000000000usize, - "P" => 1000000000000000usize, - "E" => 1000000000000000000usize, - _ => 1usize, + "K" => 1024usize, + "M" => 1024000usize, + "G" => 1024000000usize, + "T" => 1024000000000usize, + // GNU regards empty human numeric value as 1024 bytes + _ => 1024usize, }; num_usize * suf_usize } diff --git a/tests/by-util/test_sort.rs b/tests/by-util/test_sort.rs index 865e2be21..cd3a3a496 100644 --- a/tests/by-util/test_sort.rs +++ b/tests/by-util/test_sort.rs @@ -59,13 +59,13 @@ fn test_human_numeric_whitespace() { test_helper("human-numeric-whitespace", "-h"); } -// This tests the ext sort feature, but it also tests where -// serde might fail when reading back JSON if it finds a null value +// This tests where serde often fails when reading back JSON +// if it finds a null value #[test] fn test_extsort_as64_bailout() { new_ucmd!() .arg("-g") - .arg("-S 10K") + .arg("-S 5K") .arg("multiple_decimals_general.txt") .succeeds() .stdout_is_fixture("multiple_decimals_general.expected"); From 5fb7014c2b37cfbd4f617ddf0ba6c892770ddcd7 Mon Sep 17 00:00:00 2001 From: electricboogie <32370782+electricboogie@users.noreply.github.com> Date: Sun, 25 Apr 2021 15:42:36 -0500 Subject: [PATCH 43/55] Add a BufWriter for writes out to temp files --- src/uu/sort/src/external_sort/mod.rs | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/uu/sort/src/external_sort/mod.rs b/src/uu/sort/src/external_sort/mod.rs index f5a3a03af..222da5b58 100644 --- a/src/uu/sort/src/external_sort/mod.rs +++ b/src/uu/sort/src/external_sort/mod.rs @@ -4,7 +4,7 @@ use std::collections::VecDeque; use std::error::Error; use std::fs::{File, OpenOptions}; use std::io::SeekFrom::Start; -use std::io::{BufRead, BufReader, Seek, Write}; +use std::io::{BufRead, BufReader, BufWriter, Seek, Write}; use std::marker::PhantomData; use std::path::PathBuf; @@ -232,12 +232,14 @@ where } fn write_chunk(&self, file: &PathBuf, chunk: &mut Vec) -> Result<(), Box> { - let mut new_file = OpenOptions::new().create(true).append(true).open(file)?; + let new_file = OpenOptions::new().create(true).append(true).open(file)?; + let mut buf_write = Box::new(BufWriter::new(new_file)) as Box; for s in chunk { let mut serialized = serde_json::to_string(&s).expect("JSON write error: "); serialized.push_str("\n"); - new_file.write_all(serialized.as_bytes())?; + buf_write.write(serialized.as_bytes())?; } + buf_write.flush()?; Ok(()) } From dbdac2226220c8182b51928f1b5e92fe63bdd5ec Mon Sep 17 00:00:00 2001 From: electricboogie <32370782+electricboogie@users.noreply.github.com> Date: Sun, 25 Apr 2021 15:48:20 -0500 Subject: [PATCH 44/55] Add back unstable sort --- src/uu/sort/src/sort.rs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/uu/sort/src/sort.rs b/src/uu/sort/src/sort.rs index 0be91eef6..cbd02c18a 100644 --- a/src/uu/sort/src/sort.rs +++ b/src/uu/sort/src/sort.rs @@ -1278,7 +1278,11 @@ fn ext_sort_by(unsorted: Vec, settings: GlobalSettings) -> Vec { } fn sort_by(lines: &mut Vec, settings: &GlobalSettings) { - lines.par_sort_by(|a, b| compare_by(a, b, &settings)) + if settings.stable || settings.unique { + lines.par_sort_by(|a, b| compare_by(a, b, &settings)) + } else { + lines.par_sort_unstable_by(|a, b| compare_by(a, b, &settings)) + } } fn compare_by(a: &Line, b: &Line, global_settings: &GlobalSettings) -> Ordering { From 6f82cd4f15f56029bd524b0e962a95553caf5a6c Mon Sep 17 00:00:00 2001 From: electricboogie <32370782+electricboogie@users.noreply.github.com> Date: Sun, 25 Apr 2021 16:27:36 -0500 Subject: [PATCH 45/55] Fix errors for usize on 32bit platforms --- src/uu/sort/src/sort.rs | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/src/uu/sort/src/sort.rs b/src/uu/sort/src/sort.rs index cbd02c18a..c24d930dc 100644 --- a/src/uu/sort/src/sort.rs +++ b/src/uu/sort/src/sort.rs @@ -93,8 +93,8 @@ static NEGATIVE: char = '-'; static POSITIVE: char = '+'; static DEFAULT_TMPDIR: &str = r"/tmp"; -// 16GB buffer for Vec before we dump to disk, never used -static DEFAULT_BUF_SIZE: usize = 16000000000; +// 4GB buffer for Vec before we dump to disk, never used +static DEFAULT_BUF_SIZE: usize = 4000000000; #[derive(Eq, Ord, PartialEq, PartialOrd, Clone, Copy)] enum SortMode { @@ -141,10 +141,10 @@ impl GlobalSettings { .expect("Error parsing buffer size: "); let suf_usize: usize = match suf_str.to_uppercase().as_str() { // SI Units + "B" => 1usize, "K" => 1024usize, "M" => 1024000usize, "G" => 1024000000usize, - "T" => 1024000000000usize, // GNU regards empty human numeric value as 1024 bytes _ => 1024usize, }; @@ -1046,8 +1046,6 @@ pub fn uumain(args: impl uucore::Args) -> i32 { } if matches.is_present(OPT_BUF_SIZE) { - // 16G is the default in memory buffer. - // Although the "default" is never used settings.buffer_size = { let input = matches .value_of(OPT_BUF_SIZE) @@ -1277,11 +1275,11 @@ fn ext_sort_by(unsorted: Vec, settings: GlobalSettings) -> Vec { iter } -fn sort_by(lines: &mut Vec, settings: &GlobalSettings) { +fn sort_by(unsorted: &mut Vec, settings: &GlobalSettings) { if settings.stable || settings.unique { - lines.par_sort_by(|a, b| compare_by(a, b, &settings)) + unsorted.par_sort_by(|a, b| compare_by(a, b, &settings)) } else { - lines.par_sort_unstable_by(|a, b| compare_by(a, b, &settings)) + unsorted.par_sort_unstable_by(|a, b| compare_by(a, b, &settings)) } } From 0f707cdb25792d003d0cabb9df20428ebc34548d Mon Sep 17 00:00:00 2001 From: electricboogie <32370782+electricboogie@users.noreply.github.com> Date: Sun, 25 Apr 2021 16:33:12 -0500 Subject: [PATCH 46/55] Adjust max buffer size for read back as well --- src/uu/sort/src/external_sort/mod.rs | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/uu/sort/src/external_sort/mod.rs b/src/uu/sort/src/external_sort/mod.rs index 222da5b58..fae00fb72 100644 --- a/src/uu/sort/src/external_sort/mod.rs +++ b/src/uu/sort/src/external_sort/mod.rs @@ -181,7 +181,7 @@ where for seq in unsorted { let seq_size = seq.get_size(); total_read += seq_size; - // Grow buffer size for a Line larger than buffer + // Grow buffer size for a struct/Line larger than buffer adjusted_buffer_size = if adjusted_buffer_size < seq_size { seq_size @@ -212,10 +212,9 @@ where } // initialize buffers for each chunk - iter.max_per_chunk = self - .buffer_bytes + iter.max_per_chunk = adjusted_buffer_size .checked_div(iter.chunks) - .unwrap_or(self.buffer_bytes); + .unwrap_or(adjusted_buffer_size); iter.buffers = vec![VecDeque::new(); iter.chunks as usize]; iter.chunk_offsets = vec![0 as u64; iter.chunks as usize]; for chunk_num in 0..iter.chunks { From 32222c1ee7a1efe778624ddbe55577d604f045b0 Mon Sep 17 00:00:00 2001 From: electricboogie <32370782+electricboogie@users.noreply.github.com> Date: Sun, 25 Apr 2021 17:52:20 -0500 Subject: [PATCH 47/55] Remove unneeded condition for use of NumCache --- src/uu/sort/src/external_sort/mod.rs | 10 +++++++--- src/uu/sort/src/sort.rs | 15 +++------------ 2 files changed, 10 insertions(+), 15 deletions(-) diff --git a/src/uu/sort/src/external_sort/mod.rs b/src/uu/sort/src/external_sort/mod.rs index fae00fb72..9f3eb3776 100644 --- a/src/uu/sort/src/external_sort/mod.rs +++ b/src/uu/sort/src/external_sort/mod.rs @@ -212,9 +212,13 @@ where } // initialize buffers for each chunk - iter.max_per_chunk = adjusted_buffer_size - .checked_div(iter.chunks) - .unwrap_or(adjusted_buffer_size); + // iter.max_per_chunk = adjusted_buffer_size + // .checked_div(iter.chunks) + // .unwrap_or(adjusted_buffer_size); + // + // + // + iter.max_per_chunk = adjusted_buffer_size; iter.buffers = vec![VecDeque::new(); iter.chunks as usize]; iter.chunk_offsets = vec![0 as u64; iter.chunks as usize]; for chunk_num in 0..iter.chunks { diff --git a/src/uu/sort/src/sort.rs b/src/uu/sort/src/sort.rs index c24d930dc..ea7d36bae 100644 --- a/src/uu/sort/src/sort.rs +++ b/src/uu/sort/src/sort.rs @@ -1299,19 +1299,10 @@ fn compare_by(a: &Line, b: &Line, global_settings: &GlobalSettings) -> Ordering (a_str, a_selection.num_cache.as_num_info()), (b_str, b_selection.num_cache.as_num_info()), ), - // serde JSON has issues with f64 null values, so caching them won't work for us with ext sort SortMode::GeneralNumeric => { - if global_settings.buffer_size == DEFAULT_BUF_SIZE { - general_numeric_compare( - a_selection.num_cache.as_f64(), - b_selection.num_cache.as_f64(), - ) - } else { - general_numeric_compare( - general_f64_parse(&a_str[get_leading_gen(a_str)]), - general_f64_parse(&b_str[get_leading_gen(b_str)]), - ) - } + general_numeric_compare( + general_f64_parse(&a_str[get_leading_gen(a_str)]), + general_f64_parse(&b_str[get_leading_gen(b_str)]),) } SortMode::Month => month_compare(a_str, b_str), SortMode::Version => version_compare(a_str, b_str), From fc899ffe7a7a0698db414cb642cfcfee562fe1db Mon Sep 17 00:00:00 2001 From: electricboogie <32370782+electricboogie@users.noreply.github.com> Date: Sun, 25 Apr 2021 19:07:24 -0500 Subject: [PATCH 48/55] Implement a minimum readback buffer --- src/uu/sort/src/external_sort/mod.rs | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/src/uu/sort/src/external_sort/mod.rs b/src/uu/sort/src/external_sort/mod.rs index 9f3eb3776..628911fe7 100644 --- a/src/uu/sort/src/external_sort/mod.rs +++ b/src/uu/sort/src/external_sort/mod.rs @@ -212,13 +212,22 @@ where } // initialize buffers for each chunk - // iter.max_per_chunk = adjusted_buffer_size - // .checked_div(iter.chunks) - // .unwrap_or(adjusted_buffer_size); // - // + // Having a right sized buffer for each chunk for smallish values seems silly to me? // - iter.max_per_chunk = adjusted_buffer_size; + // We will have to have the entire iter in memory sometime right? + // Set minimum to the size of the writer buffer, ~8K + // + const MINIMUM_READBACK_BUFFER: u64 = 8200; + let right_sized_buffer = adjusted_buffer_size + .checked_div(iter.chunks) + .unwrap_or(adjusted_buffer_size); + iter.max_per_chunk = + if right_sized_buffer > MINIMUM_READBACK_BUFFER { + right_sized_buffer + } else { + MINIMUM_READBACK_BUFFER + }; iter.buffers = vec![VecDeque::new(); iter.chunks as usize]; iter.chunk_offsets = vec![0 as u64; iter.chunks as usize]; for chunk_num in 0..iter.chunks { From 8e258075f600b7b6891487ecae4a154ab8ee1573 Mon Sep 17 00:00:00 2001 From: electricboogie <32370782+electricboogie@users.noreply.github.com> Date: Sun, 25 Apr 2021 19:21:19 -0500 Subject: [PATCH 49/55] Potential fix to tests on Windows --- src/uu/sort/src/sort.rs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/uu/sort/src/sort.rs b/src/uu/sort/src/sort.rs index ea7d36bae..aa2e1bfe7 100644 --- a/src/uu/sort/src/sort.rs +++ b/src/uu/sort/src/sort.rs @@ -1064,7 +1064,10 @@ pub fn uumain(args: impl uucore::Args) -> i32 { settings.tmp_dir = PathBuf::from(result); } else { for (key, value) in env::vars_os() { - if key == OsString::from("TMPDIR") { + if key == OsString::from("TMPDIR") + || key == OsString::from("TEMP") + || key == OsString::from("TMP") + { settings.tmp_dir = PathBuf::from(value); break; } From 1a407c2328ba479b11e0a9af1e50c607958ebede Mon Sep 17 00:00:00 2001 From: electricboogie <32370782+electricboogie@users.noreply.github.com> Date: Sun, 25 Apr 2021 21:17:56 -0500 Subject: [PATCH 50/55] Set a dynamic minimum buffer size --- src/uu/sort/src/external_sort/mod.rs | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/src/uu/sort/src/external_sort/mod.rs b/src/uu/sort/src/external_sort/mod.rs index 628911fe7..81455eb18 100644 --- a/src/uu/sort/src/external_sort/mod.rs +++ b/src/uu/sort/src/external_sort/mod.rs @@ -176,15 +176,28 @@ where let mut chunk = Vec::new(); // Initial buffer is specified by user let mut adjusted_buffer_size = self.buffer_bytes; + let (iter_size, _) = unsorted.size_hint(); // make the initial chunks on disk for seq in unsorted { let seq_size = seq.get_size(); total_read += seq_size; - // Grow buffer size for a struct/Line larger than buffer + + // GNU minimum is 16 * (sizeof struct + 2), but GNU uses about + // 1/10 the memory that we do. And GNU even says in the code it may + // not work on small buffer sizes. + // + // The following seems to work pretty well, and has about the same max + // RSS as lower minimum values. + // + let minimum_buffer_size: u64 = iter_size as u64 * seq_size / 8; + adjusted_buffer_size = + // Grow buffer size for a struct/Line larger than buffer if adjusted_buffer_size < seq_size { seq_size + } else if adjusted_buffer_size < minimum_buffer_size { + minimum_buffer_size } else { adjusted_buffer_size }; From e5c19734c8a3d7f3b94a25887149b261ccffd1a8 Mon Sep 17 00:00:00 2001 From: electricboogie <32370782+electricboogie@users.noreply.github.com> Date: Sun, 25 Apr 2021 21:38:22 -0500 Subject: [PATCH 51/55] Change Default Buffer to usize::MAX --- src/uu/sort/src/sort.rs | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/src/uu/sort/src/sort.rs b/src/uu/sort/src/sort.rs index aa2e1bfe7..55362d4ad 100644 --- a/src/uu/sort/src/sort.rs +++ b/src/uu/sort/src/sort.rs @@ -93,8 +93,7 @@ static NEGATIVE: char = '-'; static POSITIVE: char = '+'; static DEFAULT_TMPDIR: &str = r"/tmp"; -// 4GB buffer for Vec before we dump to disk, never used -static DEFAULT_BUF_SIZE: usize = 4000000000; +static DEFAULT_BUF_SIZE: usize = usize::MAX; #[derive(Eq, Ord, PartialEq, PartialOrd, Clone, Copy)] enum SortMode { @@ -142,11 +141,11 @@ impl GlobalSettings { let suf_usize: usize = match suf_str.to_uppercase().as_str() { // SI Units "B" => 1usize, - "K" => 1024usize, - "M" => 1024000usize, - "G" => 1024000000usize, - // GNU regards empty human numeric value as 1024 bytes - _ => 1024usize, + "K" => 1000usize, + "M" => 1000000usize, + "G" => 1000000000usize, + // GNU regards empty human numeric values as K by default + _ => 1000usize, }; num_usize * suf_usize } From 6654519c7d91bfd22b14787106a4700977813020 Mon Sep 17 00:00:00 2001 From: electricboogie <32370782+electricboogie@users.noreply.github.com> Date: Sun, 25 Apr 2021 22:39:17 -0500 Subject: [PATCH 52/55] Specify a default tempdir for Windows --- src/uu/sort/src/sort.rs | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/uu/sort/src/sort.rs b/src/uu/sort/src/sort.rs index 55362d4ad..53f9a356a 100644 --- a/src/uu/sort/src/sort.rs +++ b/src/uu/sort/src/sort.rs @@ -92,7 +92,17 @@ static THOUSANDS_SEP: char = ','; static NEGATIVE: char = '-'; static POSITIVE: char = '+'; +#[cfg(any( + target_os = "windows", +))] +static DEFAULT_TMPDIR: &str = r"%USERPROFILE%\AppData\Local\Temp"; + +#[cfg(not(any( + target_os = "windows", +)))] static DEFAULT_TMPDIR: &str = r"/tmp"; + + static DEFAULT_BUF_SIZE: usize = usize::MAX; #[derive(Eq, Ord, PartialEq, PartialOrd, Clone, Copy)] From c01c6a7d78427d766c71dc3f1fc2753c863cc1cb Mon Sep 17 00:00:00 2001 From: electricboogie <32370782+electricboogie@users.noreply.github.com> Date: Sun, 25 Apr 2021 22:41:11 -0500 Subject: [PATCH 53/55] Ran rustfmt --- src/uu/sort/src/external_sort/mod.rs | 29 ++++++++++++++-------------- src/uu/sort/src/sort.rs | 24 +++++++++-------------- 2 files changed, 23 insertions(+), 30 deletions(-) diff --git a/src/uu/sort/src/external_sort/mod.rs b/src/uu/sort/src/external_sort/mod.rs index 81455eb18..fd942d4a7 100644 --- a/src/uu/sort/src/external_sort/mod.rs +++ b/src/uu/sort/src/external_sort/mod.rs @@ -183,16 +183,16 @@ where let seq_size = seq.get_size(); total_read += seq_size; - // GNU minimum is 16 * (sizeof struct + 2), but GNU uses about - // 1/10 the memory that we do. And GNU even says in the code it may + // GNU minimum is 16 * (sizeof struct + 2), but GNU uses about + // 1/10 the memory that we do. And GNU even says in the code it may // not work on small buffer sizes. - // - // The following seems to work pretty well, and has about the same max + // + // The following seems to work pretty well, and has about the same max // RSS as lower minimum values. - // + // let minimum_buffer_size: u64 = iter_size as u64 * seq_size / 8; - - adjusted_buffer_size = + + adjusted_buffer_size = // Grow buffer size for a struct/Line larger than buffer if adjusted_buffer_size < seq_size { seq_size @@ -233,14 +233,13 @@ where // const MINIMUM_READBACK_BUFFER: u64 = 8200; let right_sized_buffer = adjusted_buffer_size - .checked_div(iter.chunks) - .unwrap_or(adjusted_buffer_size); - iter.max_per_chunk = - if right_sized_buffer > MINIMUM_READBACK_BUFFER { - right_sized_buffer - } else { - MINIMUM_READBACK_BUFFER - }; + .checked_div(iter.chunks) + .unwrap_or(adjusted_buffer_size); + iter.max_per_chunk = if right_sized_buffer > MINIMUM_READBACK_BUFFER { + right_sized_buffer + } else { + MINIMUM_READBACK_BUFFER + }; iter.buffers = vec![VecDeque::new(); iter.chunks as usize]; iter.chunk_offsets = vec![0 as u64; iter.chunks as usize]; for chunk_num in 0..iter.chunks { diff --git a/src/uu/sort/src/sort.rs b/src/uu/sort/src/sort.rs index 53f9a356a..c9e797579 100644 --- a/src/uu/sort/src/sort.rs +++ b/src/uu/sort/src/sort.rs @@ -92,17 +92,12 @@ static THOUSANDS_SEP: char = ','; static NEGATIVE: char = '-'; static POSITIVE: char = '+'; -#[cfg(any( - target_os = "windows", -))] +#[cfg(any(target_os = "windows",))] static DEFAULT_TMPDIR: &str = r"%USERPROFILE%\AppData\Local\Temp"; -#[cfg(not(any( - target_os = "windows", -)))] +#[cfg(not(any(target_os = "windows",)))] static DEFAULT_TMPDIR: &str = r"/tmp"; - static DEFAULT_BUF_SIZE: usize = usize::MAX; #[derive(Eq, Ord, PartialEq, PartialOrd, Clone, Copy)] @@ -1073,9 +1068,9 @@ pub fn uumain(args: impl uucore::Args) -> i32 { settings.tmp_dir = PathBuf::from(result); } else { for (key, value) in env::vars_os() { - if key == OsString::from("TMPDIR") - || key == OsString::from("TEMP") - || key == OsString::from("TMP") + if key == OsString::from("TMPDIR") + || key == OsString::from("TEMP") + || key == OsString::from("TMP") { settings.tmp_dir = PathBuf::from(value); break; @@ -1311,11 +1306,10 @@ fn compare_by(a: &Line, b: &Line, global_settings: &GlobalSettings) -> Ordering (a_str, a_selection.num_cache.as_num_info()), (b_str, b_selection.num_cache.as_num_info()), ), - SortMode::GeneralNumeric => { - general_numeric_compare( - general_f64_parse(&a_str[get_leading_gen(a_str)]), - general_f64_parse(&b_str[get_leading_gen(b_str)]),) - } + SortMode::GeneralNumeric => general_numeric_compare( + general_f64_parse(&a_str[get_leading_gen(a_str)]), + general_f64_parse(&b_str[get_leading_gen(b_str)]), + ), SortMode::Month => month_compare(a_str, b_str), SortMode::Version => version_compare(a_str, b_str), SortMode::Default => default_compare(a_str, b_str), From f3ed5a100fdc301dbf05d94e5042bdc17d39d32c Mon Sep 17 00:00:00 2001 From: electricboogie <32370782+electricboogie@users.noreply.github.com> Date: Mon, 26 Apr 2021 08:54:40 -0500 Subject: [PATCH 54/55] Possible fix to Windows issues, ext_sort bool setting --- src/uu/sort/src/sort.rs | 26 +++++++++----------------- 1 file changed, 9 insertions(+), 17 deletions(-) diff --git a/src/uu/sort/src/sort.rs b/src/uu/sort/src/sort.rs index c9e797579..b6610be64 100644 --- a/src/uu/sort/src/sort.rs +++ b/src/uu/sort/src/sort.rs @@ -32,7 +32,6 @@ use smallvec::SmallVec; use std::cmp::Ordering; use std::collections::BinaryHeap; use std::env; -use std::ffi::OsString; use std::fs::File; use std::hash::{Hash, Hasher}; use std::io::{stdin, stdout, BufRead, BufReader, BufWriter, Lines, Read, Write}; @@ -98,7 +97,7 @@ static DEFAULT_TMPDIR: &str = r"%USERPROFILE%\AppData\Local\Temp"; #[cfg(not(any(target_os = "windows",)))] static DEFAULT_TMPDIR: &str = r"/tmp"; -static DEFAULT_BUF_SIZE: usize = usize::MAX; +static DEFAULT_BUF_SIZE: usize = std::usize::MAX; #[derive(Eq, Ord, PartialEq, PartialOrd, Clone, Copy)] enum SortMode { @@ -132,6 +131,7 @@ struct GlobalSettings { zero_terminated: bool, buffer_size: usize, tmp_dir: PathBuf, + ext_sort: bool, } impl GlobalSettings { @@ -180,6 +180,7 @@ impl Default for GlobalSettings { zero_terminated: false, buffer_size: DEFAULT_BUF_SIZE, tmp_dir: PathBuf::from(DEFAULT_TMPDIR), + ext_sort: false, } } } @@ -328,9 +329,7 @@ impl Line { ); range.shorten(num_range); NumCache::WithInfo(info) - } else if selector.settings.mode == SortMode::GeneralNumeric - && settings.buffer_size == DEFAULT_BUF_SIZE - { + } else if selector.settings.mode == SortMode::GeneralNumeric { let str = range.get_str(&line); NumCache::AsF64(general_f64_parse(&str[get_leading_gen(str)])) } else { @@ -1057,7 +1056,8 @@ pub fn uumain(args: impl uucore::Args) -> i32 { .unwrap_or(format!("{}", DEFAULT_BUF_SIZE)); GlobalSettings::human_numeric_convert(&input) - } + }; + settings.ext_sort = true; } if matches.is_present(OPT_TMP_DIR) { @@ -1066,17 +1066,9 @@ pub fn uumain(args: impl uucore::Args) -> i32 { .map(String::from) .unwrap_or(DEFAULT_TMPDIR.to_owned()); settings.tmp_dir = PathBuf::from(result); + settings.ext_sort = true; } else { - for (key, value) in env::vars_os() { - if key == OsString::from("TMPDIR") - || key == OsString::from("TEMP") - || key == OsString::from("TMP") - { - settings.tmp_dir = PathBuf::from(value); - break; - } - settings.tmp_dir = PathBuf::from(DEFAULT_TMPDIR); - } + settings.tmp_dir = env::temp_dir(); } settings.zero_terminated = matches.is_present(OPT_ZERO_TERMINATED); @@ -1207,7 +1199,7 @@ fn exec(files: Vec, settings: GlobalSettings) -> i32 { // Only use ext_sorter when we need to. // Probably faster that we don't create // an owned value each run - if settings.buffer_size != DEFAULT_BUF_SIZE { + if settings.ext_sort { lines = ext_sort_by(lines, settings.clone()); } else { sort_by(&mut lines, &settings); From ec19bb72d56be25f7cfb795aa61458b029794def Mon Sep 17 00:00:00 2001 From: electricboogie <32370782+electricboogie@users.noreply.github.com> Date: Tue, 27 Apr 2021 15:39:20 -0500 Subject: [PATCH 55/55] Modified to remove 2 unnecessary consts now that we use std::env::temp_dir --- src/uu/sort/src/sort.rs | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/src/uu/sort/src/sort.rs b/src/uu/sort/src/sort.rs index b6610be64..2c67f1063 100644 --- a/src/uu/sort/src/sort.rs +++ b/src/uu/sort/src/sort.rs @@ -91,12 +91,6 @@ static THOUSANDS_SEP: char = ','; static NEGATIVE: char = '-'; static POSITIVE: char = '+'; -#[cfg(any(target_os = "windows",))] -static DEFAULT_TMPDIR: &str = r"%USERPROFILE%\AppData\Local\Temp"; - -#[cfg(not(any(target_os = "windows",)))] -static DEFAULT_TMPDIR: &str = r"/tmp"; - static DEFAULT_BUF_SIZE: usize = std::usize::MAX; #[derive(Eq, Ord, PartialEq, PartialOrd, Clone, Copy)] @@ -179,7 +173,7 @@ impl Default for GlobalSettings { threads: String::new(), zero_terminated: false, buffer_size: DEFAULT_BUF_SIZE, - tmp_dir: PathBuf::from(DEFAULT_TMPDIR), + tmp_dir: PathBuf::new(), ext_sort: false, } } @@ -1064,7 +1058,7 @@ pub fn uumain(args: impl uucore::Args) -> i32 { let result = matches .value_of(OPT_TMP_DIR) .map(String::from) - .unwrap_or(DEFAULT_TMPDIR.to_owned()); + .unwrap_or(format!("{}", env::temp_dir().display())); settings.tmp_dir = PathBuf::from(result); settings.ext_sort = true; } else {