pax_global_header00006660000000000000000000000064132205145500014507gustar00rootroot0000000000000052 comment=7077da9bfefcb59da0eb133bd7c06013a99c05ce Quickcal_Calculator-2.1-7077da9bfefcb59da0eb133bd7c06013a99c05ce/000077500000000000000000000000001322051455000232045ustar00rootroot00000000000000Quickcal_Calculator-2.1-7077da9bfefcb59da0eb133bd7c06013a99c05ce/CHANGELOG.md000066400000000000000000000005371322051455000250220ustar00rootroot00000000000000# Change Log All notable changes to this project will be documented in this file. 2.1 (26-Dec-2017) Features: * Completion of Porting to Python 3.5 ensuring backward compatibility with 2.7 2.0 (16-Dec-2017) Features: * Porting to Python 3.5 ensuring backward compatibility with 2.7 1.0 (22-Nov-2017) Features: * Initial release. Quickcal_Calculator-2.1-7077da9bfefcb59da0eb133bd7c06013a99c05ce/LICENSE000066400000000000000000000011511322051455000242070ustar00rootroot00000000000000# License * GNU General Public License (GPL) 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. Quickcal Copyright (c) Nathan SR num Copyright (c) Joel Parker Henderson / SixArm Quickcal_Calculator-2.1-7077da9bfefcb59da0eb133bd7c06013a99c05ce/num000077500000000000000000003060731322051455000237420ustar00rootroot00000000000000#!/bin/sh set -euf ## # # Num: number utilties for mathematics and statistics. # # Syntax: # # num [ options ] [ file ... ] # # Full documentation and code is available: # # * http://www.numcommand.com # * https://github.com/numcommand/num # # ## Tracking # # Author: Joel Parker Henderson (joel@joelparkerhenderson.com) # License: GPL, BSD, MIT # Created: 2015-03-28 # Updated: 2015-11-29 # Version: 1.2.2 # ## AWK=${AWK:-$(command -v gawk || command -v awk || echo "awk")} "$AWK" -v awk="$AWK" ' ############################################################################ # # num-help.awk # ## function num_help() { print "Num version 1.3.0." print "Update 2015-11-30." print "" print "Copyright (C) 2015 Joel Parker Henderson." print "Please see http://github.com/numcommand" print "" print "Num uses this Awk:" print awk print "" cmd = awk "-Wversion 2>/dev/null || awk --version" print cmd system(cmd) exit } function num_help_init() { num_function_init(\ "help version usage", 0, "Print help, version, usage.", "") } ############################################################################ # # num-out-err.awk # ## ## # # Print a message to stdout. # # Example: # # num_out("hello") # => Print "hello" to STDOUT # function num_out(msg) { print msg } ## # # Print a message to stderr. # # Example: # # num_err("hello") # => Print "hello" to STDERR # # This is purposefully POSIX compatible. # function num_err(msg) { print msg | "cat 1>&2" } ############################################################################ # # num-function.awk # ## ## # # Initialize function metadata for a given function. # # Example: # # function hello() { # print "hello world" # } # # function hello_init() { # num_function_init("hello hi hola", "Print a greeting", "http://example.com/hello.html") # } # # The example creates these: # # num_functions["hello", "names"] = "hello hi hola" # num_functions["hello", "help"] = "Print a greeting" # num_functions["hello", "link"] = "http://example.com/hello.html" # num_synonyms["hello"] = "hello" # num_synonyms["hi"] = "hello" # num_synonyms["hola"] = "hello" # ## function num_function_init(names, arity, help, link, f, i, name, name_list) { split(names, names_arr) f = names_arr[1] num_functions[f, "names"] = names num_functions[f, "arity"] = arity num_functions[f, "help"] = help num_functions[f, "link"] = link for (i in names_arr) { name = names_arr[i] gsub(/_/,"", name) num_synonyms[name] = f } } ############################################################################ # # num-absolute-value.awk # ## ## # # Absolute value. # # Examples: # # abs(1) => 1 # abs(-1) => 1 # ## function num_absolute_value(x) { return (x >= 0) ? x : -x } # Alias function num_abs(x) { return num_absolute_value(x) } ############################################################################ # # num-sign.awk # ## ## # # Return the sign of the value, either 1, -1, or 0. # # Examples: # # sign(8) => 1 # sign(-8) => -1 # sign(0) => 0 # ## function num_sign(x) { return (x > 0) - (x < 0) } ############################################################################ # # num-increment.awk # ## ## # # Increment a value, i.e. add 1. # # Examples: # # increment(1) => 2 # ## function num_increment(x) { return x + 1 } ############################################################################ # # num-round.awk # # We provide four kinds of rounding: # # * round a.k.a. nint. # * round off a.k.a. truncate. # * round up a.k.a. ceiling. # * round down a.k.a. floor. # ## ## # # Round to the nearest integer, a.k.a. nint(). # # Examples: # # num_round(1.9) => 2 # num_round(-1.9) => -2 # # num_nint(1.9) => 2 # num_nint(-1.9) => -2 # ## function num_round(x) { return (x >= 0) ? int(x + 0.5) : int(x - 0.5) } # Alias function num_nint(x) { return num_round(x) } ## # # Round off the fractional part, a.k.a. truncate(). # # Examples: # # num_round_off(1.9) => 1 # num_round_off(-1.9) => -1 # # num_truncate(1.9) => 1 # num_truncate(-1.9) => -1 # ## function num_round_off(x) { return int(x) } # Alias function num_truncate(x) { return num_round_off(x) } ## # # Round up, a.k.a. ceiling(). # # Examples: # # num_round_up(1.9) => 2 # num_round_up(-1.9) => -1 # # num_ceiling(1.9) => 2 # num_ceiling(-1.9) => -1 # function num_round_up(x, y) { y = int(x) return (x == y) ? x : (x >= 0) ? y + 1 : y } # Alias function num_ceiling(x) { return num_round_up(x) } ## # # Round down, a.k.a. floor(). # # Examples: # # num_round_down(1.9) => 1 # num_round_down(-1.9) => -2 # # num_floor(1.9) => 1 # num_floor(-1.9) => -2 # ## function num_round_down(x, y) { y = int(x) return (x == y) ? x : (x >= 0) ? y : y - 1 } # Alias function num_floor(x) { num_round_down(x) } ############################################################################ # # num-sum.awk # ## ## # # Sum, a.k.a. total. # # Example: # # num_sum(1 2 4) => 7 ## function num_sum(arr, i, x) { for (i in arr) x += arr[i] return x } function num_sum_(num, num_, opts, f) { f = "num_sum" if (!(f in num_)) num_[f] = num_sum(num) # TODO optimize linear return num_[f] } function num_sum_init() { num_function_init(\ "num_sum sum total", 0, "Get the sum, a.k.a. total.", "https://en.wikipedia.org/wiki/Summation") } ############################################################################ # # num-product.awk # ## ## # # Product. # # Example: # # num_product(1 2 4) => 8 # ## function num_product(arr, x) { x = 1 for (i in arr) x *= arr[i] return x } function num_product_(num, num_, opts, f) { f = "num_product" if (!(f in num_)) num_[f] = num_product(num) return num_[f] } function num_product_init() { num_function_init(\ "num_product product", 0, "Get the product.", "https://wikipedia.org/wiki/Product_(mathematics)") } ############################################################################ # # num-arr.awk # ## ## # # Dump an array, suitable for debugging. # # Example: # # num_arr_dump(arr) # 1 a # 2 b # 3 d # function num_arr_dump(arr) { for (k in arr) print k, arr[k] } ## # # Is an array empty? # # Example: # # split("", arr) # num_arr_empty(arr) => TRUE # # This is POSIX compatible. # function num_arr_empty(arr, i) { for (i in arr) return FALSE return TRUE } ## # # Length of an array. # # Example: # # num_arr_length(1 2 4) => 3 # # TODO: benchmark this POSIX implementation with # any Gawk implementation, and if Gawk is much faster, # then research a way to use the Gawk implementation. # function num_arr_length(arr, i, len) { for (i in arr) len++ return len } ## # # Swap array items. # # Example: # # arr = 4 5 6 # num_arr_swap(arr, 1, 3) => 6 5 4 # ## function num_arr_swap(A, i, j, t) { t = A[i]; A[i] = A[j]; A[j] = t } ## # # Get the closest value to a target value in an array. # # Example: # # arr = 1 2 4 # target = 2.5 # num_arr_closest_value(arr, target) => 2 # # If multiple values are equidistant to the target, # then return the earliest index. # # TODO optimize when the array is already sorted, # by using quicksort or similar divide-and-conquer. # function num_arr_closest_value(arr, target, _closest_value, _closest_delta, _delta, x, i) { for (i in arr) { _delta = num_abs(arr[i] - target) if (_closest_delta == "" || _delta < _closest_delta) { _closest_value = arr[i] _closest_delta = _delta } } return _closest_value } ## # # Join an array to a string, with a separator string. # # Examples: # # num_arr_join(1 2 3, OFS) => "1 2 3" # num_arr_join(1 2 3, ",") => "1,2,3" # ## function num_arr_join(arr, sep, s, i) { s = "" for (i = 1; i <= num_arr_length(arr); i++) s = s arr[i] sep return substr(s, 1, length(s) - length(sep)) } ## # # Join an array to a string, with a separator string, prefix, and suffix. # # Examples: # # num_arr_join(1 2 3, OFS, "<", ">") => "<1> <2> <4>" # num_arr_join(1 2 3, ",", "(", ")") => "(1),(2),(4)" # ## function num_arr_join_with_prefix_suffix(arr, sep, prefix, suffix, s, i) { s = "" for (i = 1; i <= num_arr_length(arr); i++) s = prefix arr[i] suffix sep return substr(s, 1, length(s) - length(sep)) } ############################################################################ # # num-list.awk # ## ## # # Number of items, a.k.a. count, length. # # Example: # # num_n(1 2 4) => 3 # ## function num_n(arr) { return num_arr_length(arr) } function num_n_(num, num_, opts, f) { f = "n" if (!(f in num_)) num_[f] = num_n(num) return num_[f] } function num_n_init() { num_function_init(\ "num_n n count length size", 0, "Get the number of items, a.k.a. count, length, size.", "https://en.wikipedia.org/wiki/Enumeration") } ## # # First item. # # Example: # # num_first(1 2 4) => 1 # ## function num_first(arr) { return arr[1] } function num_first_(num, num_, opts, f) { f = "num_first" if (!(f in num_)) num_[f] = num_first(num) return num_[f] } function num_first_init() { num_function_init(\ "num_first first head", 0, "Get the first item.", "https://en.wikipedia.org/wiki/Enumeration") } ## # # Last item. # # Example: # # num_last(1 2 4) => 4 # ## function num_last(arr) { return arr[num_arr_length(arr)] } function num_last_(num, num_, opts, f) { f = "num_last" if (!(f in num_)) num_[f] = num_last(num) return num_[f] } function num_last_init() { num_function_init(\ "num_last last tail", "Get the last item.", "https://en.wikipedia.org/wiki/Enumeration") } ## # # Minimum value. # # Example: # # num_min(1 2 4) => 1 # # This implementation does a scan of the entire array. # ## function num_min(arr, _min, i) { _min = "" for (i in arr) { if (_min == "" || arr[i] < _min) { _min = arr[i] } } return _min } function num_min_(num, num_, opts, f) { f = "num_min" if (!(f in num_)) num_[f] = num_min(num) # TODO optimize ascending & descending return num_[f] } function num_min_init() { num_function_init(\ "num_min min minimum least lowest", 0, "Get the minimum value, a.k.a. least, lowest.", "https://en.wikipedia.org/wiki/Maxima_and_minima") } ## # # Maximum value. # # Example: # # num_max(1 2 4) => 4 # # This implementation does a scan of the entire array. # ## function num_max(arr, _max, i) { _max = "" for (i in arr) { if (_max == "" || arr[i] > _max) { _max = arr[i] } } return _max } function num_max_(num, num_, opts, f) { f = "num_max" if (!(f in num_)) num_[f] = num_max(num) # TODO optimize ascending & descending return num_[f] } function num_max_init() { num_function_init(\ "num_max max maximum greatest highest", 0, "Get the maximum value, a.k.a. greatest, highest.", "https://en.wikipedia.org/wiki/Maxima_and_minima") } ## # # Range, a.k.a. spread. # # Example: # # num_range(1 2 4) => 3 # ## function num_range(arr) { return num_max(arr) - num_min(arr) } function num_range_(num, num_, opts, f) { f = "num_range" if (!(f in num_)) num_[f] = num_max_(num, num_, opts) - num_min_(num, num_, opts) return num_[f] } function num_range_init() { num_function_init(\ "num_range range interval breadth spread", 0, "Get the range, a.k.a. interval, breadth, spread.", "https://en.wikipedia.org/wiki/Range_(statistics)") } ## # Filter a list by doing a comparison. # # * eq: equal to # * ne: not equal to # * lt: less than # * le: less than or equal to # * gt: greater than # * ge: greater than or equal to # * in: intra-range, i.e. include [min, max]. # * ex: extra-range, i.e. exclude [min, max]. # # Examples of select: # # num_select_eq(1 2 3 4 5, 3) => 3 # num_select_ne(1 2 3 4 5, 3) => 1 2 4 5 # num_select_lt(1 2 3 4 5, 3) => 1 2 # num_select_le(1 2 3 4 5, 3) => 1 2 3 # num_select_gt(1 2 3 4 5, 3) => 4 5 # num_select_ge(1 2 3 4 5, 3) => 3 4 5 # num_select_in(1 2 3 4 5, 2, 4) => 2 3 4 # num_select_ex(1 2 3 4 5, 2, 4) => 1 5 # # Examples of reject i.e. delete: # # num_reject_eq(1 2 3 4 5, 3) => 1 2 4 5 # num_reject_ne(1 2 3 4 5, 3) => 3 # num_reject_lt(1 2 3 4 5, 3) => 3 4 5 # num_reject_le(1 2 3 4 5, 3) => 4 5 # num_reject_gt(1 2 3 4 5, 3) => 1 2 3 # num_reject_ge(1 2 3 4 5, 3) => 1 2 # num_reject_in(1 2 3 4 5, 2, 4) => 1 5 # num_reject_ex(1 2 3 4 5, 2, 4) => 2 3 4 # ## ## Select EQ function num_select_eq(arr, x, n) { for (i in arr) if (arr[i] == x) { n++ } else { delete arr[i] } return n } function num_select_eq_(num, num_, opts, x, f) { f = "num_select_eq " x if (!(f in num_)) num_[f] = num_["n"] = num_select_eq(num, x) return num_[f] } function num_select_eq_init() { num_function_init(\ "num_select_eq select_eq eq", 0, "Select all values equal to x", "") } ## Select NE function num_select_ne(arr, x, n) { for (i in arr) if (arr[i] != x) { n++ } else { delete arr[i] } return n } function num_select_ne_(num, num_, opts, x, f) { f = "num_select_ne " x if (!(f in num_)) num_[f] = num_["n"] = num_select_ne(num, x) return num_[f] } function num_select_ne_init() { num_function_init(\ "num_select_ne select_ne ne", 0, "Select all values not equal to x", "") } ## Select LT function num_select_lt(arr, x, n) { for (i in arr) if (arr[i] < x) { n++ } else { delete arr[i] } return n } function num_select_lt_(num, num_, opts, x, f) { f = "num_select_lt select_lt lt " x if (!(f in num_)) num_[f] = num_["n"] = num_select_lt(num, x) return num_[f] } function num_select_lt_init() { num_function_init(\ "num_select_lt", 0, "Select all values less than x", "") } ## Select LE function num_select_le(arr, x, n) { for (i in arr) if (arr[i] <= x) { n++ } else { delete arr[i] } return n } function num_select_le_(num, num_, opts, x, f) { f = "num_select_le " x if (!(f in num_)) num_[f] = num_["n"] = num_select_le(num, x) return num_[f] } function num_select_le_init() { num_function_init(\ "num_select_le select_le le", 0, "Select all values less than or equal to x", "") } ## Select GT function num_select_gt(arr, x, n) { for (i in arr) if (arr[i] > x) { n++ } else { delete arr[i] } return n } function num_select_gt_(num, num_, opts, x, f) { f = "num_select_gt select_gt gt" x if (!(f in num_)) num_[f] = num_["n"] = num_select_gt(num, x) return num_[f] } function num_select_gt_init() { num_function_init(\ "num_select_gt", 0, "Select all values greater than x", "") } ## Select GE function num_select_ge(arr, x, n) { for (i in arr) if (arr[i] >= x) { n++ } else { delete arr[i] } return n } function num_select_ge_(num, num_, opts, x, f) { f = "num_select_ge " x if (!(f in num_)) num_[f] = num_["n"] = num_select_ge(num, x) return num_[f] } function num_select_ge_init() { num_function_init(\ "num_select_ge select_ge ge", 0, "Select all values greater than or equal to x", "") } ## Select IN function num_select_in(arr, min, max, n) { for (i in arr) if (arr[i] >= min && arr[i] <= max) { n++ } else { delete arr[i] } return n } function num_select_in_(num, num_, opts, x, f) { f = "num_select_in " x if (!(f in num_)) num_[f] = num_["n"] = num_select_in(num, x) return num_[f] } function num_select_in_init() { num_function_init(\ "num_select_in select_in in", 0, "Select all values intra-range [min, max]", "") } ## Select EX function num_select_ex(arr, min, max) { for (i in arr) if (arr[i] < min || arr[i] > max) { n++ } else { delete arr[i] } return n } function num_select_ex_(num, num_, opts, x, f) { f = "num_select_ex " x if (!(f in num_)) num_[f] = num_["n"] = num_select_ex(num, x) return num_[f] } function num_select_ex_init() { num_function_init(\ "num_select_ex select_ex ex", 0, "Select all values extra-range [min, max]", "") } ## Reject EQ function num_reject_eq(arr, x, n) { for (i in arr) if (arr[i] == x) { delete arr[i] } else { n++ } return n } function num_reject_eq_(num, num_, opts, x, f) { f = "num_reject_eq " x if (!(f in num_)) num_[f] = num_["n"] = num_reject_eq(num, x) return num_[f] } function num_reject_eq_init() { num_function_init(\ "num_reject_eq reject_eq", 0, "Reject all values equal to x", "") } ## Reject NE function num_reject_ne(arr, x, n) { for (i in arr) if (arr[i] != x) { delete arr[i] } else { n++ } return n } function num_reject_ne_(num, num_, opts, x, f) { f = "num_reject_ne " x if (!(f in num_)) num_[f] = num_["n"] = num_reject_ne(num, x) return num_[f] } function num_reject_ne_init() { num_function_init(\ "num_reject_ne reject_ne", 0, "Reject all values not equal to x", "") } ## Reject LT function num_reject_lt(arr, x, n) { for (i in arr) if (arr[i] < x) { delete arr[i] } else { n++ } return n } function num_reject_lt_(num, num_, opts, x, f) { f = "num_reject_lt " x if (!(f in num_)) num_[f] = num_["n"] = num_reject_lt(num, x) return num_[f] } function num_reject_lt_init() { num_function_init(\ "num_reject_lt reject_lt", 0, "Reject all values less than x", "") } ## Reject LE function num_reject_le(arr, x, n) { for (i in arr) if (arr[i] <= x) { delete arr[i] } else { n++ } return n } function num_reject_le_(num, num_, opts, x, f) { f = "num_reject_le " x if (!(f in num_)) num_[f] = num_["n"] = num_reject_le(num, x) return num_[f] } function num_reject_le_init() { num_function_init(\ "num_reject_le reject_le", 0, "Reject all values less than or equal to x", "") } ## Reject GT function num_reject_gt(arr, x, n) { for (i in arr) if (arr[i] > x) { delete arr[i] } else { n++ } return n } function num_reject_gt_(num, num_, opts, x, f) { f = "num_reject_gt " x if (!(f in num_)) num_[f] = num_["n"] = num_reject_gt(num, x) return num_[f] } function num_reject_gt_init() { num_function_init(\ "num_reject_gt reject_gt", 0, "Reject all values greater than x", "") } ## Reject GE function num_reject_ge(arr, x, n) { for (i in arr) if (arr[i] >= x) { delete arr[i] } else { n++ } return n } function num_reject_ge_(num, num_, opts, x, f) { f = "num_reject_ge reject_ge" x if (!(f in num_)) num_[f] = num_["n"] = num_reject_ge(num, x) return num_[f] } function num_reject_ge_init() { num_function_init(\ "num_reject_ge", 0, "Reject all values greater than or equal to x", "") } ## Reject IN function num_reject_in(arr, min, max, n) { for (i in arr) if (arr[i] >= min && arr[i] <= max) { delete arr[i] } else { n++ } return n } function num_reject_in_(num, num_, opts, x, f) { f = "num_reject_in " x if (!(f in num_)) num_[f] = num_["n"] = num_reject_in(num, x) return num_[f] } function num_reject_in_init() { num_function_init(\ "num_reject_in reject_in", 0, "Reject all values intra-range [min, max]", "") } ## Reject EX function num_reject_ex(arr, min, max) { for (i in arr) if (arr[i] < min || arr[i] > max) { delete arr[i] } else { n++ } return n } function num_reject_ex_(num, num_, opts, x, f) { f = "num_reject_ex " x if (!(f in num_)) num_[f] = num_["n"] = num_reject_ex(num, x) return num_[f] } function num_reject_ex_init() { num_function_init(\ "num_reject_ex reject_ex", 0, "Reject all values extra-range [min, max]", "") } ############################################################################ # # num-frequency.awk # ## ## # # Frequency minimum. # # Example: # # num_frequency_min(10 10 11 11 11) => 2 # ## function num_frequency_min(arr, min, i, seen) { for (i in arr) ++seen[arr[i]] return num_min(seen) } function num_frequency_min_(num, num_, opts, f) { f = "num_frequency_min" if (!(f in num_)) num_[f] = num_frequency_min(num) # TODO optimize when min and min are both wanted return num_[f] } function num_frequency_min_init() { num_function_init(\ "num_frequency_min frequency_minimum freq_min", 0, "Get the frequency minimum: count the occurances of each value, and get the minimum count.", "https://en.wikipedia.org/wiki/Frequency_Minimum_(statistics)") } ## # # Frequency maximum. # # Example: # # num_frequency_max(10 10 11 11 11) => 3 # ## function num_frequency_max(arr, max, i, seen) { for (i in arr) ++seen[arr[i]] return num_max(seen) } function num_frequency_max_(num, num_, opts, f) { f = "num_frequency_max" if (!(f in num_)) num_[f] = num_frequency_max(num) # TODO optimize when min and max are both wanted return num_[f] } function num_frequency_max_init() { num_function_init(\ "num_frequency_max frequency_maximum freq_max", 0, "Get the frequency maximum: count the occurances of each value, and get the maximum count.", "https://en.wikipedia.org/wiki/Frequency_Maximum_(statistics)") } ############################################################################ # # num-shift.awk # ## ## # # Shift one item from the head of a list. # # Example: # # arr = 1 2 4 # num_shift(arr, 4) # => 4 # => arr == 1 2 # # Return the item. # function num_shift(arr, item, i) { len = num_arr_length(arr) item = arr[1] for (i = 1; i < len; i++) arr[i] = arr[i+1] delete arr[len] return item } ## # # Unshift one item onto the head of a list. # # Example: # # arr = 1 2 # num_unshift(arr, 4) # => 4 # => arr == 4 1 2 # # Return the item for chainability. # function num_unshift(arr, item, i) { len = num_arr_length(arr) for (i = 1; i < len; i++) arr[i+1] = arr[i] arr[1] = item return item } ############################################################################ # # num-stack.awk # ## ## # # Push one item on an array list. # # Example: # # arr = 1 2 # num_push(arr, 4) # => 4 # => arr == 1 2 4 # # Return the item for chainability. # function num_push(arr, item, i) { i = num_arr_length(arr) + 1 arr[i] = item return item } ## # # Pop one item from an array list. # # Example: # # arr = 1 2 4 # num_pop(arr) # => 4 # => arr == 1 2 # # Return the item. # function num_pop(arr, item, i) { i = num_arr_length(arr) item = arr[i] delete arr[i] return item } ############################################################################ # # num-queue.awk # ## ## # # Enqueue one item to an array queue. # # Example: # # arr = 1 2 # num_enqueue(arr, 4) # => 4 # => arr == 1 2 4 # # Return the item for chainability. # function num_enqueue(arr, item, i) { i = num_arr_length(arr) + 1 arr[i] = item return item } ## # # Dequeue one item from an array queue. # # Example: # # arr = 1 2 4 # num_dequeue(arr) # => 1 # => arr == 2 4 # # Return the item. # function num_dequeue(arr, item, i) { return num_shift(arr) } ############################################################################ # # num-sort.awk # # Caution: This implementation requires the `asort` function, # which we believe is available in current `gawk` implementations, # but may not be POSIX-compliant. This calls the C qsort library. # # TODO: Research if `asort` is POSIX or if there are alternatives. # # TODO: Research if `asort` has more flexibility that we can use. # # TODO: Consider using Timsort, which tends to be faster for real # world data that may already be sorted or partially sorted. # ## ## # # Initialize. # # This tracks which metadata fields to preserve during a sort. # For example, a sort will not affect the sum of the numbers, # so the metadata can continue to cache the sum throughout a sort. # ## function num_sort_awk_init() { #TODO refactor NUM_SORT_MEMO["n"] = \ NUM_SORT_MEMO["num_sum"] = \ NUM_SORT_MEMO["num_mean"] = \ NUM_SORT_MEMO["num_variance"] = \ NUM_SORT_MEMO["num_sample_variance"] = \ NUM_SORT_MEMO["num_population_variance"] = \ NUM_SORT_MEMO["num_skewness"] = \ NUM_SORT_MEMO["num_sample_skewness"] = \ NUM_SORT_MEMO["num_population_skewness"] = \ NUM_SORT_MEMO["num_kurtosis"] = \ NUM_SORT_MEMO["num_sample_kurtosis"] = \ NUM_SORT_MEMO["num_population_kurtosis"] = \ NUM_SORT_MEMO["num_standard_deviation"] = \ NUM_SORT_MEMO["num_sample_standard_deviation"] = \ NUM_SORT_MEMO["num_population_standard_deviation"] = \ NUM_SORT_MEMO["num_sum_of_squares"] = \ NUM_SORT_MEMO["num_sum_of_cubes"] = \ NUM_SORT_MEMO["num_sum_of_quads"] = \ TRUE } ## # # Remember metadata, then restore it. # ## function num_sort_before_(num, num_, opts, memo) { split("", memo) for (k in NUM_SORT_MEMO) if (k in num_) memo[k] = num_[k] } function num_sort_after_(num, num_, opts, memo) { split("", num_) for (k in NUM_SORT_MEMO) if (k in memo) num_[k] = memo[k] } ## # # Sort ascending in place. # # Example: # # num_sort_ascending(3 1 2) => 1 2 3 # ## function num_sort_ascending(arr) { if (AWK_HAS_ASORT) { asort(arr, arr, "@val_num_asc") } else { num_err("Num needs a function for sort ascending. We expect to add a solution in Num version 2.") } } function num_sort_ascending_(num, num_, opts, f, memo) { f = "num_sort_ascending" if (num_[f] != TRUE) { num_sort_before_(num, num_, opts, memo) num_sort_ascending(num) num_sort_after_(num, num_, opts, memo) num_[f] = TRUE num_["ascending"] = TRUE num_["strictly_descending"] = FALSE } } function num_sort_ascending_init() { num_function_init(\ "num_sort_ascending sort_ascending sort_asc sort_up sort", 0, "Sort the values in ascending order.", "https://wikipedia.org/wiki/Sorting_algorithm") } ## # # Sort descending in place. # # Example: # # num_sort_descending(3 1 2) => 3 2 1 # ## function num_sort_descending(arr) { if (AWK_HAS_ASORT) { asort(arr, arr, "@val_num_desc") } else { num_err("Num needs a function for sort descending. We expect to add a solution in Num version 2.") } } function num_sort_descending_(num, num_, opts, f, memo) { f = "num_sort_descending" if (num_[f] != TRUE) { num_sort_before_(num, num_, opts, memo) num_sort_descending(num) num_sort_after_(num, num_, opts, memo) num_[f] = TRUE num_["descending"] = TRUE num_["strictly_ascending"] = FALSE } } function num_sort_descending_init() { num_function_init(\ "num_sort_descending sort_descending sort_desc sort_down", 0, "Sort the values in descending order.", "https://wikipedia.org/wiki/Sorting_algorithm") } ############################################################################ # # num-sort.awk booleans # ## ## # # Is the list sorted in ascending order? # # Examples: # # 1 2 3 => TRUE # 3 2 1 => FALSE # 2 2 2 => TRUE # # A.k.a. non-descending. # # Return TRUE iff each successive number is greater or equal. # function num_is_ascending_(num, num_, opts, f, x, i, flag) { f = "num_ascending" if (!(f in num_)) { if (num_["strictly_ascending"] == TRUE) { num_[f] = TRUE } else if (num_["descending"] == TRUE || num_["strictly_descending"] == TRUE) { num_[f] = FALSE } else { flag = TRUE for (i in num) { if (x == "" || num[i] >= x) { x = num[i] } else { flag = FALSE break } } if (flag == TRUE) { num_["strictly_descending"] = FALSE } num_[f] = flag } } return num_[f] } function num_is_ascending_init() { num_function_init(\ "num_is_ascending is_ascending is_asc", 0, "Is the list sorted in ascending order?", "https://wikipedia.org/wiki/Sorting_algorithm") } ## # # Is the list sorted in strictly ascending order? # # Examples: # # 1 2 3 => TRUE # 3 2 1 => FALSE # 2 2 2 => FALSE # # This is TRUE iff each successive number is greater. # function num_is_strictly_ascending_(num, num_, opts, f, x, i, flag) { f = "num_strictly_ascending" if (!(f in num_)) { if (num_["descending"] == TRUE || num_["strictly_descending"] == TRUE) { num_[f] = FALSE } else { flag = TRUE for (i in num) { if (x == "" || num[i] > x) { x = num[i] } else { flag = FALSE break } } if (flag == TRUE) { num_["ascending"] = TRUE num_["desending"] = num_["strictly_desending"] = FALSE } num_[f] = flag } } return num_[f] } function num_is_strictly_ascending_init() { num_function_init(\ "num_is_strictly_ascending is_strictly_ascending is_strict_asc", 0, "Is the list sorted in strictly ascending order?", "https://wikipedia.org/wiki/Sorting_algorithm") } ## # # Is the list sorted in descending order? # # Examples: # # 3 2 1 => TRUE # 1 2 3 => FALSE # 2 2 2 => TRUE # # A.k.a. non-ascending. # # Return TRUE when each successive number is lesser or equal. # function num_is_descending_(num, num_, opts, f, x, i, flag) { f = "num_descending" if (!(f in num_)) { if (num_["strictly_descending"] == TRUE) { num_[f] = TRUE } else if (num_["ascending"] == TRUE || num_["strictly_ascending"] == TRUE) { num_[f] = FALSE } else { flag = TRUE for (i in num) { if (x == "" || num[i] <= x) { x = num[i] } else { flag = FALSE break } } if (flag == TRUE) { num_["strictly_ascending"] = FALSE } num_[f] = flag } } return num_[f] } function num_is_descending_init() { num_function_init(\ "num_is_descending is_descending is_desc", 0, "Is the list sorted in descending order?", "https://wikipedia.org/wiki/Sorting_algorithm") } ## # # Is the list sorted in strictly descending order? # # Examples: # # 3 2 1 => TRUE # 1 2 3 => FALSE # 2 2 2 => FALSE # # Return TRUE when each successive number is lesser. # function num_is_strictly_descending_(num, num_, f, x, i, flag) { f = "num_strictly_descending" if (!(f in num_)) { if ("ascending" in num) { num_[f] = ! num_["ascending"] } else { flag = TRUE for (i in num) { if (x == "" || num[i] < x) { x = num[i] } else { flag = FALSE break } } if (flag == TRUE) { num_["ascending"] = num_["strictly_ascending"] = FALSE num_["desending"] = TRUE } num_[f] = flag } } return num_[f] } function num_is_strictly_descending_init() { num_function_init(\ "num_is_strictly_descending is_strictly_descending is_strict_desc", 0, "Is the list sorted in strictly descending order?", "https://wikipedia.org/wiki/Sorting_algorithm") } ############################################################################ # # num-insertion-sort.awk # ## ## # # Insertion sort. # # This implementation is a slightly faster version that moves A[i] # to its position in one go and only performs one assignment in the # inner loop body. # # Thanks to https://en.wikipedia.org/wiki/Insertion_sort # ## function num_insertion_sort(A) { num_insertion_sort_slice(A, 1, num_arr_length(A)) } function num_insertion_sort_slice(A, lo, hi, i, j, x) { if (lo >= hi) return for (i = lo + 1; i <= hi; i++) { x = A[i] j = i while (j > lo && A[j-1] > x) { A[j] = A[j-1] j-- } A[j] = x } } ############################################################################ # # num-quicksort.awk # ## function num_quicksort_awk_init() { NUM_QUICKSORT_INSERTION_SORT_THRESHOLD = 12 } ## # # Quicksort. # # Quicksort selects a pivot and divides the data into values above and # below the pivot. Sorting then recurses on these sub-lists. # # From http://awk.info/?doc/quicksort.html # # TODO: research implementing the pivot by using Tukeys ninther, # which is a median of medians, and may be a faster heuristic. # See http://www.johndcook.com/blog/2009/06/23/tukey-median-ninther/ # # TODO: research implementing the small size sort using Shell sort, # which is similar to insertion sort yet better for typical data. # See https://en.wikipedia.org/wiki/Shellsort # # TODO: research upgrading from single pivot to dual pivot. # http://stackoverflow.com/questions/20917617/whats-the-difference-of-dual-pivot-quick-sort-and-quick-sort # http://stackoverflow.com/questions/32001841/how-to-implement-dual-pivot-quicksort-in-python # http://hg.openjdk.java.net/jdk8/jdk8/jdk/file/tip/src/share/classes/java/util/DualPivotQuicksort.java # ## function num_quicksort(A) { num_quicksort_slice(A, 1, num_arr_length(A)) } function num_quicksort_slice(A, lo, hi, pivot_index) { if (lo >= hi) return if (hi - lo < NUM_QUICKSORT_INSERTION_SORT_THRESHOLD) { num_insertion_sort_slice(A, lo, hi) return } pivot_index = num_quicksort_pivot_index_via_median_of_three_sort_slice(A, lo, hi) pivot_index = num_partition_slice(A, lo, hi, pivot_index) num_quicksort_slice(A, lo, pivot_index - 1) num_quicksort_slice(A, pivot_index + 1, hi) } ## # # Partition an array slice using a given pivot index. # # Return the new pivot index. # ## function num_partition_slice(A, lo, hi, pivot_index, left, right, pivot_value, t) { if (lo >= hi) return lo left = lo right = hi pivot_value = A[pivot_index] num_arr_swap(A, left, pivot_index) while (left < right) { while (left <= hi && A[left] <= pivot_value) left++ while (right >= lo && A[right] > pivot_value) right-- if (left < right) num_arr_swap(A, left, right) } pivot_index = right num_arr_swap(A, lo, pivot_index) return pivot_index } ## # # Choose a quicksort pivot index by using a random number. # This is a naive implemenation and is here for benchmarking. # Typically you will never need this function for real-world data. # ## function num_quicksort_pivot_index_via_rand(A, lo, hi) { return lo + int((hi - lo + 1) * rand()) } ## # # Choose a quicksort pivot index by using the "median of three" heuristic # with a swap sort of the three items for efficiency on the next pivot. # # Compared to picking the pivot randomly, the median of three heuristic: # # * Ensures that a common case of fully sorted data remains optimal. # * Is more difficult for an attacker to manipulate into the worst case. # * Is often faster than a PRNG, which is often relatively slow. # # The median of three looks at the first, middle and last elements of # the array, and choose the median of those as the pivot. # # To get the "full effect" of the median of three, it is also important # to sort those three items, not just use the median as the pivot -- # this does not affect what is chosen as the pivot in the current # iteration, but can/will affect what is used as the pivot in the next # recursive call, which helps to limit the bad behavior for a few # initial orderings (one that turns out to be particularly bad in many # cases is an array that is sorted, except for having the smallest # element at the high end of the array (or largest element at the low # end)). # # Thanks to http://stackoverflow.com/questions/7559608/median-of-three-values-strategy # # To calculate the midpoint, we prefer (lo+(hi−lo)/2) instead of the naive # simpler ((hi+lo)/2) because the former does not risk integer overflow. # # Return the pivot index. # ## function num_quicksort_pivot_index_via_median_of_three_sort(A) { num_quicksort_pivot_index_via_median_of_three_sort_slice(A, 1, num_arr_length(A)) } function num_quicksort_pivot_index_via_median_of_three_sort_slice(A, lo, hi, mid) { if (lo == hi) return lo mid = lo + int((hi - lo) / 2) if (A[hi] < A[lo]) num_arr_swap(A, lo, hi) if (A[mid] < A[lo]) num_arr_swap(A, mid, lo) if (A[hi] < A[mid]) num_arr_swap(A, hi, mid) return mid } ############################################################################ # # num-map.awk # # Map helpers. # # Call `map_before` to remember the number of items. # # Call `map_after` to invalidate all metadata then # restore the number of items. # ## function map_before_(num, num_, opts, memo) { memo["n"] = num_["n"] } function map_after_(num, num_, opts, memo) { split("",num_) num_["n"] = memo["n"] } ############################################################################ # # num-unique.awk # ## ## # # Is the list all unique items? # # Examples: # # 1 2 3 => TRUE # 1 2 2 => FALSE # function num_is_unique_(num, num_, opts, f, i, seen, flag) { f = "num_unique" if (!(f in num_)) { flag = TRUE split("", seen) for (i in num) { if (num[i] in seen) { flag = FALSE break } else { seen[num[i]] = TRUE } } num_[f] = flag } return num_[f] } function num_is_unique_init() { num_function_init(\ "num_is_unique is_unique is_uniq", 0, "Is the list all unique items?", "https://en.wikipedia.org/wiki/Uniqueness_quantification") } ############################################################################ # # num-map-increment.awk # ## ## # # Map increment. # # Example: # # num_map_increment_(1 2 3) => -2 3 4 # ## function num_map_increment(arr) { for (i in arr) arr[i] = num_increment(arr[i]) } function num_map_increment_(num, num_, opts, f, i, memo) { f = "num_map_increment" if (num_[f] != TRUE) { map_before_(num, num_, opts, memo) num_map_increment(num) map_after_(num, num_, opts, memo) num_[f] = TRUE } return "" } function num_map_increment_init() { num_function_init(\ "num_map_increment increment", 0, "Map using increment.", "https://en.wikipedia.org/wiki/Increment_and_decrement_operators") } ############################################################################ # # num-map-absolute-value.awk # ## ## # # Map absolute value. # # Example: # # num_map_absolute_value_(1 -2 3) => 1 2 3 # ## function num_map_absolute_value(arr) { for (i in arr) arr[i] = num_absolute_value(arr[i]) } function num_map_absolute_value_(num, num_, opts, f, i, memo) { f = "num_map_absolute_value" if (num_[f] != TRUE) { map_before_(num, num_, opts, memo) num_map_absolute_value(num) map_after_(num, num_, opts, memo) num_[f] = TRUE } return "" } function num_map_absolute_value_init() { num_function_init(\ "num_map_absolute_value absolute_value abs magnitude", 0, "Map using absolute value.", "https://en.wikipedia.org/wiki/Absolute_value_(algebra)") } # Alias function num_map_abs(arr) { return num_map_absolute_value(arr) } function num_map_abs_(num, num_, opts) { return num_map_absolute_value_(num, num_, opts) } ############################################################################ # # num-map-sign.awk # ## ## # # Map sign. # # Example: # # num_map_sign_(-8 0 8) => -1 0 1 # ## function num_map_sign(arr) { for (i in arr) arr[i] = num_sign(arr[i]) } function num_map_sign_(num, num_, opts, f, i, memo) { f = "num_map_sign" if (num_[f] != TRUE) { map_before_(num, num_, opts, memo) num_map_sign(num) map_after_(num, num_, opts, memo) num_[f] = TRUE } return "" } function num_map_sign_init() { num_function_init(\ "num_map_sign sign sgn signum", 0, "Map using sign.", "https://en.wikipedia.org/wiki/Sign_function") } ############################################################################ # # num-map-round.awk # ## ## # # Initialize. # ## function num_map_round_awk_init() { #TODO refactor NUM_MAP_ROUND_MEMO["n"] = \ NUM_MAP_ROUND_MEMO["sorted"] = \ NUM_MAP_ROUND_MEMO["ascending"] = \ NUM_MAP_ROUND_MEMO["descending"] = \ TRUE } ## # # Map round, a.k.a. round towards nearest integer, nint. # # Example: # # num_map_round(-1.9 1.9) => -2 2 # ## function num_map_round(arr) { for (i in arr) arr[i] = num_round(arr[i]) } function num_map_round_(num, num_, opts, f, memo, i) { if (num_["integer"] == TRUE) return f = "num_map_round" if (num_[f] != TRUE) { num_map_round_before_(num, num_, opts, f, memo) num_map_round(num) num_map_round_after_(num, num_, opts, f, memo) } return "" } function num_map_round_init() { num_function_init(\ "num_map_round round round_towards_nearest nearest_integer n_int", 0, "Map using round, a.k.a. round towards nearest, nint.", "https://en.wikipedia.org/wiki/Rounding") } # Alias function num_map_nint(arr) { return num_map_round(arr) } # Alias function num_map_nint_(num, num_, opts) { return num_map_round_(num, num_, opts) } ## # # Map: round off, a.k.a. round towards zero, truncate. # # Example: # # num_map_round_off(-1.9 1.9) => -1 1 # ## function num_map_round_off(arr) { for (i in arr) arr[i] = num_round_off(arr[i]) } function num_map_round_off_(num, num_, opts, f, memo) { if (num_["integer"] == TRUE) return "" f = "num_map_round_off" if (num_[f] != TRUE) { num_map_round_before_(num, num_, opts, f, memo) num_map_round_off(num) num_map_round_after_(num, num_, opts, f, memo) } return "" } function num_map_round_off_init() { num_function_init(\ "num_map_round_off round_off round_towards_zero truncate", 0, "Map using round off, a.k.a. round towards zero, truncate.", "https://en.wikipedia.org/wiki/Rounding") } # Alias function num_map_truncate(arr) { return num_map_round_off(arr) } # Alias function num_map_truncate_(num, num_, opts) { return num_map_round_off_(num, num_, opts) } ## # # Map: round up, a.k.a. round towards positive infinity, ceiling. # # Example: # # num_map_round_up(-1.9 1.9) => -1 2 # ## function num_map_round_up(arr) { for (i in arr) arr[i] = num_round_up(arr[i]) } function num_map_round_up_(num, num_, opts, f, memo) { if (num_["integer"] == TRUE) return "" f = "num_map_round_up" if (num_[f] != TRUE) { num_map_round_before_(num, num_, opts, f, memo) num_map_round_up(num) num_map_round_after_(num, num_, opts, f, memo) } return "" } function num_map_round_up_init() { num_function_init(\ "num_map_round_up round_up ceiling", 0, "Map using round up, a.k.a. round towards positive infinity, ceiling.", "https://en.wikipedia.org/wiki/Rounding") } # Alias function num_map_ceiling(arr) { return num_map_round_up(arr) } # Alias function num_map_ceiling_(num, num_, opts) { return num_map_round_up_(num, num_, opts) } ## # # Map: round down, a.k.a. round towards negative infinity, floor. # # Example: # # num_map_round_down(-1.9 1.9) => -2 1 # ## function num_map_round_down(arr) { for (i in arr) arr[i] = num_round_down(arr[i]) } function num_map_round_down_(num, num_, opts, f, memo) { if (num_["integer"] == TRUE) return "" f = "num_map_round_down" if (num_[f] != TRUE) { num_map_round_before_(num, num_, opts, f, memo) num_map_round_down(num) num_map_round_after_(num, num_, opts, f, memo) } return "" } function num_map_round_down_init() { num_function_init(\ "num_map_round_down round_down floor", 0, "Map using round down, a.k.a. round towards negative infinity, floor.", "https://en.wikipedia.org/wiki/Rounding") } # Alias function num_map_floor(arr) { return num_map_round_down(arr) } # Alias function num_map_floor_(num, num_, opts) { return num_map_round_down_(num, num_, opts) } ## # # Map round after helper: call this function only from within # each of the rounding functions, before the work begins. # This function saves as much metadata as possible. # ## function num_map_round_before_(num, num_, opts, f, memo) { for (k in NUM_MAP_ROUND_MEMO) if (k in num_) memo[k] = num_[k] } ## # # Map round after helper: call this function only from within # each of the rounding functions, after the work ends. # This function restores as much metadata as possible. # ## function num_map_round_after_(num, num_, opts, f, memo) { split("",num_) for (k in NUM_MAP_ROUND_MEMO) if (k in memo) num_[k] = memo[k] num_[f] = TRUE num_["integer"] = TRUE } ############################################################################ # # num-map-normalize.awk # ## ## # # Map: normalize each value to be 0 to 1. # # Example: # # num_map_normalize(1 2 4) => 0 0.33333 1 # ## function num_map_normalize_with_min_max(arr, min_old, max_old, min_new, max_new) { multiply = (max_new - min_new) / (max_old - min_old) add = min_new - (multiply * min_old) for (i in arr) arr[i] = arr[i] * multiply + add } function num_map_normalize(arr) { return num_map_normalize_with_min_max(arr, num_min(arr), num_max(arr), 0, 1) } function num_map_normalize_(num, num_, opts, f, min_old, max_old, min_new, max_new, multiply, add) { f = "num_map_normalize" if (num_[f] != TRUE) { map_before_(num, num_, opts, memo) x = num_map_normalize_with_min_max(num, num_min_(num, num_, opts), num_max_(num, num_, opts), 0, 1) map_after_(num, num_, opts, memo) num_[f] = TRUE } return "" } function num_map_normalize_init() { num_function_init(\ "num_map_normalize normalize norm", 0, "Map using normalize.", "https://wikipedia.org/wiki/Normalization_(statistics)") } ############################################################################ # # num-mean.awk # ## ## # # Mean, a.k.a. arithmetic mean, average. # # Example: # # num_mean(1 2 4) => 2.33333 # ## function num_mean(arr) { return num_sum(arr) / num_n(arr) } function num_mean_(num, num_, opts, f, _n, _min, _max, _sum) { f = "num_mean" if (!(f in num_)) { if (num_["linear"]) { _n = num_n_(num, num_, opts) _min = num_min_(num, num_, opts) _max = num_max_(num, num_, opts) num_[f] = _min + (_max - _min) / _n } else { _n = num_n_(num, num_, opts) _sum = num_sum_(num, num_, opts) num_[f] = _sum / _n } } return num_[f] } function num_mean_init() { num_function_init(\ "num_mean mean average avg", 0, "Get the mean, a.k.a artihmetic mean, average.", "https://en.wikipedia.org/wiki/Mean") } ############################################################################ # # num-mean-absolute-deviation.awk # ## ## # # Mean absolute deviation. # # The average distance between each value and the mean. # # Example: # # num_mean_absolute_deviation(1 2 4) => 1.11111 ## function num_mean_absolute_deviation(arr, _mean, _n, x) { _mean = num_mean(arr) _n = num_n(arr) for (i in arr) x += num_absolute_value(arr[i] - _mean) return x / _n } function num_mean_absolute_deviation_(num, num_, opts, f, _n, _mean, i, x) { f = "num_mean_absolute_deviation" if (!(f in num_)) { _n = num_n_(num, num_, opts) _mean = num_mean_(num, num_, opts) for (i in num) x += num_absolute_value(num[i] - _mean) num_[f] = x / _n } return num_[f] } function num_mean_absolute_deviation_init() { num_function_init(\ "num_mean_absolute_deviation mean_absolute_deviation mad", 0, "Get the average distance between each value and the mean.", "https://en.wikipedia.org/wiki/Average_absolute_deviation") } # Alias function num_mad(arr) { return num_mean_absolute_deviation(arr) } function num_mad_(num, num_, opts) { return num_mean_absolute_deviation_(num, num_, opts) } ############################################################################ # # num-sum-of-mean-deviation.awk # ## ## # # Sum of mean deviation exp. # # Example: # # arr = 1 2 4 # exponent = 3 # sum_of_mean_deviation_exp(arr, exponent) => sum of cubes # # Typically useful to calculate variance, skewness, kurtosis. # ## function num_sum_of_mean_deviation_exp(arr, mean, exponent, i, x) { for (i in arr) x += (arr[i] - mean) ^ exponent return x } function num_sum_of_mean_deviation_exp_(num, num_, opts, mean, exponent) { f = "num_sum_of_mean_deviation_exp" ":mean:" mean ":exponent:" exponent if (!(f in num_)) num_[f] = num_sum_of_mean_deviation_exp(num, mean, exponent) return num_[f] } function num_sum_of_mean_deviation_exp_init() { num_function_init(\ "num_sum_of_mean_deviation_exp", 0, "Get the sum of mean deviation for a given exponent.", "https://en.wikipedia.org/wiki/Deviation_(statistics)") } ## # # Sum of Squares, a.k.a. the sum of each deviation to the power of 2, a.k.a. SS. # # Example: # # num_sum_of_squares(1 2 4) => 4.66667 # ## function num_sum_of_squares(arr) { return num_sum_of_mean_deviation_exp(arr, num_mean(arr), 2) } function num_sum_of_squares_(num, num_, opts) { f = "num_sum_of_squares" if (!(f in num_)) num_[f] = num_sum_of_mean_deviation_exp_(num, num_, opts, num_mean_(num, num_, opts), 2) return num_[f] } function num_sum_of_squares_init() { num_function_init(\ "num_sum_of_squares sum_of_squares sum_squares ss mean_squared_error mse", 0, "Get the sum of squares, a.k.a. sum of each mean deviation to the power of 2, a.k.a. SS", "https://en.wikipedia.org/wiki/Deviation_(statistics)") } ## # # Sum of Cubes, a.k.a. sum of each mean deviation to the power of 3. # # Example: # # 1 2 4 => 2.22222 # ## function num_sum_of_cubes(arr) { return num_sum_of_mean_deviation_exp(arr, num_mean(arr), 3) } function num_sum_of_cubes_(num, num_, opts) { f = "num_sum_of_cubes" if (!(f in num_)) num_[f] = num_sum_of_mean_deviation_exp_(num, num_, opts, num_mean_(num, num_, opts), 3) return num_[f] } function num_sum_of_cubes_init() { num_function_init(\ "num_sum_of_cubes sum_of_cubes sum_cubes", 0, "Get the sum of cubes, a.k.a. sum of each mean deviation to the power of 3.", "https://en.wikipedia.org/wiki/Mean_squared_error") } ## # # Sum of Quads, a.k.a. sum of each mean deviation to the power of 4. # # Example: # # 1 2 4 => TODO # ## function num_sum_of_quads(arr) { return num_sum_of_mean_deviation_exp(arr, num_mean(arr), 4) } function num_sum_of_quads_(num, num_, opts) { f = "num_sum_of_quads" if (!(f in num_)) num_[f] = num_sum_of_mean_deviation_exp_(num, num_, opts, num_mean_(num, num_, opts), 4) return num_[f] } function num_sum_of_quads_init() { num_function_init(\ "num_sum_of_quads sum_of_quads sum_quads", 0, "Get the sum of quads, a.k.a. sum of each mean deviation to the power of 4.", "https://en.wikipedia.org/wiki/Mean_squared_error") } ############################################################################ # # num-meanest.awk # ## ## # # Meanest, i.e. the value closest to the mean. # # Example: # # num_meanest(1 2 4) => 2 # ## function num_meanest(arr) { return num_arr_closest_value(arr, num_mean(arr)) } function num_meanest_(num, num_, opts, f, _n, _mean, i, x) { f = "num_meanest" if (!(f in num_)) num_[f] = num_arr_closest_value(num, num_mean_(num, num_, opts)) return num_[f] } function num_meanest_init() { num_function_init(\ "num_meanest meanest", 0, "Get the value that is closest to the mean.", "https://en.wikipedia.org/wiki/Mean") } ############################################################################ # # num-trimean.awk # ## ## # # Trimean. # # Example: # # num_trimean(1 1.75 3 27.75 99) => 8.875 # # Requirement: the array is sorted. # ## function num_trimean(arr, _q1, _q2, _q3) { _q1 = num_quartile_1(arr) _q2 = num_quartile_2(arr) _q3 = num_quartile_3(arr) return (_q1 + _q2 + _q2 + _q3) / 4 } function num_trimean_(num, num_, opts, f, _q1, _q2, _q3) { f = "num_trimean" if (!(f in num_)) { _q1 = num_quartile_1_(num, num_, opts) _q2 = num_quartile_2_(num, num_, opts) _q3 = num_quartile_3_(num, num_, opts) num_[f] = (_q1 + _q2 + _q2 + _q3) / 4 } return num_[f] } function num_trimean_init() { num_function_init(\ "num_trimean trimean", 0, "Calculate the trimean.", "https://wikipedia.org/wiki/Trimean") } ############################################################################ # # num-trimmed-mean.awk # ## ## # # Trimmed mean. This implemention deletes values that are not in the IQR. # # Example: # # num_trimmed_mean(1 2 3 4 5) => 2 3 4 # # TODO: implement, and also upgrade to enable custom ranges. # ## function num_trimmed_mean_min_max(arr, q1, q3) { q1 = num_quartile_1(arr) q3 = num_quartile_3(arr) return TODO } function num_trimmed_mean_(num, num_, opts, f) { f = "num_trimmed_mean" if (!(f in num_)) { q1 = num_quartile_1_(num, num_, opts) q3 = num_quartile_3_(num, num_, opts) num_[f] = TODO } return num_[f] } function num_trimmed_mean_init() { num_function_init(\ "num_trimmed_mean trimmed_mean truncated_mean", 0, "Calculate the trimmed mean", "https://en.wikipedia.org/wiki/Truncated_Mean") } ############################################################################ # # num-median.awk # ## ## # # Median of an array slice. # # Example: # # num_median_slice((1 2 4), 1, 3) => 2 # num_median_slice((1 2 4 9 9), 1, 3) => 3 # ## function num_median_of_slice(arr, start, stop, _n, i) { _n = 1 + stop - start if (_n % 2) { i = (start - 1) + ((_n + 1) / 2) return arr[i] } else { i = (start - 1) + (_n / 2) return (arr[i] + arr[i+1]) / 2 } } ## # # Median. # # Example: # # num_median(1 2 4) => 2 # num_median(1 2 4 99) => 3 # # Requirement: the array is sorted. # ## function num_median(arr, _n, i) { _n = num_n(arr) if (_n % 2) { i = (_n + 1) / 2 return arr[i] } else { i = _n / 2 return (arr[i] + arr[i+1]) / 2.0 } } function num_median_(num, num_, opts, f, i, _n) { f = "num_median" if (!(f in num_)) { _n = num_n_(num, num_, opts) num_sort_ascending_(num, num_, opts) if (_n % 2) { i = (_n + 1) / 2 num_[f] = num_["num_median_low"] = num_["num_median_high"] = num[i] } else { i = _n / 2 num_["num_median_low"] = num[i] num_["num_median_high"] = num[i+1] num_[f] = (num[i] + num[i+1]) / 2.0 } } return num_[f] } function num_median_init() { num_function_init(\ "num_median median med", 0, "Get the median.", "https://en.wikipedia.org/wiki/Median") } ## # # Median low: get the lesser median. # # Example: # # num_median_low(1 2 4) => 2 # num_median_low(1 2 4 99) => 2 # function num_median_low(arr, _n, i) { _n = num_n(arr) if (_n % 2) { i = (_n + 1) / 2 } else { i = _n / 2 } return arr[i] } function num_median_low_(num, num_, opts, f, _n) { f = "num_median_low" if (!(f in num_)) { num_median_(num, num_, opts) # n.b. median sets median_low } return num_[f] } function num_median_low_init() { num_function_init(\ "num_median_low median_low med_low", 0, "Get the median that is lower a.k.a. lesser.", "https://en.wikipedia.org/wiki/Median") } ## # # Median high: get the greater median. # # Example: # # num_median_high(1 2 4) => 2 # num_median_high(1 2 4 99) => 4 # ## function num_median_high(arr, _n, i) { _n = num_n(arr) if (_n % 2) { i = (_n + 1) / 2 } else { i = (_n / 2) + 1 } return arr[i] } function num_median_high_(num, num_, opts, f, _n) { f = "num_median_high" if (!(f in num_)) { num_median_(num, num_, opts) # n.b. median sets median_high } return num_[f] } function num_median_high_init() { num_function_init(\ "num_median_high median_high med_high", 0, "Get the median that is higher a.k.a. greater.", "https://en.wikipedia.org/wiki/Median") } ############################################################################ # # num-mode.awk # ## ## # # Modes: get the modes, which may be a number, or list, or UNDEF. # # The modes are: # # * The value that appears most often in a set of data. # * If mutlipe values appear as often, there are multiple modes. # * If each value occurs only once, then there are no modes. # # Examples: # # 1 2 2 3 => 2 # 1 1 2 3 3 => 1 3 # 1 2 3 => UNDEF # # Output the `modes` array. # Return the `modes` array length. # # Examples: # # num_modes(1 2 3, modes) => 0, modes == [] # num_modes(1 2 2 3, modes) => 1, modes == [2] # num_modes(1 1 2 3 3, modes) => 2, modes == [1, 3] # ## function num_modes(arr, modes, modes_i, i, n, seen, max) { for (i in arr) { # Optimization: use one scan n = ++seen[arr[i]] if (max == "" || max < n) max = n } split("", out); out_i = 0 if (max > 1) { for (i in seen) { if (seen[i] == max) { out[++out_i] = i } } } return out_i } function num_modes_(num, num_, opts, modes, f, modes_i) { f = "num_modes" if (!(f in num_)) { if (num_["unique"]) { num_[f] = num_["num_mode_min"] = num_["num_mode_max"] = UNDEF } else { # TODO: optimize if we know the array is sorted num_[f] = modes_i = num_modes(num, modes) if (modes_i == 0) { num_["unique"] = TRUE num_["num_mode_min"] = num_["num_mode_max"] = UNDEF } else { num_["unique"] = FALSE } num_[f] = modes_i } } return num_[f] } function num_modes_init() { num_function_init(\ "num_modes modes", 0, "Get the modes, which is a list.", "https://en.wikipedia.org/wiki/Mode_(statistics)") } ## # # Mode min: get the minimum mode, if any, or UNDEF. # # TODO: IMPLEMENT # # Examples: # # num_mode_min(1 2 3) => UNDEF # num_mode_min(1 2 2 3) => 2 # num_mode_min(1 1 2 4 4) => 1 # ## function num_mode_min(arr) { return TODO } function num_mode_min_(num, num_, opts, f) { f = "num_mode_min" if (!(f in num_)) { num_[f] = TODO } return num_[f] } function num_mode_min_init() { num_function_init(\ "num_mode_min mode_min", 0, "Get the minimum mode, if any, or UNDEF.", "https://en.wikipedia.org/wiki/Mode_(statistics)") } ## # # Mode max: get the maximum mode, if any, or UNDEF. # # TODO: IMPLEMENT # # Examples: # # num_mode_max(1 2 3) => UNDEF # num_mode_max(1 2 2 3) => 2 # num_mode_max(1 1 2 4 4) => 4 # ## function num_mode_max(arr) { return TODO } function num_mode_max_(num, num_, opts, f) { f = "num_mode_max" if (!(f in num_)) { num_[f] = TODO } return num_[f] } function num_mode_max_init() { num_function_init(\ "num_mode_max mode_max", 0, "Get the maximum mode, if any, or UNDEF.", "https://en.wikipedia.org/wiki/Mode_(statistics)") } ############################################################################ # # num-variance.awk # ## # Alias function num_variance(arr) { num_sample_variance(arr) } function num_variance_(num, num_, opts) { num_sample_variance_(num, num_, opts) } # Alias function num_var(arr) { num_sample_variance(arr) } function num_var_(num, num_, opts) { num_sample_variance_(num, num_, opts) } ## # # Sample Variance. # # Example: # # num_population_variance(1 2 4) => 2.33333 # ## function num_sample_variance(arr) { return TODO } function num_sample_variance_(num, num_, opts, f) { f = "num_sample_variance" if (!(f in num_)) num_[f] = num_sum_of_mean_deviation_exp_(num, num_, _opts, num_mean_(num, num_, opts), 2) / (num_n_(num, num_, opts) - 1) return num_[f] } function num_sample_variance_init() { num_function_init(\ "num_sample_variance sample_variance s_var variance var sample_second_moment_about_the_mean s_second_moment_about_the_mean s_2_m_a_t_m second_moment_about_the_mean 2_m_a_t_m", 0, "Get the sample variance, a.k.a. sample second moment about the mean.", "https://wikipedia.org/wiki/Variance") } # Alias function num_svar(arr) { return num_sample_variance(arr) } function num_svar_(num, num_, opt) { return num_sample_variance_(num, num_, opt) } ## # # Population Variance. # # Example: # # num_population_variance(1 2 4) => 1.55556 # ## function num_population_variance(arr) { return TODO } function num_population_variance_(num, num_, opts) { f = "num_population_variance" if (!(f in num_)) num_[f] = num_sum_of_mean_deviation_exp_(num, num_, opts, num_mean_(num, num_, opts), 2) / num_n_(num, num_, opts) return num_[f] } function num_population_variance_init() { num_function_init(\ "num_population_variance population_variance p_var population_second_moment_about_the_mean p_second_moment_about_the_mean p_2_m_a_t_m", 0, "Get the population variance, a.k.a. sample second moment about the mean.", "https://wikipedia.org/wiki/Variance") } # Alias function num_pvar(arr) { return num_population_variance(arr) } function num_pvar_(num, num_, opt) { return num_population_variance_(num, num_, opt) } ############################################################################ # # num-skewness.awk # ## # Alias function num_skewness(arr) { num_sample_skewness(arr) } function num_skewness_(num, num_, opts) { num_sample_skewness_(num, num_, opts) } # Alias function num_skew(arr) { num_sample_skewness(arr) } function num_skew_(num, num_, opts) { num_sample_skewness_(num, num_, opts) } ## # # Sample skewness # # Example: # # num_sample_skewness(1 2 4) => 1.11111 # # A.k.a. population third moment about the mean. # # Calculation: # # * Sum each value deviation from the mean cubed. # * Divide by the number of items - 1. # ## function num_sample_skewness(arr) { return num_sum_of_mean_deviation_exp(arr, num_mean(arr), 3) / (num_n(arr) - 1) } function num_sample_skewness_(num, num_, opts, f) { f = "num_sample_skewness" if (!(f in num_)) num_[f] = num_sum_of_mean_deviation_exp_(num, num_, opts, num_mean_(num, num_, opts), 3) / (num_n_(num, num_, opts) - 1) return num_[f] } function num_sample_skewness_init() { num_function_init(\ "num_sample_skewness sample_skewness s_skew skewness skew sample_third_moment_about_the_mean s_third_moment_about_the_mean s_3_m_a_t_m third_moment_about_the_mean 3_m_a_t_m", 0, "Get the sample skewness, a.k.a. sample third moment about the mean.", "https://en.wikipedia.org/wiki/Skewness") } # Alias function num_sskew(arr) { num_sample_skewness(arr) } function num_sskew_(num, num_, opts) { num_sample_skewness_(num, num_, opts) } ## # # Population skewness # # Example: # # num_population_skewness(1 2 4) => 0.740741 # # A.k.a. population third moment about the mean. # # If skewness is greater than zero, the distribution is positively skewed. # If it is less than zero, it is negatively skewed. # Zero means it is symmetric. # # Calculation: # # * Sum each value deviation from the mean cubed. # * Divide by the number of items. # ## function num_population_skewness(arr) { return num_sum_of_mean_deviation_exp(arr, num_mean(arr), 3) / num_n(arr) } function num_population_skewness_(num, num_, opts, f) { f = "num_population_skewness" if (!(f in num_)) num_[f] = num_sum_of_mean_deviation_exp_(num, num_, opts, num_mean_(num, num_, opt), 3) / num_n_(num, num_, opts) return num_[f] } function num_population_skewness_init() { num_function_init(\ "num_population_skewness population_skewness p_skew population_third_moment_about_the_mean p_third_moment_about_the_mean p_3_m_a_t_m", 0, "Get the population skewness, a.k.a. population third moment about the mean.", "https://en.wikipedia.org/wiki/Skewness") } # Alias function num_pskew(arr) { num_population_skewness(arr) } function num_pskew_(num, num_, opts) { num_population_skewness_(num, num_, opts) } ############################################################################ # # num-kurtosis.awk # # A.k.a. fourth moment about the mean. # # Calculation: # # * Sum each value’s deviation from the mean quaded. # * Divide by the number of items. # # The kurtosis formula measures the degree of peak. # # Kurtosis measures the heaviness of tails, # relative to a normal distribution. # # * Positive kurtosis (peakness) is termed leptokurtic. # * Negative kurtosis (flatness) is termed platykurtic. # * In-between is termed mesokurtic. # # Kurtosis equals 3 for a normal distribution. # # Kurtosis is a nondimensional quantity. # ## # Alias function num_kurtosis(arr) { num_sample_kurtosis(arr) } function num_kurtosis_(num, num_, opts) { num_sample_kurtosis_(num, num_, opts) } # Alias function num_kurt(arr) { num_sample_kurtosis(arr) } function num_kurt_(num, num_, opts) { num_sample_kurtosis_(num, num_, opts) } ## # # Sample kurtosis # # Example: # # num_sample_kurtosis(1 2 4) => 5.44444 # ## function num_sample_kurtosis(arr) { return num_sum_of_mean_deviation_exp(arr, num_mean(arr), 4) / (num_n(arr) - 1) } function num_sample_kurtosis_(num, num_, opts, f) { f = "num_sample_kurtosis" if (!(f in num_)) num_[f] = num_sum_of_mean_deviation_exp_(num, num_, opts, num_mean_(num, num_, opts), 4) / (num_n_(num, num_, opts) - 1) return num_[f] } function num_sample_kurtosis_init() { num_function_init(\ "num_sample_kurtosis sample_kurtosis s_kurt kurtosis kurt sample_fourth_moment_about_the_mean s_fourth_moment_about_the_mean s_4_m_a_t_m fourth_moment_about_the_mean 4_m_a_t_m", 0, "Get the kurtosis, a.k.a. sample fourth moment about the mean.", "https://en.wikipedia.org/wiki/Kurtosis") } # Alias function num_skurt(arr) { num_sample_kurtosis(arr) } function num_skurt_(num, num_, opts) { num_sample_kurtosis_(num, num_, opts) } ## # # Population kurtosis # # Example: # # num_population_kurtosis(1 2 4) => 3.62963 # ## function num_population_kurtosis(arr) { return num_sum_of_mean_deviation_exp(arr, num_mean(arr), 4) / num_n(arr) } function num_population_kurtosis_(num, num_, opts, f) { f = "num_population_kurtosis" if (!(f in num_)) num_[f] = num_sum_of_mean_deviation_exp_(num, num_, opts, num_mean_(num, num_, opts), 4) / num_n_(num, num_, opts) return num_[f] } function num_population_kurtosis_init() { num_function_init(\ "num_population_kurtosis population_kurtosis p_kurt population_fourth_moment_about_the_mean p_fourth_moment_about_the_mean p_4_m_a_t_m", 0, "Get the kurtosis, a.k.a. population fourth moment about the mean.", "https://en.wikipedia.org/wiki/Kurtosis") } # Alias function num_pkurt(arr) { num_population_kurtosis(arr) } function num_pkurt_(num, num_, opts) { num_population_kurtosis_(num, num_, opts) } ############################################################################ # # num-standard-deviation.awk # ## # Alias function num_standard_deviation(arr) { num_sample_standard_deviation(arr) } function num_standard_deviation_(num, num_, opts) { num_sample_standard_deviation_(num, num_, opts) } # Alias function num_stddev(arr) { num_sample_standard_deviation(arr) } function num_stddev_(num, num_, opts) { num_sample_standard_deviation_(num, num_, opts) } ## # # Sample Standard Deviation. # # Example: # # num_sample_standard_deviation(1 2 4) => 1.52753 # ## function num_sample_standard_deviation(arr) { return sqrt(num_sample_variance(arr)) } function num_sample_standard_deviation_(num, num_, opts, f) { f = "num_sample_standard_deviation" if (!(f in num_)) num_[f] = sqrt(num_sample_variance_(num, num_, opts)) return num_[f] } function num_sample_standard_deviation_init() { num_function_init(\ "num_sample_standard_deviation sample_standard_deviation s_st_dev s_s_d standard_deviation std_dev sd", 0, "Get the sample standard deviation", "https://wikipedia.org/wiki/Standard_deviation") } # Alias function num_sstddev(arr) { num_sample_standard_deviation(arr) } function num_sstddev_(num, num_, opts) { num_sample_standard_deviation_(num, num_, opts) } ## # # Population Standard Deviation. # # Example: # # num_population_standard_deviation(1 2 4) => 1.24722 # ## function num_population_standard_deviation(arr) { return sqrt(num_population_variance(arr)) } function num_population_standard_deviation_(num, num_, opts, f) { f = "num_population_standard_deviation" if (!(f in num_)) num_[f] = sqrt(num_population_variance_(num, num_, opts)) return num_[f] } function num_population_standard_deviation_init() { num_function_init(\ "num_population_standard_deviation population_standard_deviation p_st_dev p_s_d", 0, "Get the population standard deviation.", "https://wikipedia.org/wiki/Standard_deviation") } # Alias function num_pstddev(arr) { num_population_standard_deviation(arr) } function num_pstddev_(num, num_, opts) { num_population_standard_deviation_(num, num_, opts) } ############################################################################ # # num-coefficient-of-variance.awk # ## # Alias function num_coefficient_of_variance(arr) { num_sample_coefficient_of_variance(arr) } function num_coefficient_of_variance_(num, num_, opts) { num_sample_coefficient_of_variance_(num, num_, opts) } # Alias function num_covar(arr) { num_sample_coefficient_of_variance(arr) } function num_covar_(num, num_, opts) { num_sample_coefficient_of_variance_(num, num_, opts) } ## # # Sample Coefficient of Variance. # # Example: # # num_sample_coefficient_of_variance(1 2 4) => 0.654654 # ## function num_sample_coefficient_of_variance(arr) { return num_sample_standard_deviation(arr) / num_mean(arr) } function num_sample_coefficient_of_variance_(num, num_, opts, f) { f = "num_sample_coefficient_of_variance" if (!(f in num_)) num_[f] = num_sample_standard_deviation_(num, num_, opts) / num_mean_(num, num_, opts) return num_[f] } function num_sample_coefficient_of_variance_init() { num_function_init(\ "num_sample_coefficient_of_variance sample_coefficient_of_variance s_co_var s_c_v coefficient_of_variance co_var c_v sample_relative_standard_deviation s_r_s_d relative_standard_deviation r_s_d", 0, "Get the sample coefficient of variance", "https://en.wikipedia.org/wiki/Coefficient_of_variation") } # Alias function num_scovar(arr) { num_sample_coefficient_of_variance(arr) } function num_scovar_(num, num_, opts) { num_sample_coefficient_of_variance_(num, num_, opts) } ## # # Population Coefficient of Variance. # # Example: # # 1 2 4 => 0.534522 # ## function num_population_coefficient_of_variance(arr) { return num_population_standard_deviation(arr) / num_mean(arr) } function num_population_coefficient_of_variance_(num, num_, opts, f) { f = "num_population_coefficient_of_variance" if (!(f in num_)) num_[f] = num_population_standard_deviation_(num, num_, opts) / num_mean_(num, num_, opts) return num_[f] } function num_population_coefficient_of_variance_init() { num_function_init(\ "num_population_coefficient_of_variance population_coefficient_of_variance p_co_var p_c_v population_relative_standard_deviation p_r_s_d", 0, "Get the population coefficient of variance.", "https://en.wikipedia.org/wiki/Coefficient_of_variation") } # Alias function num_pcovar(arr) { num_population_coefficient_of_variance(arr) } function num_pcovar_(num, num_, opts) { num_population_coefficient_of_variance_(num, num_, opts) } ############################################################################ # # num-quartiles.awk # # This implemention uses the smoothing method for discrete distrubtions: # # * If there are an even number of data points, then the median is no # single datum point. Do not include the median in either half. # # * If there are (4x+1) data points, then the lower quartile is 25% of the # xth data value plus 75% of the (x+1)th data value; the upper quartile # is 75% of the (3x+1)th data point plus 25% of the (3x+2)th data point. # # * If there are (4x+3) data points, then the lower quartile is 75% of the # (x+1)th data value plus 25% of the (x+2)th data value; the upper # quartile is 25% of the (3x+2)th data point plus 75% of the (3x+3)th # data point. # # * This ensures that the median value is given its correct weight, # and thus quartile values change as smoothly as possible as additional # data points are added. # # * For more see https://en.wikipedia.org/wiki/Quartile # ## ## # # Interquartile Range, a.k.a. IQR. # # Example: # # num_interquartile_range(1 2 3 4 5) => 2.5 # ## function num_interquartile_range(arr) { return num_quartile_3(arr) - num_quartile_1(arr) } function num_interquartile_range_(num, num_, opts, f) { f = "num_interquartile_range" if (!(f in num_)) num_[f] = num_quartile_3_(num, num_, opts) - num_quartile_1_(num, num_, opts) return num_[f] } function num_interquartile_range_init() { num_function_init(\ "num_interquartile_range interquartile_range i_q_r mid_spread middle_fifty", 0, "Get the interquartile range, a.k.a. IQR.", "https://en.wikipedia.org/wiki/Interquartile_range") } # Alias function num_iqr(arr) { return num_interquartile_range(arr) } function num_iqr_(num, num_, opts) { return num_interquartile_range_(num, num_, opts) } ## # # Quartile 0, a.k.a. Q0, 0th percentile, minimum. # # Example: # # num_quartile_0(1 2 3 4 5) => 1 # ## function num_quartile_0(arr) { return num_min(arr) } function num_quartile_0_(num, num_, opts, f) { f = "num_quartile_0" if (!(f in num_)) num_[f] = num_min_(num, num_, opts) return num_[f] } function num_quartile_0_init() { num_function_init(\ "num_quartile_0 quartile_0 q_0 0_percent", 0, "Get the quartile 0, a.k.a. Q0, 0th percentile, minimum.", "https://en.wikipedia.org/wiki/Quartile") } # Alias function num_q0(arr) { return num_quartile_0(arr) } function num_q0_(num, num_, opts) { return num_quartile_0_(num, num_, opts) } ## # # Quartile 1, a.k.a. Q1, 25th percentile, lower quartile. # # Example: # # num_quartile_1(1 2 3 4 5) => 1.75 # # Requires sorted array. # ## function num_quartile_1(arr, _n, i, x, q1) { _n = num_n(arr) if ((_n % 2) == 0) { i = (_n / 2) - 1 q1 = num_median_of_slice(arr, 1, i) } else if ((_n % 4) == 1) { x = ((_n - 1) / 4) q1 = (0.25 * arr[x]) + (0.75 * arr[x+1]) } else if ((_n % 4) == 3) { x = ((_n - 3) / 4) q1 = (0.75 * arr[x+1]) + (0.25 * arr[x+2]) } else { q1 = "" } return q1 } function num_quartile_1_(num, num_, opts, f, _n, i, x) { f = "num_quartile_1" if (!(f in num_)) { _n = num_n_(num, num_, opts) num_median_(num, num_, opts) if ((_n % 2) == 0) { i = (_n / 2) - 1 num_[f] = num_median_of_slice(num, 1, i) } else if ((_n % 4) == 1) { x = ((_n - 1) / 4) num_[f] = (0.25 * num[x]) + (0.75 * num[x+1]) } else if ((_n % 4) == 3) { x = ((_n - 3) / 4) num_[f] = (0.75 * num[x+1]) + (0.25 * num[x+2]) } else { num_[f] = ERROR } } return num_[f] } function num_quartile_1_init() { num_function_init(\ "num_quartile_1 quartile_1 q_1 25_percent", 0, "Get the quartile 1, a.k.a. Q1, 25th percentile, lower quartile.", "https://en.wikipedia.org/wiki/Quartile") } # Alias function num_q1(arr) { return num_quartile_1(arr) } function num_q1_(num, num_, opts) { return num_quartile_1_(num, num_, opts) } ## # # Quartile 2, a.k.a. Q2, 50th percentile, median. # # Example: # # num_quartile_1(1 2 3 4 5) => 3 # ## function num_quartile_2(arr, f) { return num_median(arr) } function num_quartile_2_(num, num_, opts, f) { f = "num_quartile_2" if (!(f in num_)) num_[f] = num_median_(num, num_, opts) return num_[f] } function num_quartile_2_init() { num_function_init(\ "num_quartile_2 quartile_2 q_2 50_percent", 0, "Get the quartile 2, a.k.a. Q2, 50th percentile, median.", "https://en.wikipedia.org/wiki/Quartile") } # Alias function num_q2(arr) { return num_quartile_2(arr) } function num_q2_(num, num_, opts) { return num_quartile_2_(num, num_, opts) } ## # # Quartile 3, a.k.a. Q3, 75th percentile, upper quartile. # # Example: # # num_quartile_1(1 2 3 4 5) => 4.25 # # Requires sorted array. # ## function num_quartile_3(arr, _n, i, x, q3) { _n = num_n(arr) if ((_n % 2) == 0) { i = (_n % 2) + 1 q3 = num_median_of_slice(arr, i, _n) } else if ((_n % 4) == 1) { x = (_n - 1) / 4 q3 = (0.75 * arr[3 * x + 1]) + (0.25 * arr[3 * x + 2]) } else if ((_n % 4) == 3) { x = (_n - 3) / 4 q3 = (0.25 * arr[3 * x + 2]) + (0.75 * arr[3 * x + 3]) } else { q3 = "" } return q3 } function num_quartile_3_(num, num_, opts, f, _n, i, x) { f = "num_quartile_3" if (!(f in num_)) { _n = num_n_(num, num_, opts) num_median_(num, num_, opts) if ((_n % 2) == 0) { i = (_n % 2) + 1 num_[f] = num_median_of_slice(num, i, _n) } else if ((_n % 4) == 1) { x = (_n - 1) / 4 num_[f] = (0.75 * num[3 * x + 1]) + (0.25 * num[3 * x + 2]) } else if ((_n % 4) == 3) { x = (_n - 3) / 4 num_[f] = (0.25 * num[3 * x + 2]) + (0.75 * num[3 * x + 3]) } else { num_[f] = ERROR } } return num_[f] } function num_quartile_3_init() { num_function_init(\ "num_quartile_3 quartile_3 q_3 75_percent", 0, "Get the quartile 3, a.k.a. Q3, 75th percentile, upper quartile.", "https://en.wikipedia.org/wiki/Quartile") } # Alias function num_q3(arr) { return num_quartile_3(arr) } function num_q3_(num, num_, opts) { return num_quartile_3_(num, num_, opts) } ## # # Quartile 4, a.k.a. Q4, 100th percentile, maximum. # # Example: # # num_quartile_1(1 2 3 4 5) => 5 # ## function num_quartile_4(arr) { return num_max(arr) } function num_quartile_4_(num, num_, opts, f) { f = "num_quartile_4" if (!(f in num_)) num_[f] = num_max_(num, num_, opts) return num_[f] } function num_quartile_4_init() { num_function_init(\ "num_quartile_4 quartile_4 q_4 100_percent", 0, "Get the quartile 4, a.k.a. Q4, 100th percentile, maximum.", "https://en.wikipedia.org/wiki/Quartile") } # Alias function num_q4(arr) { return num_quartile_4(arr) } function num_q4_(num, num_, opts) { return num_quartile_4_(num, num_, opts) } ############################################################################ # # num-init.awk # ## ## # # Initialize everything. # # This calls various `init_*` functions. # ## function num_init() { #init_lint() num_init_constants() num_init_conf() num_init_word_argv() num_init_word_list() num_function_manager_init() } ## # # Initialize constants that we use; these are essentially like defines. # ## function num_init_constants() { # Boolean FALSE = 0 TRUE = 1 # Math PI = 3.141592653589797 # also atan2(0,-1) # Content TODO = "TODO" UNDEF = "UNDEF" ERROR = "ERROR" NAN = "NAN" # Function kinds: a way to track what a function will do. #TODO FUN_KIND_CALC = "CALC" FUN_KIND_SORT = "SORT" FUN_KIND_MAP = "MAP" FUN_KIND_FLAG = "FLAG" FUN_KIND_CONF = "CONF" # Feature detection. Currently this script requires Gawk. AWK_HAS_ASORT = TRUE AWK_HAS_LENGTH = TRUE # The scope controls whether the user wants input and output # to be all items (the default), or per record, or per field. # The scope per field is a special case, because it lets the # the awk loop read one record, calculate, then flush data. NUM_CONF_SCOPE_ALL = "NUM_CONF_SCOPE_ALL" # Default NUM_CONF_SCOPE_RECORD = "NUM_CONF_SCOPE_RECORD" # Triggers optimized loop NUM_CONF_SCOPE_FIELD = "NUM_CONF_SCOPE_FIELD" # TODO implement } ## # # Initialize the configuration dictionary. # ## function num_init_conf() { split("", global_conf) global_conf["scope"] = NUM_CONF_SCOPE_ALL } ## # # Initialize the word argv list. # # The word argv list holds the argv items that this script cares about. # We keep them in order, so we can output results in order. # ## function num_init_word_argv() { split("", global_word_argv) } ## # # Initialize the global word list lookup array. # # This is to recognize words that a user types on the command line. # # TODO: research if there is a better way to initialize a dictionary. # ## function num_init_word_list() { num_synonyms["overall"] = \ "num_conf_scope_all" num_synonyms["records"] = \ num_synonyms["rows"] = \ "num_conf_scope_record" num_synonyms["fields"] = \ num_synonyms["colums"] = \ "num_conf_scope_field" # Convention: default is sample, not population. num_synonyms["secondmomentaboutthemean"] = \ num_synonyms["secondmoment"] = \ "num_sample_variance" # Convention: default is sample, not population. num_synonyms["thirdmomentaboutthemean"] = \ num_synonyms["thirdmoment"] = \ "num_sample_skewness" # Convention: default is sample, not population. num_synonyms["fourthmomentaboutthemean"] = \ num_synonyms["fourthmoment"] = \ "num_sample_kurtosis" ## Booleans num_synonyms["isunique"] = \ "num_is_unique" num_synonyms["isascending"] = \ num_synonyms["isasc"] = \ num_synonyms["isnondescending"] = \ num_synonyms["isnondesc"] = \ "num_is_ascending" num_synonyms["isstrictlyascending"] = \ num_synonyms["isstrictasc"] = \ "num_is_strictly_ascending" num_synonyms["isdescending"] = \ num_synonyms["isdesc"] = \ num_synonyms["isnonascending"] = \ num_synonyms["isnonasc"] = \ "num_is_descending" num_synonyms["isstrictlydescending"] = \ num_synonyms["isstrictdesc"] = \ "num_is_strictly_descending" ### Configurations num_synonyms["commaseparatedvalues"] = \ num_synonyms["csv"] = \ "num_io_comma_separated_values" num_synonyms["inputcommaseparatedvalues"] = \ num_synonyms["inputcsv"] = \ num_synonyms["incsv"] = \ "num_input_comma_separated_values" num_synonyms["outputcommaseparatedvalues"] = \ num_synonyms["outputcsv"] = \ num_synonyms["outcsv"] = \ "num_output_comma_separated_values" num_synonyms["tabseparatedvalues"] = \ num_synonyms["tsv"] = \ "num_io_tab_separated_values" num_synonyms["inputtabseparatedvalues"] = \ num_synonyms["inputtsv"] = \ num_synonyms["intsv"] = \ "num_input_tab_separated_values" num_synonyms["outputtabseparatedvalues"] = \ num_synonyms["outputtsv"] = \ num_synonyms["outtsv"] = \ "num_output_tab_separated_values" num_synonyms["unitseparatedvalues"] = \ num_synonyms["usv"] = \ "num_io_unit_separated_values" num_synonyms["inputunitseparatedvalues"] = \ num_synonyms["inputusv"] = \ num_synonyms["inusv"] = \ "num_input_unit_separated_values" num_synonyms["outputunitseparatedvalues"] = \ num_synonyms["outputusv"] = \ num_synonyms["outusv"] = \ "num_output_unit_separated_values" } ############################################################################ # # num-conf.awk # ## ## # # Configure # ## function num_conf() { num_conf_words(global_word_argv) num_conf_ofmt() num_conf_scope() } ## # # num_configure the output format string, a.k.a. AWK OFMT. # This controls number formatting as float, decimal, int, etc. # ## function num_conf_ofmt() { OFMT = (ENVIRON["OFMT"]) ? ENVIRON["OFMT"] : "%.6g" } ## # num_configure scope flags to optimize for tight inner loops. # # This enables us to speed up code by doing this: # # - if (global_conf["scope"] == NUM_CONF_SCOPE_ALL) # + if (NUM_CONF_SCOPE_ALL_FLAG) ## function num_conf_scope() { NUM_CONF_SCOPE_ALL_FLAG = (global_conf["scope"] == NUM_CONF_SCOPE_ALL) NUM_CONF_SCOPE_RECORD_FLAG = (global_conf["scope"] == NUM_CONF_SCOPE_RECORD) NUM_CONF_SCOPE_FIELD_FLAG = (global_conf["scope"] == NUM_CONF_SCOPE_FIELD) } ## # # Given a word, set a num_configuration setting. # # Call this function for each option word a.k.a. flag, # before any calculation happens and any work happens. # # This function must only set `conf` keys and values. # This function must NOT do any calculations, work, etc. # # TODO: consider changing these to functions. # ## function num_conf_word(word) { if (word == "") return else if (word == "help") num_help() else if (word == "num_conf_scope_all") global_conf["scope"] = NUM_CONF_SCOPE_ALL else if (word == "num_conf_scope_record") global_conf["scope"] = NUM_CONF_SCOPE_RECORD else if (word == "num_conf_scope_field") global_conf["scope"] = NUM_CONF_SCOPE_FIELD else if (word == "num_io_comma_separated_values") { FS = ","; RS = "\n"; OFS = ","; ORS = "\n" } else if (word == "num_input_comma_separated_values") { FS = ","; RS = "\n" } else if (word == "num_output_comma_separated_values") { OFS = ","; ORS = "\n" } else if (word == "num_io_tab_separated_values" ) { FS = "\t"; RS = "\n"; OFS = "\t"; ORS = "\n"} else if (word == "num_input_tab_separated_values" ) { FS = "\t"; RS = "\n" } else if (word == "num_output_tab_separated_values") { OFS = "\t"; ORS = "\n" } else if (word == "num_io_unit_separated_values") { FS = "␟"; RS = "␞"; OFS = "␟"; ORS = "␞" } else if (word == "num_input_unit_separated_values") { FS = "␟"; RS = "␞" } else if (word == "num_output_unit_separated_values") { OFS = "␟"; ORS = "␞" } else return "" } ## # # Given a list of words, set all num_configuration settings. # ## function num_conf_words(words, imax) { for (i=1; i <= num_arr_length(words); i++) num_conf_word(words[i]) } ############################################################################ # # num-scope.awk # ## ## # # Start receiving input. # # Ready the global number array for new input and new metadata. # ## function scope_start() { global_num_scope_n++ global_num_scope_output_n = 0 global_num_n = 0 split("", global_num) split("", global_num_) } ## # # Stop receiving input. # # Set any work in progress here. # ## function scope_stop() { global_num_["n"] = global_num_n num_print_record(global_num, global_num_, global_opts) } ############################################################################ # # num-function-manager.awk # ## ## # # Initialize every file and function. # ## function num_function_manager_init() { num_n_init() num_first_init() num_last_init() num_min_init() num_max_init() num_range_init() num_frequency_min_init() num_frequency_max_init() num_select_eq_init() num_select_ne_init() num_select_lt_init() num_select_le_init() num_select_gt_init() num_select_ge_init() num_select_in_init() num_select_ex_init() num_reject_eq_init() num_reject_ne_init() num_reject_lt_init() num_reject_le_init() num_reject_gt_init() num_reject_ge_init() num_reject_in_init() num_reject_ex_init() num_sum_init() num_product_init() num_mean_init() num_mean_absolute_deviation_init() num_meanest_init() num_trimean_init() num_trimmed_mean_init() num_median_init() num_median_low_init() num_median_high_init() num_modes_init() num_mode_min_init() num_mode_max_init() num_sum_of_squares_init() num_sum_of_cubes_init() num_sum_of_quads_init() num_sample_variance_init() num_population_variance_init() num_sample_standard_deviation_init() num_population_standard_deviation_init() num_sample_coefficient_of_variance_init() num_population_coefficient_of_variance_init() num_sample_skewness_init() num_population_skewness_init() num_sample_kurtosis_init() num_population_kurtosis_init() num_interquartile_range_init() num_quartile_0_init() num_quartile_1_init() num_quartile_2_init() num_quartile_3_init() num_quartile_4_init() num_sort_awk_init() num_sort_ascending_init() num_sort_descending_init() num_map_increment_init() num_map_absolute_value_init() num_map_sign_init() num_map_normalize_init() num_map_round_awk_init() num_map_round_init() num_map_round_off_init() num_map_round_up_init() num_map_round_down_init() num_is_unique_init() num_is_ascending_init() num_is_strictly_ascending_init() num_is_descending_init() num_is_strictly_descending_init() num_help_init() } ## # # Function manager call: given a function name, call its function. # # Example: # # num = 1 2 4 # num_ = [] # function_name = "num_sum" # num_function_manager_call(num, num_, opts, function_name) # => 7 (by calling the `sum` function) # # Note: this implementation uses if..else instead of # any switch or case, because we want POSIX usability. # # TODO: Research if it is possible to simultaneously support # gawk indirect functions, to do a function call via `@f()`. # ## function num_function_manager_call(num, num_, opts, f) { if (f == "") return ("") else if (f == "num_n") return num_n_(num, num_, opts) else if (f == "num_first") return num_first_(num, num_, opts) else if (f == "num_last") return num_last_(num, num_, opts) else if (f == "num_min") return num_min_(num, num_, opts) else if (f == "num_max") return num_max_(num, num_, opts) else if (f == "num_range") return num_range_(num, num_, opts) else if (f == "num_frequency_min") return num_frequency_min_(num, num_, opts) else if (f == "num_frequency_max") return num_frequency_max_(num, num_, opts) else if (f == "num_select_eq") return num_select_eq_(num, num_, opts) # x else if (f == "num_select_ne") return num_select_ne_(num, num_, opts) # x else if (f == "num_select_lt") return num_select_lt_(num, num_, opts) # x else if (f == "num_select_le") return num_select_le_(num, num_, opts) # x else if (f == "num_select_gt") return num_select_gt_(num, num_, opts) # x else if (f == "num_select_ge") return num_select_ge_(num, num_, opts) # x else if (f == "num_select_in") return num_select_in_(num, num_, opts) # min, max else if (f == "num_select_ex") return num_select_ex_(num, num_, opts) # min, max else if (f == "num_reject_eq") return num_reject_eq_(num, num_, opts) # x else if (f == "num_reject_ne") return num_reject_ne_(num, num_, opts) # x else if (f == "num_reject_lt") return num_reject_lt_(num, num_, opts) # x else if (f == "num_reject_le") return num_reject_le_(num, num_, opts) # x else if (f == "num_reject_gt") return num_reject_gt_(num, num_, opts) # x else if (f == "num_reject_ge") return num_reject_ge_(num, num_, opts) # x else if (f == "num_reject_in") return num_reject_in_(num, num_, opts) # min, max else if (f == "num_reject_ex") return num_reject_ex_(num, num_, opts) # min, max else if (f == "num_sum") return num_sum_(num, num_, opts) else if (f == "num_product") return num_product_(num, num_, opts) else if (f == "num_mean") return num_mean_(num, num_, opts) else if (f == "num_mean_absolute_deviation") return num_mean_absolute_deviation_(num, num_, opts) else if (f == "num_meanest") return num_meanest_(num, num_, opts) else if (f == "num_trimean") return num_trimean_(num, num_, opts) else if (f == "num_trimmed_mean") return num_trimmed_mean_(num, num_, opts) else if (f == "num_median") return num_median_(num, num_, opts) else if (f == "num_median_low") return num_median_low_(num, num_, opts) else if (f == "num_median_high") return num_median_high_(num, num_, opts) else if (f == "num_modes") return num_modes_(num, num_, opts) else if (f == "num_mode_min") return num_mode_min_(num, num_, opts) else if (f == "num_mode_max") return num_mode_max_(num, num_, opts) else if (f == "num_sum_of_squares") return num_sum_of_squares_(num, num_, opts) else if (f == "num_sum_of_cubes") return num_sum_of_cubes_(num, num_, opts) else if (f == "num_sum_of_quads") return num_sum_of_quads_(num, num_, opts) else if (f == "num_sample_variance") return num_sample_variance_(num, num_, opts) else if (f == "num_population_variance") return num_population_variance_(num, num_, opts) else if (f == "num_sample_standard_deviation") return num_sample_standard_deviation_(num, num_, opts) else if (f == "num_population_standard_deviation") return num_population_standard_deviation_(num, num_, opts) else if (f == "num_sample_coefficient_of_variance") return num_sample_coefficient_of_variance_(num, num_, opts) else if (f == "num_population_coefficient_of_variance") return num_population_coefficient_of_variance_(num, num_, opts) else if (f == "num_sample_skewness") return num_sample_skewness_(num, num_, opts) else if (f == "num_population_skewness") return num_population_skewness_(num, num_, opts) else if (f == "num_sample_kurtosis") return num_sample_kurtosis_(num, num_, opts) else if (f == "num_population_kurtosis") return num_population_kurtosis_(num, num_, opts) else if (f == "num_interquartile_range") return num_interquartile_range_(num, num_, opts) else if (f == "num_quartile_0") return num_quartile_0_(num, num_, opts) else if (f == "num_quartile_1") return num_quartile_1_(num, num_, opts) else if (f == "num_quartile_2") return num_quartile_2_(num, num_, opts) else if (f == "num_quartile_3") return num_quartile_3_(num, num_, opts) else if (f == "num_quartile_4") return num_quartile_4_(num, num_, opts) else if (f == "num_sort_ascending") return num_sort_ascending_(num, num_, opts) else if (f == "num_sort_descending") return num_sort_descending_(num, num_, opts) else if (f == "num_map_increment") return num_map_increment_(num, num_, opts) else if (f == "num_map_absolute_value") return num_map_absolute_value_(num, num_, opts) else if (f == "num_map_sign") return num_map_sign_(num, num_, opts) else if (f == "num_map_normalize") return num_map_normalize_(num, num_, opts) else if (f == "num_map_round") return num_map_round_(num, num_, opts) else if (f == "num_map_round_off") return num_map_round_off_(num, num_, opts) else if (f == "num_map_round_up") return num_map_round_up_(num, num_, opts) else if (f == "num_map_round_down") return num_map_round_down_(num, num_, opts) else if (f == "num_is_unique") return num_is_unique_(num, num_, opts) else if (f == "num_is_ascending") return num_is_ascending_(num, num_, opts) else if (f == "num_is_strictly_ascending") return num_is_strictly_ascending_(num, num_, opts) else if (f == "num_is_descending") return num_is_descending_(num, num_, opts) else if (f == "num_is_strictly_descending") return num_is_strictly_descending_(num, num_, opts) else return "" } ## # # Function name to s: given a function name, call its function, # and convert the return value to a string using formatting. # # Example: # # num = 1 2 4 # num_ = [] # num_function_manager_call_to_s(num, num_, opts, "sum") # => 7.00 (by calling the `sum` function and using OFMT) # ## function num_function_name_to_s(num, num_, opts, function_name, s) { s = num_function_manager_call(num, num_, opts, function_name) if (s != "") { num_scope_output_n++ if (s + 0 == s) s = sprintf(OFMT, s) } return s } ## # # Function names to s: given a list of function names, call each function, # and convert all the return values to a string using formatting. # # Example: # # num = 1 2 4 # num_ = [] # function_names = "sum", "max" # function_names_to_s(num, opts, ("sum", "max")) # => "7.00,4.00" (by calling the functions and using OFMT and OFS) # ## function num_function_names_to_s(num, num_, opts, function_names, word, i, n, s, build) { build = "" for (i=1; i <= num_arr_length(function_names); i++) { function_name = function_names[i] s = num_function_name_to_s(num, num_, opts, function_name) if (s != "") { n++ if (n > 1) build = build OFS build = build s } } return build } ############################################################################ # # num-print.awk # ## ## # # Print a record. # # This is the core output function, and the only one that should ever # print any normal result to the screen, during any normal operation. # ## function num_print_record(num, num_, opts, s, i) { s = num_function_names_to_s(global_num, global_num_, global_opts, global_word_argv) if (global_num_scope_n > 1) printf ORS if (s != "") { printf s } else { num_print_record_fields(global_num, global_num_, global_opts) } } ## # # Print all record fields. # # This is the fallback if the record has no output so far. # A typical example would be for a sort, or filter, or map. # ## function num_print_record_fields(num, num_, opts) { for (i = 1; i <= num_["n"]; i++) { if (i > 1) printf OFS printf(OFMT, num[i]) } } ############################################################################ # # num-argv.awk # ## ## # # Parse words by iterating: when a word is recognized, # move it from the inputs array to the outputs array. # # Example to parse ARGV: # # parse_words(ARGV, global_word_argv, synonyms) # ## function num_argv_parse(inputs, outputs, synonyms, i, imax, item, outputs_n) { split("", outputs) outputs_n = 0 imax = length(inputs) for (i=1; i<=imax; i++) { item = tolower(inputs[i]) gsub(/[-_]/, "", item) if (item in synonyms) { item = synonyms[item] delete inputs[i] outputs[++outputs_n] = item } } } ## # # Parse the command line ARGV inputs to recognized word outputs. # ## function num_argv() { num_argv_parse(ARGV, global_word_argv, num_synonyms) } ############################################################################ # # num-main.awk # ## BEGIN{ num_init() num_argv() num_conf() if (NUM_CONF_SCOPE_ALL_FLAG) scope_start() } { if (NUM_CONF_SCOPE_ALL_FLAG) { for(i=1;i<=NF;i++) global_num[++global_num_n] = $i # Inline } else if (NUM_CONF_SCOPE_RECORD_FLAG) { scope_start() for(i=1;i<=NF;i++) global_num[++global_num_n] = $i # Inline scope_stop() } else { num_err("Num configuration scope is not recognized") } } END{ if (NUM_CONF_SCOPE_ALL_FLAG) scope_stop() printf "\n" # TODO add conf flag equivalent to echo -n } ' "$@" Quickcal_Calculator-2.1-7077da9bfefcb59da0eb133bd7c06013a99c05ce/num.1.gz000066400000000000000000000006131322051455000245040ustar00rootroot00000000000000Znum.1QN1)F{j%kS5`7bácʮgޞ[/ϣ6a6{򤕖K l,}'Gmi&#Jc@:Sn +ttEBSPFa zIѿc&Ï(]6C‡ D)!sxgh~ .o6ݦT'HI8}@EzPePbJ>u-?dhQ ODɂ-,@y!$i)G(_D-<1) kT |3Rwq] yhX`y5?->۟vo/&dtk cv]Quickcal_Calculator-2.1-7077da9bfefcb59da0eb133bd7c06013a99c05ce/quickcal000077500000000000000000000241741322051455000247360ustar00rootroot00000000000000#!/usr/bin/python3 # -*- coding: utf-8 -*- # Quickcal - fast and easy to use calculator with support for filing # Copyright (C) 2017 Nathan SR # License: # quickcal is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # quickcal is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with quickcal. If not, see . # Necessary Imports import gi gi.require_version('Gtk', '3.0') from gi.repository import Gtk from gi.repository import Pango import subprocess import re import os, errno import datetime import time import sys # Following checks for specific characters in inputs, that affects calculations later def check_existence(text): return bool(re.search(r'[A-Za-z\`\~\!\@\#\%\^\&\*\(\)\-\_\=\+\\\|\]\}\[\{\'\"\;\:\/\?\>\<]', text)) def check_existenceii(text): return bool(re.search(r'[\+\-\*\/\%\^\(\)]', text)) def check_existenceiii(text): return bool(re.search(r'[\`\~\!\@\#\&\_\=\\\|\]\}\[\{\'\"\;\:\?\>\<]', text)) # Following executes os commands after replacing any currency symbols in input def execute_command(dc): start = textbuffer.get_start_iter() end = textbuffer.get_end_iter() if check_existence(textbuffer.get_text(start,end,"false")): result.set_text("Input Contains Non Numerical Characters") else: static_command = "echo " + textbuffer.get_text(start,end,"false").replace('\n', ' ').replace('\r', ' ').replace('$', '').replace(',', '').replace('؋', '').replace('.د.ب', '').replace('¢', '').replace('£', '').replace('¥', '').replace('৳', '').replace('฿', '').replace('៛', '').replace('₡', '').replace('₥', '').replace('₦', '').replace('₩', '').replace('₪', '').replace('₫', '').replace('€', '').replace('₭', '').replace('₮', '').replace('₱', '').replace('₲', '').replace('₴', '').replace('₹', '').replace('ƒ', '').replace('د.إ', '').replace('د.ت', '').replace('د.ع', '').replace('د.ك', '').replace('د.م.', '').replace('دج', '').replace('ر.س', '').replace('ر.ع.', '').replace('ر.ق', '').replace('ر.ي', '').replace('ل.د', '') full_command = static_command + dc proc = subprocess.Popen(full_command,stdout=subprocess.PIPE,stderr=subprocess.PIPE,shell=True,universal_newlines=True) (out, err) = proc.communicate() # outwithoutreturn = out.rstrip('\n') if err: result.set_text("Input Contains Non Numerical Characters") else: result.set_text(out.rstrip('\n')) def execute_commandii(): start = textbuffer.get_start_iter() end = textbuffer.get_end_iter() if not check_existenceii(textbuffer.get_text(start,end,"false")): result.set_text("BM needs any of +,-,*,/,%,^,() operators") elif check_existenceiii(textbuffer.get_text(start,end,"false")): result.set_text("Input Contains Invalid Characters") else: full_command = "awk \"BEGIN {printf \\\"%." + str(spinbutton.get_value_as_int()) + "f\\\", " + textbuffer.get_text(start,end,"false").replace('\n', ' ').replace('\r', ' ').replace('$', '').replace(',', ',').replace('؋', '').replace('.د.ب', '').replace('¢', '').replace('£', '').replace('¥', '').replace('৳', '').replace('฿', '').replace('៛', '').replace('₡', '').replace('₥', '').replace('₦', '').replace('₩', '').replace('₪', '').replace('₫', '').replace('€', '').replace('₭', '').replace('₮', '').replace('₱', '').replace('₲', '').replace('₴', '').replace('₹', '').replace('ƒ', '').replace('د.إ', '').replace('د.ت', '').replace('د.ع', '').replace('د.ك', '').replace('د.م.', '').replace('دج', '').replace('ر.س', '').replace('ر.ع.', '').replace('ر.ق', '').replace('ر.ي', '').replace('ل.د', '') + "}\"" proc = subprocess.Popen(full_command,stdout=subprocess.PIPE,stderr=subprocess.PIPE,shell=True,universal_newlines=True) (out, err) = proc.communicate() # outwithoutreturn = out.rstrip('\n') if err: result.set_text("Input Contains Invalid Characters") else: result.set_text(out) # Following functions creates folder, does filing and viewing of files def create_directory(myfolder): try: os.makedirs(myfolder) # print "Successfully Created : " + myfolder except OSError as e: if e.errno != errno.EEXIST: result.set_text("Failed Creating quickcal Home Folder") def fileit(): from datetime import datetime datestring = datetime.strftime(datetime.now(), '%Y-%m-%d') try: f = open(path + '/' + 'Inputs_' + datestring + '.txt', 'a+') start = textbuffer.get_start_iter() end = textbuffer.get_end_iter() f.write ('\n' + '\n' + textbuffer.get_text(start,end,"false")) f.close() g = open(path + '/' + 'Results_' + datestring + '.txt', 'a+') g.write ('\n' + '\n' + result.get_text()) g.close() result.set_text("Filed Successfully") except IOError: result.set_text("Failed Writing to Inputs / Results file") def viewit(file): from datetime import datetime datestring = datetime.strftime(datetime.now(), '%Y-%m-%d') subprocess.call(["xdg-open", path + '/' + file + datestring + '.txt']) # if sys.platform == 'linux2': # subprocess.call(["xdg-open", path + '/' + file + datestring + '.txt']) # else: # os.startfile(path + '/' + file + datestring + '.txt') class Handler: # Calculator buttons and their respective clicked actions. Calls the functions above with their respective parameters. def basicbutton(self, button): execute_commandii() def sumbutton(self, button): dynamic_command = " | awk '{ for(i=1; i<=NF;i++) j+=$i; printf \"%." + str(spinbutton.get_value_as_int()) + "f\", j; j=0 }'" execute_command(dynamic_command) def averagebutton(self, button): dynamic_command = " | awk '{ for(i=1; i<=NF;i++) j+=$i; printf \"%." + str(spinbutton.get_value_as_int()) + "f\", j/NF; j=0 }'" execute_command(dynamic_command) def countbutton(self, button): dynamic_command = " | awk '{ printf NF }'" execute_command(dynamic_command) def minimumbutton(self, button): dynamic_command = " | awk '{m=$1;for(i=1;i<=NF;i++)if($i False True Quickcal Calculator center True /usr/share/pixmaps/quickcal.png normal True Quickcal Calculator Files: quickcal* Copyright: 2017 Nathan SR <cquickcal@gmail.com> License: GPL-3+ Files: num* Copyright: 2017 Joel Parker Henderson License: GPL Fast and Easy to use Calculator with support for Filing It performs arithmetic and statistical calculations, at just the click of a button. It allows quick pasting of a large set of numbers, separated with newlines or spaces or tabs and performs calculations based on the button pressed. As in other calculators, basic math can be performed too, with the +,-,*,/,%,^ operators, in between numbers. Setting the scale allows for more decimals to be displayed. Also, about 65 statistical calculations can be performed by choosing an option from the more stats list box. Keyboard Shortcuts: Basic Math : Ctrl+b CLEAR : Ctrl+x SUM : Ctrl+s AVERAGE : Ctrl+a COUNT : Ctrl+t MINIMUM : Ctrl+m MAXIMUM : Ctrl+n RANGE : Ctrl+g STDDEV : Ctrl+e Details : Ctrl+d FILING : Ctrl+f VIEW INPUTS : Ctrl+i VIEW RESULTS : Ctrl+r https://sourceforge.net/projects/quickcal/ Program Website License: GPL-3+ This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see <http://www.gnu.org/licenses/>. On Debian systems, the full text of the GNU General Public License version 3 can be found in the file `/usr/share/common-licenses/GPL-3'. Quickcal : Nathan SR <cquickcal@gmail.com> num: Joel Parker Henderson /usr/share/pixmaps/quickcal_small.png custom False vertical 2 False end False False 0 100 1 10 False Quickcal Calculator center 300 300 /usr/share/pixmaps/quickcal.png True False 10 0 none True False vertical True False Paste or type your numbers here.... False True 1 True False False True 2 150 150 True True in 150 150 True True word False True 3 True False _SUM True True True Sum of just plain numbers. Eg. 64.28 48.16 Spaces, Tabs or Newlines between Numbers are supported Keyboard Shortcut : Ctrl+s True True True 0 5 _AVERAGE True True True Average of just plain numbers. Eg. 64.28 48.16 Spaces, Tabs or Newlines between Numbers are supported Keyboard Shortcut : Ctrl+a True True True 1 5 COUN_T True True True Keyboard Shortcut : Ctrl+t True True True 2 5 True False 0 4 True False 1 4 True False 2 4 True False True Basic Math: 0 3 _MINIMUM True True True Keyboard Shortcut : Ctrl+m True True 0 7 True False 0 6 True False 1 6 True False 2 6 MAXIMUM True True True Keyboard Shortcut : Ctrl+n True 1 7 RAN_GE True True True Keyboard Shortcut : Ctrl+g True True 2 7 True False 0 10 True False 1 10 True False 2 10 Click This True True True Basic Math Eg. 8+4 or 8-4 or 8*4 or 8/4 or 8%4 or 8^4 or their combination Also supported are int(x) , atan2(y, x) , cos(x) , exp(x) , log(x) , rand() , sin(x) , sqrt(x) , srand([x]) Keyboard Shortcut : Ctrl+b True True 1 3 True False 0 2 True False 1 2 True False 2 2 True False 0 8 True False 1 8 True False 2 8 STDDE_V True True True Keyboard Shortcut : Ctrl+e True True 0 9 CLEAR True True True Clears the input and result data Keyboard Shortcut : Ctrl+x True 2 3 True False True Program: 1 9 _Details True True True Keyboard Shortcut : Ctrl+d True True 2 9 False True 5 True False True False True Set Scale to: False True 0 True True center center True digits adjustment1 1 True 2 False True 1 True False True digits False True 2 False True 6 True False False True 7 True False More Stats: False True 8 True False False True 9 True False False True 10 True True False Result False True 11 True False False True 12 _File the Inputs & Results True True True Keyboard Shortcut : Ctrl+f True False True 13 True False False True 14 View Today's _Inputs True True True Keyboard Shortcut : Ctrl+i True False True 15 True False False True 16 View Today's _Results True True True Keyboard Shortcut : Ctrl+r True False True 17 True False False True 18 True False Quickcal_Calculator-2.1-7077da9bfefcb59da0eb133bd7c06013a99c05ce/quickcal.png000066400000000000000000003027061322051455000255160ustar00rootroot00000000000000PNG  IHDR\g3 pHYs  tIME %u IDATxڌis$ɱĚk-XzfWW 3"I|Ij-V}(3{%PKfF]?X֍3Ӽpy!;1A"dbÁ#kk,͋S1~!2\,z\eY6 T\al,!2Qdm0 tM6kIg%Kƞiq\3yf[W lP $J VCnqq<xw|Yzɝw}Fi2 uTԐsd6O օ˲8K)'b>D_Ȳ8PX40}d 2>e*)RS&D D@RL7Ҳ2β -ah7dwZ Ze|ۦ/_%^7Tl!25In^K$5>'WB,r1T ˗=2th!B%5sliDIP`eqF@!D *.qbd` 5>f|L1e|)Ĵz.יu^X7G,T,I$Ms_ 3]2 ak]+uB2VuZ3m5881"(-Zboُ#Cq+v:o`B AlDaY??ˉ>F\LB,׎§{PBUAK0(ZcR4FhMkAoiA?$9%duB1dm#șIZrm-׾:v5Ʈe7tbFh #й$5%7s}ɢ\텹n$*eB-S [%煟~?7~G^1)hk;[!gQ#-NJ:m[]rwQCIn$bX>3Qo,D\8Bd aY76ec<bdLE )xw0ll)Uq +ǥu.3+Ӳr[!H%JXCbh]C6bla@/B].Q``&>qY<駟Ӗ7 ԫS<}$<ۺc;:UB|jR@g5 Heļ#!3"%)rr&L8P9e|24k؏ݡNHH Jjt ֫EvPSlkq3~(c=>_#+GgV FpҰ+>o>m+Gg{N3`t b..EoF+46X3Pl\8qγzϺyV!1-k&)b‡TSSB DL#x!rH$B (l $uN|$Deӥ1,DvU 4VXABgW`*\2/U3[g^+;:iLi }3eh^kYFcx\9O3 P"$. BT%@Ȃd)q9VǼzNj ƈ)R>?xI>T@o\΋/zpq ?|׏p9_(# bl3-,z|p ]}gok%V8Rdd~3Ts02Y6`5뺱9<ض/GBnyX]R1.b&m[q 9eFb|`a0r&@&h.|0Ĩ rɋ͟"#(hZV` HY޿Uh5+L+~y~'7-X(mJ#D  F(PD!b&NϬ^_7hPZ)'jڎVJ6q:ZBtH#@$BJ%&B#dRĘ %.NJ#ݣ o,?"}Pze&kD,%1+/O\g*_.'~DۆuY+糥9<\w/g b7 COXXJ&Rt#<<,AucuX׭D0޳G)BM(3!x)y϶yܶsDKhtfw(FP,$&%, Q$BʤTQ7-0 zk?׏>gq`[5hkѦAᴲ(IA6( "gp>-8/7h3b[M.gY >64E!<ĸBsZ@ΒBIP}ʤ,B y_'.}Uw Zˑ(ߕD_]V"HU {VYcYZ&LH:>43g~u[.cH9,B"R!Aer1e踎=Ck؍#q`{ҵk Zk>fB 8j ,\ff_ -\1IRVT6uJιض a[n)){!hɤ/'duYU"2B+ eQ?Q /CL`3TݰŨ2]D3.$ϟ___3඲zEmiڠA5 ؆lB)MN EJ %9P&5t,Lq@"(yQMЍ CbzBp "KDNHJ*AN )@ -8t/; Ao7.˕w%3YdŮ_r&}eR.iZ|2;H,l9 (,?_R!Eb{Rc жwݎ~dǁmh68\`sDͱ%hB(W()pB J|ÇmD3~B&J@bљ| DI΅c*!UFT*|[̢8Ireo)ܴ|WvoB҄L).F^. ?oėgi":Wj{-Y6DQ֒m-ƶ _YF)RK}6+#e CG ѵޙCDB$J74ǘj]8TD)@E )bP?❯=+2&u:m,5ַxCP& G%Ʋ/B,_ [;2>OD=Z+w|7,:.P?Wy @V`f1cL=G֐.Нǁ~BxiZJa 1%"S,w×mSjna[ѭ噴!g|D_ 8?-QB6ϓJ`ELJo"j7NdJQ~}Oa-Ӵ_/lc!R%Dmbf%Clm)S[ Z"%YR(JO4A(BD=DaDADH5+47G zHCy>yVOt\/^.^\f ωZܾݣBRI j2{ZoӍ2jLYoeFr,Io-[?BrቴJZQgξzUʑ&+IІdz&`maX躎tE)Ps,)"|+8D")RDcs8αuu$ MUޜɍκea!ƛ(j+z %QK-ڿ #ւؤ|3A*Ⱥ0s5׋\'A*T 4݀-w%ļk7R"+MJ !Fwi iPE+$b$EEJ`pFaDJ%6X}`ƗJ=KC7F"SJ6@!xRrFpsgG|L<7j1&T&7XW;?!j}ߊ2E yg^Q HU)a ?Gr͞9DFbkWs, fѯJ;b86$<X֕yb]ҫb)RjD-`U%DM[Ո|_k5!_#+_n%uxe(2ƨ#9'cC猌'Һ'R)7.WNQaJQIArV+(جa]f۠FcA+RXʒIE9 3*0xG6/c[ܶ}Zɓ'O sI=#w|㮧k- yYWFʞwjc)ʻp+3 $ehQXZ-^QCRJq8ݷF GK6rΩ⠘2o O,AX-1EiKz\ӱMWKǹt1k4{ Oȼqeohc EO6n!܊ R r@!ٶmY^ 2U!UŻu囀dYx8P$bU8j2}h2 XaQ)`e|:\'y!D&#D.rn] MX5)LV 5Z"MRlWX=ۺ.P*K'h%ҠÇOo 74CSJw|ub,u2-dٱn'Z.rIQJ5Etlu(U"K w.}p r!JBgSI +Ʀ XY!7 Q7d:f ScYu]/[ra]Kn)'yGBX<)l8J*DWaW[4ο7CK`soXU}~(Y0uk̓&myK>ߓ{5BYQo?Қ~/<==|:$&nϞ(3JBq_%YR8)JӶMxJ#e% Dʂ,TxtvC9G@*_BMɕ;`Wǟ?-~H(jJľ"T#MB9u\ϼeNiX}d݊, ZKk=QtF041єX\rmӲV ݰ'&#ֹnp%[(J՞(MZ4dk8^Qa3 :H H]ҕʗ%wN+7%B"7b*\J i[AYi:~m,Q~sz*2ǔވIkdG< ^#KJޒ"J9#DDiP%2$,o{e|>s\rr+&qehP$)X%I)_L֛ PFJ$9O$w Qm5sE16`;qn7xw/~;W=(G,_W*EoKH i񜧍i lS0B@SadxpR&U@1һܗךcU _=7)Kv[DC~ny<kZU b0"!:ϋbeVتeK/QDcɑs KS" "Bx4"ЫRA@IdB¥cQҷmKGA#um#o kiZ R%ז|&HVV5W R[HZ!)lĥ-ƑʂTvb&zR.R}em44;w|W;*dyK/DR̾0-"UC*.lOc FJ4tqF*74@#B)zAͯ($& C 6^$-=ÑwΗ+///<=u\NO8 ʐJмgSV®_#r|pƉW-nsJ!A5}px~˽6UjSA>Am̗ۘI26U3׌l $4ӺO(@DL[3Vb[]jc-cєB5 (MCK"fEnZi1@^WҶ j)"DQlw]ّ I!zSJe t!(]0RDb9r*GLEo]Һ|iPM<zAo@C+<|y9]9'.ӊjmZ j֪Т1sfcO }r89 -aĦ&Ҕ^0QU 2&DJ,V]a7d b(^16}ut]x>|z>.'X/$+pnJmyDS& UBN Yмj?(:qzvw_G|?xyĬ6]i[` 3k0o06NB#_Ç $VGnײ:{*I"\@ˑjF[C$I Vv$t5 e[扴mKL k0gr!rrdͫ}mT) 5Z(¯H-"Y8eJn֎q<;yxo~MUdT )s^9t|9m|y:s\e)y֖޶̡7vm۠,X^ RE#),+qx,_(^w5ϫ.P~UPy9b(@=*IS粳ayį9.ĵ2dĒۑ~L京jB($^MoFE5-j߳p8Ǟ7rtAv>s]/1+c,Q"W@&D1V "mmlb)k?Ɓ398|Hɩ0v9XPRcCFiH2tk7+JvNdV c#6n5[!1W yc* LpEɌԆ 1Z( IDAT^rzat=r_=>ՇG}ةȨ_eqn\8G%2 DZ/vd{ڮfAZi ut]i3Y׍e~:$A7֖C!e_hoYѭcJX),hE Z-X V3]guîx>?^?pӗlz-Jh'U{YK <Aaq2*;qȫj-iv{ȸq;rq7~t)u-̴֖es=i2sSRzߪ4IDz wqxh:8tš.Jv H-_iQMfLDYjqRjn0%%m-3~[-m-9OB*)SH7y@"} KސGݴiҊ,r-Sh{mhGÑyxxqA+̯ IdtMapa]h:0hb?v1͎/O:]x=q9[.O/CG ZS uTuuݮM!S(3fmi8|xȸ/sDqT48Z:eZu [:-'Ǽ-B&e 2PvFĶ j9)[Uia8j(C Ԟץ^.sMGV.,H)nDTNP 1V)/9c^Y 6I!"u+VmD~9}{޼pUhFwGǞ? |8ۍZKoK0$ދh }qg%J63WWCVR-7p+!HTnP U>z]g#˺1_l v]FrUr!ShYp  Z1%0>s0oNr8s///9N\/Wit:C,2+eHHJ|4]1a4~ǏϬA(.KVIZ͢U*T *Jwy_7ea'r}O34Î(S# Ƣ1\ɉNٖ:YI~"h%CkM5 Dj3ٶ \WLvƶlH=!Ji-V04rS , ck6HRX̓"!Kk3_̲\Ina:;Ԡ0m@,VU ^Il41 OWg<  CC y>M\/[e$rȸgr/|~I;j$] s6&VR"+M^[u0>5l7vu"$(Ѳ4 *VRИ5"fjm}<-Ͽ~o~!#! 7vZ6PBK Uei #z2;+e|n@G鑭tE|u1T+3,42d3a:*ߦ|M\"%DS`=)0;oMeոEȬ㭙h5ZcenNG#ZCJ:[S.IRfG̊%'~3? /|d>L ۶虮'ޔ'ao/fk˵-&X#i;Q+efY病k2e$ޑL2[L8 &~b]0yx*!_u֞[j~mi"5 V9)I)RThXWd.e4Q`TRG22EaQon:)4?'>?p.|`:XוFi5#ĕHaQj!!^ٕ5\||0OlI)1=$EGdvwd_i_ovTuu!3"¹U?g H T|д|Ȕ]A5D [*TDrHq,+ҭ11tbu!3 { V(a^ݍ JZM?d` >\D7a1ckQ8ȵ29.t]MpAjO?qo{>}ێ}`fK\ ;يai3e5#hU|XӘ ';wO鐆h DjBhsy.UcVn @;TVPN3*'/o,-4uTU|]I]io! E4א p߇M\I9|Q7(jTl;"$"2l"L CUI5F3A51J)J2uEy0֐ݝHl:6z6=lOS95/G~/_>}}x:q8_%8U8_2 BR1b^ td v A ^gK)u}[ti3G89IeG@y,ε- b;I[wUs!Ȋ7Ț&⺗l.ƥ?5*2L+p_oxdخ⭭褣30#n_o=ӑ} z"!XM-><#s"96Πuc}x,y+ L-}qGڮo[ј fm'CrOi%2Az=N5BT7O>=Hd 1zYl]zK,èHe`f#3Փ4(Lk 탿1?6`4ҷzڪ$Jq3(zm9[lk>O_፾kZYJ+1G@}s4h8۞lQ!|\7;Mw:ef%x[GC?bĜ z?p (Պ< ekVጮ@!Y*/Iu=rFagFQiYEeQ>텗b+g{. lg>}{5ZC$8}bo!d帢 /rL>dYN[iۖC.*cn:@ADK]xhSɾ*IYb<(aR7gǚǡK'A"a㇞q]C{ 9&&Lj)C+]jƤ^5DD/Igq`b!mϿG Ù90K|ku[:a2 m>m"#abd@rikS9&p!r8w'K(4JPJO;0P74?iDf0j)P~.h_~EJ RLzB|hPq'`ǹ75TNO : ]}-%*1tÍ2(DmY{dysiχ9f{!#n8@I7ȭ}jŋ'ӷIy4*Lꞹz1#w}/jQ+> ܻZQ2C'-s5)-LY2%8.qN#F)0#)G%~Vlr=2(%JE)hNwϥ _>~u=B:v̧*oG\ F"+ڊ9ñO1yI@qn{v Kse)k,ѓKKs!ax#}w6 tùo=Å$VrQ:[MOQ_sZūG_hU7U xmH(R{Hm \,X6|>|=ׯo Fc*'?$VgsDe'dA$e=]QЗC_%64\{جfb{Ai|N Cx5VؔF%90 ȣ.ʓJ]`'=iL@vm#! xCY)*1}ZN%rHJ_!Gj2wzQE%x௩1) 0(E7@p؟>^̘O3!7̪$Pt O,Etn^wG΁hM1Sގ1o1Ռ˴覕a;`Z7wo_]Gt= 5QIu&m瘬e5%6sؼ$+Р QV mףqrHaD09E $ x/_ߥ^~C3mB'㟮A %uF݁]RJk\b r2i\ 7e+B u88Zd"_p958=ݩNpy;\ o9wj%tAnH\t }O?Hw IEz}>gy5g\{r"2X`ZNdJAQ%<F |$&\ #zeu_%(ngvE-A:@ru&8SddT~p mzy&'r8e2FzGp^*n zOAwईhYp!u>=Th R/)MH]9v5]}dh/텮qMMC+/n(lӻg͌$$JAMI.ĭcT+R" W q(AOPH |5ˡc[]xSxY,4e S(tvo=EzYFss[Aɥ;f] Sh'2 3}[bv5ۖoq`B Qto"[>+><=CȡҊ<-)M *t@F#ql_u\4% M"{JY"^>Q&3P0տc^u C&c+FYzlu|ǟIJ;L-puXH_"]2tSЍ 1.ƔDY<l7K;݆ҊKx tTH;ͼ(4BbP]\Lz?5t+\:Z),Vѧ&bDN%d3( "+9E6[#316k9.K:MgtBOݠ)gKDOy-JX͐c_QQY*5 1ͳK9yn w|&x7mL=x'`Y珿?"ca$w$lV=HhZhȥ \.K=Pws "g)˂z!`IKADƚ\'VJIb\Hx'(Swj`$~9U0CQ,K/\.5~G9K=V46=4Pr$^Z7!窱[6բf !Q!71{\)VfÒVg?>xZVV7(2-߶g^w5Ss-#۠;\wq}њd|6g:QBPBo;G@7{>osLՔ(\.tk28z׀1m-!it3頹^|-}N`p.&u1}IJP,̟6kP&X4K O=m`׈xr\. M'CBQk4TeV,I$̓K:/) Q>s* 9͆zT+(oepH`r)9>&ٝ9$ u,Rb#a`+wO|kaog`Z8?p{.cm * -CKr@G{0>~3,'uL2s=#s\@Y&sU$Gtph_wg۱;du) !ܺFSdiU0&L%ʲf&L&C`wQ75L($]fIgb\^/YWnvB 3;CoK-pMUo8|Jk&:7MFJRZZp7JJ9ddW)8cXIUR6a6/y Ol# esi# ZwFEa g,}bSVX ?xՆX"/Qdn$* '*ĊI39)D(/FvbQ/tr<?;˟w/,Uc(,pux׼O|9\{3MF]d?b5Ԓ[GuL۾a35Nd9˙0U%>i6t7\)ˑ &ƫDYN;ϙ fej>inmێ帣kjLh^OEaH8G`i@%ЈtكK'tD/571F~AtXWL+~o~?&ly(M tdѥ NO^x;vԽge3$msb*(x{۳d< ex^+òR̋RL>j\88uXw-#o@AE =4( ,rMQѴK͉p+,51wipmMVbPe. }jicH(TfS7K18>j}?)=dBwntK~./V7߱Y=Ļ?glT(E|\ 8+mϧ#=ǮwZؼJOcAN>Rii(P6) SYk7ޯy1JfbR[}pV:@TDYM (br~#*S+K;XVŬq9l6gWLW 3->Ը1iR$9(V%tJh޹Dٔj7gy`9əM B`HyvUpim؟[^_v|{;ݞ9_z6"IUHơ&G- DJd2T.`Sẜwsb8k'GN|>iD]=V1s2RN>*}6b]0lyu)t>bI*o9dJk5#,Y5aX<߿w~qy^b"Zݠhvײ; _nO m"qnv5:nuX"0ڽJL->l:6<<>^Y?aC ,C[8N9f{l؟[3 Yf%RSpˮR] N퉷̥iATQSl)FL N y5qY㌧e1LsEiՕmVcgQ|\#˥TOx\N8n=펷7.W ?X$/էid4;m{J('Œ#fEźҬJͺ, Mu)2۱vK=CC x򌪚YxQ]w ! (<s-'lQ\UÏCEG9)$SbeÝ/alL v wC7υxS-GdRZdr!6!wL*JLQW͆bwPבq5t T7;=(xYy[u=cXVQssvn箧$pe{`HO9Oٌ`>(P4chnT)B*9j(z *yls誒z<&秔[w:vԗ uطD߀EO]l\ʠL dsbr遧wx|~b5+ͼk01*c'Gv_[^dž{\0"J2RTRyDC 5]k)FѪ9. ,'̫wO!Zzo\;D\Q2X 5٠- ;x'"jO'9*}TS[2*2z 1T ˊTX ?HFN~߰۵Sһ5²jwmo;>g3鍌ȥt]B=LUUu$au`l|XO6[/O-m?H‹ZIjLTu߲zkM|xzk"2)f|>3H( ʟ wWʎhֳ9uE{v5c|p`8q89/\.5ms5qhnH1,Y.jz`^,l,sUbL+J"OK{ct^j%̿/Kh@>_EmriN).V4)Z,$2Z jE2&y*"kS)Sm )2C6n׼nEQ,v;LhSq:!OȻI6w7Įґ̨s9޽{az`^RT%ׅreʱshC"()rw+~c\Vb{8 v|{9l4]-1ב[b6aZYX',"Ӎ2+s5mҸ~jn^V([%Ӵ}˩ZO/'>~;ݚa5KA Uwp T*ͼ@Nv3bw^'%*K,$cj,k3Kͱn8'\ XVs)E%iɤy!YX#|_Zy|zۉƧo5>HH‹ HSsma;A>q: _mnR=5X@n&%  k*Qm] ?Z~v`򕡗8tӒwIWlB1xkP~e9J^|=;zy;\:G "O"VT1qϧ3Մ#_W<. c$0>|VKG1(ӆaQ_*tLWfehږSд)tc= 2 6F$KenOrezXaZj&bCe)t"mZEfK/og~ƹq["0IlMJxi}[,,|Vk 6bJQq1"R,ˉa +x eE=)ϼ|]߾p|}w ݥj3e!. 5"Lkc Ɍl6>{qj9c6-39 /-ooon_e^TwcN,n$c"2J٨5 uewaQ1v0<1D@]jSlLɬbRZO/9Nl<^Z:m wΗmE3~~{ ^ӦF+H-62AQnfh}F7ݔp. Jc!䆜f` J)$TVaCOf6Sڧh5.>O/G4}69(o:Di{~|`%:m ,fnYWP&RNġk{ZJFh6ZjYm y{nϧ_qپwIMBCzj{J0 WM@bZy|f\yyr6qfƤ L ͠R;'̿//[uCz^㔒ep bSrVUډRsȘ/ Ō(v5&8 ]Mx^/xzXn3e9I@ 16Q cC.Xy$豴l~TN43\ևާFD(4ՙEV SjrrO> w9m])s&,fBʽ:{^^vt$O-2VY) r2a95lN3lYчW"XacW*QxA*u7 Ns-VYPK3A*VyALLF<:Vһf^Q5уQaRH!TEfTҎH2Od'O~aS5MS!19yV,+]h#Gʘ6QBAKo3H'ZJ*XͧL˂l&qT3=og)CI(my}8crdwY<>?p:h@Sښk'd$DQp2(*ΦLf6%MaYZXtbXLaVEIW~#]g3"G"ho"&5X0!(yli.3y.S n$4z FMZt@O IDAT_:~tx8;^'EF +E .rx4gziE- x]1Q 0vu*'SVQ&,I^ #FHr-{N=q}P)2'BiYLWMJ Ŕtl1a6WKXZyMs(ؘR]\_gCsi-3!D+Q11=?4[ =xXZ3%3N PJ F] v/;xٷ|~=1_}<15ܸl &O-JNZ[9ku{i7W# O1s@N,BA+* ǹA 㮐N^vb1$ūo(o}b}:z}}_/-7}fG5!ZbTYз,'+ pQbh:=ugʦ]?$L0DtٔlFex,EL3NY,yI0&{L\[fKڶ+. iΥ 4LN˅QI*MUeƤ̘MK*yj{naY,KMPgxm㧯O6rbbvRH=sKI[ϋ ׼xؔ2Ѩ;dcyy΃O'^5S̲it!5 >9ĨI%l2i=!8gm~=yAH)d8ě$Lf qΣlD]'FҀ$xL6`l}OA :@^u-Kj" .+6R0t( ʥ rP(( pj۳LYE"'ŔOR$t6xFΦ/:K)Mi\s`J(2Y=5ErMofSU~i#*/$` ϿĿϏݾ& 2rD*Ĩo,(F[fcۖO/{7s慴wSE^a-piX> ;,y~weIe2ӊCml3bB>0vRFd]soN|}OM#XppĨ~=KJ\#wq Z}L.kuH\:^:R;Pf\N[\W`ȊJyKR&Z֔"7Fy9u;,R;RsizO4Z)Me! ň1~N 'tR˛%"#UҢ¦, e^BgA"quZh_:+oo;.2=lUN6+љA[&CR5$sЋMi2"}]ϩ_ d>Q4(eZ3&,s7 ѧ1IYVl o2żP{hǗݙ/xy6m )PA(z8Ƅe.zB{B_\/߾;>dậ+jw+/g!(5QQ6Z9*ͷXcwX* `f3#g>L: 6j ޤixDF a>w%ݸ WrB,6ΉN(jR#cp/'ֱ=7fS48~ UXx,`tV\+AI sEtv\ 1>mdI@:r+,錢Q b/B?|s Gߵx{]Q9*匞[=J}X*qFwЅ!do5iaxXÆRf fEF@5eϯuǧm ZI5 *Ӎ- 9!FTpgLsrEYd]U8-H@NWFȨP c괚3%'Lg뽖,K,ݷ:ZR@M6f|1\sA!{[PUYY)"C-rωHPȊCcMj4Fyw/1"{P."U4?/`ś#44*K u;DN%+zJstHQ:Dhm%+hQ3>B"5hWx2.Lf)̏.t$5X[fu`ޱ\m}sf*6`Z˰EbHT@Ӝj EkE/}e*(V2 P2WE*CE?/|z(0;*^]!h\.zduC}E䲒.J'X>Ă]9R*-C6&KAch-*˶4DNz [Ow+>^-ؗ5ashW&&,}D]e䥕ek ƲږJ1`"*y$<טG<vwc 8Gb̋]4Q"E)$VEZ |xfjob`̑`$,ٹCm M5$pY[tE E}d9*/ xFTa829Z'0QcigJ][ LgnIJR ~-i /*IC4^g$`0UK QCw?~g¾( ¼RU"]qfrOH yg@ :}mfiJ  m+2UM6-xqxGUm)AO$}LIβ\,Y<=Y-ٗ;ݡ F1.GnCYpM-M=u?6ϔEͫYNE]46:B:7BrU?>T>rlԂD@`u-siW";wܷ\a=p7Vދh3"숊ROvZ).m,j|@)*`:tg@ȵFG N\1(@SU(m\&.Rb Q`uq@z 9cS Ġ}uGWi`G RU! /f4-8RA'2ho7BʴuE[=&I;=ä%$2,;qƈV 붥"Zf#Rzn R\*J>T˰T-XKiiJĖ{ v]$|lv%ws6^uN{50<;JzhH:_Vi :p*(2ry&<4=5Cu*FʽC \l*ik6t]!w o&4xW(N_ =JYq ejфfi{֡؀=ڤ(!^F6:.Q'l2.΢eJܱ>$\关 L2\it[gV3$gYB2S ,eI3LZǣӻ&"GcbɒE6nkrNs@VH.EOd:IJ\bHxPzZk1Ɛ&MM"pu%yA4xyl"^eEyGPI{VDz+$taw8ww,c^zr ˃Zh,/z]_(./3A=ti5'>ˑsk"h0rJL2J>Y^hB ˱XZdj1eEҭHI#2nZ˕Db~yp[叵bU G8tS6MΝ,vYW.DKD6//'wDz\o~G>"X{C 8G,RmhR`kBMƾY'ʪ$H;=O *=+mP8S\mi;=꘦GdQqp!퉖!;*uI]WԇaO[WKhK{rUm ln7v8YIOGǿ/21YSѮu g3cw#[4kNd'pEL% WY·O'v.xF=$m3&wN9x^X]xTݠwH h,%LbtGk-I $[Z/?cCVj.^uRs]<&G)O\$~&Jj%~<773+4>݉ĉM' 0EGx{|O~7e74-ipu-ŀ=, &csH7C%IhQc39*M+ǪG+4vUESh*Gxܔd,$yFw|Nok we Ώo MgK*k\Tcuy#Ʃωn eP `lAIG ڣ}JZrE2JgCn1O{r*=q$R&iiW>856>lZ+qJXr$)Ebd"x\Pac!l} [8%{#!ʪNϗE"Bǻ}ScM~ /Et;:HR9@+Dž*9)"1X~38 ?MV;rGҔ[Ƶe7Xq(E|Tu!-%77\b<꒥Gnñ[&D|"MEeeil?lxfc~/ErL=\(*AGCPmS1)ItH {\ y2gaٲZ-Ntۉ^Njg6(PB)w5ac ET)axV<q _׌'4wXB~dX-(OC:>*Ʊ+clҏR6FBEn2FqȋxyMx&Vo O"hO,4c DyÌ^>a<, K [OwT9/QZ>b;)y+UgKb\:&mؓlXiY2_.Xl+`ڔֶ4:8NgHK'e:q6r>Rzؗ RD 0&ׯv|Y<#yp2lI-IKS]41"YD@=fGH1 BcmR7%,MI{Xk^zoK.rg"݅c @a._W gwa"vr<+W;ےjϦl@=midڈ6QVdc4(82FCPBR1Y'Ja5* ؐA'5;}<#O^"ܳ ˧)՜r={yN/\*9 b8 Ow=pM4 6(MMC6Yy!@bdVvcX-X[!R]n%(Y&d#@+KǗS%hszڦA8Ֆ`K ;Ll>Ga&b0QWYAR'7_}ŷ=:NM NF$hZedUjiSݻ[nVl C-|iU]'K݂a7g8p}>lr`QѶ0%]! 6狃rbT٨H;Vӂrj1a8`]/.@6O$A$qPQGУ(DBQZ'XT !~ }a|awӴ-!)ؔ ӘO!-=hup}9f_՛>þl To*X=߽|j)R/)pVI Tҁ$E@ w\&_ƴ+y$A%)Y#4#͋4{<8SتWb$vFpEAR'ҿ՛W|k/:# =<a" k曚}1$ʍ(Gf,74uEtP:vh#^(.f#\Oys3dwY9%Y>HIV@DS':ƱѱphCn_ٗ,;Նz{%~G|H| ( ֓D% EPI y?y{O;ׁe[Z )E@o1=#sn E,α׼sv7&\\L"A_fF0%<>7;-  qH,gv ~mGR" n%&U@b IQHj"N,>VXk_YW`QZY2tRHt5R.]o|7_⫛ fpރꆆ@j:d݌`-?|zxwb(|Rt2CI^rVI2T"3)^'ir_aY 7Cbr#G34͟[ujR$~ϻOK>/VaSvC^tC@7o詖vA10լʤ#IL5աfm/V?ι~ī!_\ $"1ƚ 5 )Ne$݂ΰ+DMN 'ʋY*KZK9&V7]ፎM8ykYϰ ֟p%I}tL!:bRG g3ίyo|͗/>q92MXǖb4M5K >. )u$Zp-Cq IDAT)C0]aȭ;:}LKij@]zyzxxyfb¸B*6Ei1I^S?XHRMjM`erbtv-8kNQ'yxE`:jAGx$F]$ն !@jH$%Cz7D4HEB,AZkufAQha+ee }h >|2Θt2):&MA.ѝs}fK^np؜@ K\tEiF`Gb  .}E9]j~nqPEP̳reB P/5/x}9rbE4Z%`s||w?֋Ӷ j6 a8{Rr݁V?86ll C,589wMP4VU2a_1gLGi~O<JKhU()G yREh c_6&=.}!rN;YD57r= hש\I=Bk9ЩYJ'I=_ІkeU-kj m4NZT cd`4Vib[XnV+n|8|rx4bL` tpfrsC&gg9뻏T;~E'ፅ搾 o#.DDbWG_^\WQ1@^.JFz*,HNR:>)ׯyuul0g\(&bS&9?7|xXxZnY.ʽQ)ِ>g=&Tkr%7RnXk6U}Jj#&j0I'36+!U-UcS˷:X^B'5ryk^VG:$e FvވB,ImS^R! oΘNG:|2`+u4f PϻAmas|]|:<4b6.WgӜQ?#QLIgh`w jX'rI(^Vn`|iN7S|x\s6qy>ryʤ1"OG.F]Bdx09 hY*%u+< (Gqdz3f[Źjj1ޮTs{&o&rHE JVVpkϺ\ 82(E7?΋ n`/||XaŇ-{vRG3Qi.ɒ[7MEzuztzx`kB@R z08uYφY+~C+٨FOw|eSK 1|t;HRdJ}KW6&zGG݅oJRtxr_\p~5fv9d<1 t2ױv@Yv/kޭ[lOIYBj:ۯ/ !:Zl4r2J0԰>7& m]5$1&›hY(./蜽 OOkwO\ߏyu5c2dY4~D'x@D$ܤR-6 öZMɩT0ma\2?^-9췔=aKBUF}'^PI)ɠb6zW_%\oMm0Z]./f=k~w6x: gB]$tOC-=FҘx/K(0є˳kn_suyxڥ?0znQҴl[l,C oxYtdPW>+~}7F JQǭLZw}h&4IóV [Z_{gv-e]JVՎOsz]÷_՘0g2 O :oyoD^^1^v`'b3lUF:Mv)2MbeR1 nnc]Sw[[#$jcyN$LCWo\\B*:]">Y׊^c":'-Z`J?Sy͡pN_kkRڈhE&!8w9ۢ5yƠc:pq~l27Cj O =>|g|bt]/ǸO)qCl)䥊YS9pJt+!$/GwH! ꚋ9gsfnB):ݔgDxSf#?tǧ J%!ŻL`wp=7|_b͙4zeؗ-}vr<.iѡɸ>gRAQ-m[c[Zp1qɣRFtZ0A7|3|}ِ0cГ]]٢G><\[.rXK޴Ckأ7/=塦+bݳZ%M#Mb[ЁEp@a:g6RrxrHy\`8<+\/HtĶr輇 i%;8ӡ =F=m`&O3 =*xBeiJt3WWMGt{CF.Eװ=k±\<=Z>?+8LsQҋy$rnRfZQAi0!& &Gzuŗo8lg:0v)$n7Ť^vPrOw,7[R|4*&U)@f7O ~3fŰ+Z;>>徤 d(c$B{چ,KLfS"-eSc5OmɡYCe;ނOOZqzؚ"NHtof\q12ɼ.q4hȒ_x$ͣpQFzp6РbGhBSKUiphihma6$F- zxJQ 𸀏w%>-x# gkp6)p3oy}>(Hg|1Yu u ϱP5)uc9l]ސ&McxSXZ籮A epvՌ <7\#=ET@|j1b60|?[?Z3?9 3 gc aU{a_+ ӁBkrqZWi |IXS*h^*%p[K1t9KSHBE~_r{W,#)U4f\:r6l͔ѸKZ(bG>\0#q>Qcۆ>͹qlbɵwFI1 R ͂y/nz|ՀWĜ8$IbUh~)yzl8 4h2MkVA أ5*>˅rtTK!M *et?:ph2T'Fh!s!~":h4?je%[?,yyǻGVm[))C%T'TDpZqM}ܰ\,~^o.50sgSV gW ,揬>}~'i״xki%I~S4GQ g '\ι R=FE50OEDywbo)ƑK>.$8-Ac*Zi/ltb}wS[HΥhaJrtnZIrv`H {4A<#L):^V?{/AKhFO}c-렒s#C } H ̷{q>~uaQ,k'ycUi"dn.0r>HyqO/8-i!>Xk$5bTSfE, g2fc#;>S }Joĺ̨hZNP4,X 㚛k ҐzVt<3=~zP-3qv緿y)gyl-),rx\(G1LF"2dvr_ss6o?9Hͪ#|TP.X.K-Þ~~w,+\ȬVD:j!ahlT|K7[\^@N.~:~"E7_E'^mEׁ2_6<-̗{1 98?K8+aE W"`/GҔqZ$7B4rVLFJ 4mjWS{ڠ7/t2 +tФDE*}8yK@ e*QN@2ij9!Mn`л5Oͮw fӂ'u8?=營>Z,9_^\*ţNC:HFqs1a<|_~fSV8<;QA\DoAyLnPI'ph[ʧ%Y_79`9uh`(<F}1a/qݞ~OY5XDrAq$)yV9%tzna/ FQ/c'$3-y'&EzʺIbH2 O ᄳX59r*=ߨL&ql3~: [+ˠ^̑]YT(; ?ݖGuM4Enw͛wqqrjvMEVt$o .8T%Q E;kgCFŏOÜÄɠ^k6׬[SbԵpZr_*دiRLEJ1t3݄^FŨggH.,q$#8$P6n[?=|Ů&-d"EL$yY/EJ`Vi 2L G}w`|~xYA~BZF$"erw]t{ϟ7%Sph*?a?ج Ӿ㧘~TA]X&|z [$ cpE7LdkCfufِ"+1O&Dh4VqzCABS# 2*Z&?|Ռˌ$cmMpiW -9Pٚ$XRQi^Hص[)1 >PAkO[χqY17)KMc%vy9r694rud :=ϦܼzHg$kgE[xvjȈZ_2RlL'*w5Wjo b9n8nP{Po*q;V!6хS݃ B)Oaڒ}nnwl# ~sK+B9*U*d!(GTYꜯoFC`X=߽~Ҷ佮$pkD{䞀i4u%UI}ؑv z3Q&E#.Z36uMEwm>?^!7@\)-w9`g4^Χ _xse6"r/S~ᑧŊfK6]ΦS Q F ~c0j;@uAZuoUVfFf'}8a;՝ DfyٶVIxgcv$fI9sg5?wtt)q` &( !A:?~-IUgҶUMaꪠ(FN%1uTA7_b=B{_T yӰK9W|Xgw{OR2q-{T3bgs.ד{&w3 v9:֗Mh$HO/ޝG>|tW8$Z,iUe,պ)ږX0X,F߭ **I O{&jde)Mh}K[5s(-*س7YFW/9p7)늭myzΆ!VjsGT|:O]OX-&J1&п"$7ʼ,U޸.!;{ڂbQP慄bP ^~ b4ыÎI伓Ξѫr`0}6 ES9YvkHRCc%XV2½N)m$Ϲ=n8;Ne ! =v7{T XRE܄rWYoOo|zCĴ!i!,yˢM64uMMMQeKZ/Ve~T-L +˒quv-~v+tݯ U(B`)=-yudbgL-vw7)K^VUI?$Kbb'5Fkw9SSn>Z%+8EY74!A [ 0oWw7.r\YU=]Zs~y?֨xaBW|`>\u%Y[.P%NKا 'H+ %A6t湺X:*|"k Е9yU, N$`x돒 ] `tk-i%1ښ8c(<1^뱽h# PƱVcyuW7wQ68(Iu ʐ#D\ذj+h*g)Jz_~ΐj>.#r!='W@Ք@?:*NƫM?*12)[kEkt esq{k.︽=C/7E鈵~xKHW6ДVL0[yEa2f-JDF`CQh!0Z!WEN] MW"ʺ/i=`+dZLhC{d--(햃vFRZJ!nxW:vAu^ԥi(g]ikCKU_SQ Ŕ2t.i%N3NBl>K$WJ*ĵnBѤZ^iʪL .9f}Ό'[lon1Ft:anh o8Tqy9 |j¼G(&IR6Q"as׊K+2%յu`'T3_\\q;gFVf§$l^Ru_I>l'ZyCg3Qۭttҡifʢ~dx*z}1 )f9y攏Np7/hLG >D4ZД BU1W.eZ%)J4?}%MJ"fUut8gɲ&.D% m)ǜj%h$P ~WvĿ7 ,5Wva)xXuҡ􆤽]'Jb]jQN ,+1% [Bipu]ޗ[Fk}F>N"V/;.ncI}Zz*T m-9q*" ̩*Iqrr&~tvyϓ5zH4z%xUK/Z#*M8IIٹg,UeC.6}2J"QgxG[L5UsEÄW72GAJıDٴb6ϙr&]MlZbQ6 ktQZhqsl]QsbJ]̨u.'Y۠d94m"T}UUQ9uU(:$sJ_xӝ^Ȗu,$%)˅PM N ϞqOT٬3+ZH؞p &&j _e+ KUH:)qm7Ih<$q5Ɔ/hudh`#=9i=zNI"ME#[eɬ() +l#8&Z64EAUfۖmS B_68ӳs..oȫ#:Yȕ>V.\L/h{KZ^5Hp64]EDd3`}C"j$'=d1^qPm/Jjɨژ"Clj"F^m( MES-(",.S|J[-xFYBd]ph骪e$e*R+Z5.Xj0~M{y5^^_rpF dW[ۿ5g_x6,az\`Qj$~ֶoԒjfѝIOuz=a,PJRY.a!*™ᰳum0g˨3/6<[_ҭMRy{.xbASUASJkmB2uZĀ\̧\ps;iʋLJPP$*/Ssydfn ~q>F[ݿ 7 +XyUJ@:NV2#z49kpJ3\um()mS/\ Y Vx}4Euъh@'[( pYQF2RL g*m9zUI]4U rFr!h׬b(NaSM-ڵT{\]3曯^àolpm!zOЦAI gWt#V 9"3))`rDу&?7Em-޶HڦjKIH ]VϙOH:IO!:qxmpRc F+\[aۖTIŸڰq&+Mc:&3 +y:R{rJ\FTxWU+ՍBDyi3bgoo4^N/#2@4-4m2Bj/{iv0J،M򏢄"m+H@9K:"sdj+C+o"##elS-2X.4uIU6plSHK]'2]$q"McW*9-mSï&QX(iq, 4-nF<u^  )%4wJ10"vDE/MdRV (MDSqHncr}3(Pi&Lƺ(馬l*z`mT+9m—eUS%Qڡ7\'Rd&I*7!qklh4ŠYY1Y:um8,^Eh^bN[uISŌf6c Ԓ'"JbZpxن:/hAO K{mUJ!TמE LEM-6iEʨ#1*&I9'X0QSZ}I mJ",F*27R4Wh/v%R-GrkkWeAU4E.ZB&M-6ʅj$h?DtRN?o_@dI a]KLh&d P \z>_L5E%j*mӖzQT Ed!x:L.lKzh?\p VBf4 )Ϟ?g(CE7`skɴFTshp*8n\s7/Y5󲡬꺥i- H$ba{g!{: ER xobû~BWY`޵t(+ΊeK"sZ:(VRkYbFH;}NG)iFdxhy!6V⇵Vx*ӏ2YAS-lEB6fcHpֆJx{{ݿ{jnGΊXa/Έ-!q~=솓o5Sl#ՊDgjҸۍD M J:$>qA9J3qŴP.W>[DtiTT^"lSlCYMYQ%M]J`{sBE)\D=8IZ,LDd8>~}ƚ|s=SP8Oȩ8MV"'S>N'>d$Xâ1N4\|2:eKyt-vlI905b؏8,1lln;>p}i\prq݂r$`f^E^IWW+X$QMRbۊ6b뚨$i J|q@X9aV ihi꒺ZPU Z:c.:K+ I|FL{n|A.`=co;~c݇._iWpՄɢh<4^BlBBUpU#\+ᾩ&%_6/nrhMIM{)~>nF޴>XKr"ِD'DBlmDoX%(q!J%62έD\|8A+K[ m2ǵ9ߗJJ.qOC%[{o:ۯxqK[iTz^{jX(CQw͗?§S.'swQDTΈ!woi* @sR@VQX*k& gS޾]yvaOY65TCPq;3Ⓐȡ\-^Y5D20y,;A +e05Mż[?I3TjGi;ǢE{޳*i[46xF tf/vҎYF.oxla?Ym"-x|mk)t*yȮ9f-?ɄsnXw׉ie2A|rCWJ+P18ʞȷ,T-T%r :DQM*t`,'kSy,4״غckQe /Qޕ_@s+Hl0>߼8n=1wMnԫFTJ9]ӗ9'ggÙTZb)#C_C-MS`:16trC/lo|l[>l>ϞX qYY ~L(E%}nn=w[3t"YN1Fq 2dуd#crR55m%e-l&ZPIDál#W'jזrRIEz0K%/ lΨ>{׼|HNq/m1XHJz4T$eQm{\-IŢhi}vPuIУ:,'xjƫWWtuJP"T>T vby%C6e[9F &E\&mꡯ &FqJ?ZP (ۿwpýM6È:궥(" qr6˻K>]-Stz0),+4 @[u1F.by {&LL9錋3.9 EHY̭>Y e3qXp:Cӣ neT>"tpEpP!W };'a_n8SMaIȺCvWx]6l 2HSXS- "c2Z*y w× ?ćh]@ίx7Pv;u-e:j$^aU*R?yWmYT/+ ?(q$_>pųԶ!I3km W-qB!jqc o'4.>/{Azh=izE'D,䖇at ?;fjAK_)[Emҏ U>ez?g:[7 Z'mđ%6vVI7$QF'EmLo5Xu8W2-꘧OǘHXu% ,)7Wqsu w7[(&//B!ܳX/aD$ކP"=j)GAb`Y?ˬ0Ayu挲[!ʻ+HiY+_}[z-{ }6G){wh-Uc(Z| z}ɏι(el`ڂD[Y̠%= eQakq.fZnHZ=R?8⮥V̫i" i"Ecs,PWx0.qR6`b6G`s9d)$'e3hn=ήf6ƛuEEYWd qFQUM>>&OOy3.…7IFO'ŸB9V6 핸m IDAT ۴DQDjRDmZ[P.>rs7{wܖsC'ڋllnquqE*P.R\uR$`իl$ZeU‹v1,ʭ]J=,<N+ Wl:9\Hxc RC<`gS[LJׇtSC/,%ed·]rÛ|pTXRjhrL[0awsO8zz~B+JHAMBY8nﹻrr1-e^6DrɷAc}AeRݬB0Jt\ګC"r,gDF Jkf->Oec(hg-'g}_~: NGX=lUFS;`1{~NPQ[i624xR NEXc11b]#8/eZ䭘&ې5H>N8zm;W,.w7PEE#9r*IR+ͣ&PG_𪐈r&pJ c ךހNӧ<{}vwXccGjb-Yugpq[ןrzu2 K[j?|ʨ8SzN&>ƺ 9> ؅lន;>]{+(K[0FRD5ֶ^)򵗰`9^C]!ӧW_qW/z/#wFG-q"XVX>],銟~lݬv Y:~6绻~lz5u#]_&*bwQ뽷x`?0m:JE(k󂫛);;c0Z3ߡ*3eF܋}7w\p_cv[Lq1ڄ@A- (̩{5~2 򈊦!2fmh]^xӣvww &1H`R!IFK8Qt_>~_α(ZU֞5T e~ր{||ýMFØ4U^E Dt {FʺEtz]gX[hZ,t+_s8tۊ?/[ˡwLuÕ|n:¨5# :H0?W/q=f}СC7 3èomcC=oޟi#]| wZ$c$X2^mW;|tFRgi Unr~:/8Rc4㚒 HFG* Co|s!I#98E6D(%J-J Wqt|ӣu8&Q'C%cF1[_2>g1m%yryyG5E=lmD{m'A=z[=ŋWƀaB#^ih#9SfE*(n`Y0|_=d4hZE->슫ɜZ[!ۣ5w69z0V e}/ɩUvP/2`&A?bîp#%>]䊋)yEcTg^X McևmN7Y 6:t|EϊH1%ws_ޝO|&R9)CiZ۽5:fwwr=W77__1Z7bV:֢tNp*蘦^જnf}q· u^e׷|>;%/;x0&Kӕ&Sfkƛ 6voq7gnifi C 'p;(`k%K/ {ϋg/x9{{lml32HKjbD~eq2[tny'W\̸Rʲch[OZ2[vƠٜ8yN/sk2{YG!~2" "jķ!ݬϬQ7bAj}*֔eyIQ5779::1{}zݔ~/e! ^ZaQ~tŸ| *mkԌ[WóؐT S+hU;7|iccJ\v^fkuDQfN8ve2?d0=4kH+4Ұ(BRyvXPim j&}^dW/ RA4FwQ&E'}Ѣ`+n/N9@q{ZAT+/u?j~=4LQ>XGuRFOx_GGln2}Z_4;\ʗdJP=D5tJPH.S4U#tL;I"&]ivB6:iL[[M'&" ;-w {Ƽak}ʺ1ыob77|!Kv-,/v @5i-!ݺ`&ސxmK67 D^M/d'k*%Fp×oOo9,N2Y COYou.%QeyrzuΟ^_zMdmi|1_=ddPQsyRTF"!I!N%Loe2lwNRԞqjpR#{ ἴ+]`ƘÈ;AYjnmI;C=g-6_\ 5 z/be>2uݍOMݰ([X+㈺ ؅ |yiԠ,M:klmmeck͍>nא%,uDbTў4P{&Sz]hjixR*[Sڒ6;RX/V8Ȣ ixq&?FS4dc߼?SNnז4UV~s^3Fui}oۛfw,&wB4 BbG[fYqAkHtCcֶ69:d6&_quudr"_P%>Rߴ$6FE:I&N1[llnvv⮈{d"M?NTP6G5ISo>_ zi Q*IHJ%ˠ}ъ~/-|x3&gOWO9>5p@b;bp4Mşӏe j$-/g'|:i, K)]lhgGlft3 N ovx{ϟ~|Ç+W;eeez֍X_ۇR;klciHõDJ&zJ(()A̴p_ 5Yk6FCFq*L6Db e_ rLv4i8]6YÚ4f] [hm kAԴ:h|Q.ݜp3mu'#jĆ5mPXq8,{滷C36 Y乸9Θ^'f6suolPTukiVME]S9mcWR3I*Ft}:.#t:>ݮX*33&Z].5"b>Uо}q`HYOA5,GR#F:n\^pBIh?Hw#:HPUO7o, PtPj 7 9N>&.Tz67bcѪbc&@`^vxnDq~˩%FՌWw}C/5̄k^I#CD$.qϓ[UK 065eYSԕ[1z](f 4t;. EM̮=&B&mn,(M?^gng5q/'1:U$.}ɋ 2WWĿ"wZ?!c{^d^P;Q\r.w>qdH?q}Kl%b:3uKնU+]kjZۆ$G{Hiz&=DFEwL` oA" h9r\ wGtWO"twSG;ճHd*EٱOTyIQf3"#3,B~ f)DSz}Q-A}?/x]nl.2q(q06XG uco%j)7w}w߾ˣsNzv)l7Prg|~G .\F,:d!3\[^//0gTLs~x.[S84٤ yw2m͌Vr~ٴfyb ة&9V, `{Is凣k9\KD*/맼99|?bɼopPb5(G3*Xk|$c;|xZ2C09E</xl_}-'ԓm;E^NQCA𙌕U"Qp9$I cqo?G6ݏڻd08ۅ "DCZPv+a"O:fh]ybUC̻Ӌy@aQ%#8p/2k'VЛZA]C^K86668=;zo;>YtB+EuD-orIf,//B?}?mQD &burA<Ϯl JR/8*Zky&)ĀQ%:RćHN/_[Vh") IDAT M+[|CɋRs8ѡ·@Pd0KQh𾅨(LJ\ К(1V=<{Hb V3PCVi +c1H+*-D'/\FVcN D6J*:)DPG.dLZ%oΈM{Aol)VTeFęXUbDIh_hdshyóӎ'aQ3yzȓ883,(TS&1n8-r̓;7y.mn!/wXzK}ϓ/_(JJUO\>d]ADy0VmP>@,=ͳ#6KˡFxX &u5vi3RKT2VO1c: :`ubjyL%nS~/ꫯqPd10QGk}ٞ% z:cPʊD|}ƬwW 2q}wo_Gy: *Z kf`dk5t666q 'M`yGZyJr>͗_/7R[hVDj.ODg9(к@lW"pǴT)IKLUDedPs8[b=ٌ30_DQCɍkܽu gO5EVqm{>}^дi=zO<{qw3K!Z%3Jbp`zR~Vu"/ï~í? Yŕġܺ1pCnSGH]mFdSF cJXyt~8#J$QH\;6%Q../-tVa H:!OiNڬC}˴7]筠rX 1R%;7x!o̅S5FCpdV):1:]EvNJ}g+(<7e2np+'=*2GGIF nk0[V¨̢@\":kc1ʤ0tAWNs SZ3ED\Rmsr޾(< u"f6p9tIs}wʵMf Uh[Lȓg =y74}$/j闔N.R,`B 6.AR:S(j:B-2S+nآ㏸]A5*UDR\Yt\3IH:͏u߶\kJ9M m&9"t-{& .NOKr=ClzE2 1YZt1!gdՀfF2MO*"h%. D/(&f'XjZ&.}%Ms!sI>|4 װZ?- B9K07D>{?8}"sj\(0*&_"!Yኇo44:x%9:>]>o*2}mʣ=[!+ &E _rvɪB =&[L~m}pR11Y⻞д,M͗_5=v'פBQ*=(+*s\|fEfLܼBFcY1uEInL -L -J0Մ}' 8 uĊȚ~@"AXv @V&+tf0_QkaLm5~V19^;Ǽi9?9̈́W[Sͦ\ J0RTL[*^HkfWxl<+Nq\rz|" i/FihW " RZt9yyFYM)&r %RzF Лb>1HXGf(V03) &-nbʆd6VP7 ,4N;Ʈȓ|s<{YC ̨~hX6gc9fUFyC ҽotM#)0]'hȍ<'g|w|mR6'罊Wt\!6CMR$RX&U@Uܸ6C[d:22} .Pw_!\ 7 w=Y/g j-Ĺo4eӋ|85DQ397t9/_1(= :\Utީqas%7bvbB1^,:C@Gֲ뚎zrA%e? deM^c1ybH T-K/<Al['7f|"gg.[0d`K)<;C^:b1 [VxU~p1;yޔ2uK~w;i SG Sz~%YoU0ZK(&Wc$edUSMiPow,:e+=eII!L E5%+rirt^`BQ8"*8bLc0bfZO:.z^JJuv3+miXc 'iW'èHW Q0Hk$C1)@Vn@Y( I:iE=w,LE%S:V:ye~AՄW_?6@n39g\~you7ϸ{,3<Ŝ7{>삓'gK@P9Y\sn1eJVJ)> aF]{HIE Axր/>µxՐq'571' lcP ) b\%iz:`cjAG%7M"~6_6 ^!\>=Q2TuĶ ByAvJ^eMYUآ"x ҰF㜓vC)p&9S]Q`WdFVcꃕ,xIV)QL%Õ]10o]^&`5`2%(almlrqzҚL,ч@uD{ɉY|Nӡ?UE^Y2#>(LItWN={*Ea-X4 \\.麗|K2,ru}òY,z霧a=9=4/e]* V }/^crw@QĔ-Jc$ZXg!ZO$W+jKĮ,(!JHJH7V/ſ`-m7U'6#`S%؜E?) jjBVԘLSoKn,fXU˄G!B{Jv2V왑tÊD5FU{!Kz XG> >/˾Żٮ |Od!ir*drzmM{$cց6gHANȻJ0v֦RP 8PƤsE$] iݑUdpmT: )0 4ZЖ{M)M ,MC\HBJC)s. РXg(K01/\{b$=Õibп܏ *F*] 3Ba੫?9<[!< 0넿9$pW| _eE^RTT! Zs}L$qr+RQƘq #&va'r}Y6cL~*mTZ)ĸ*O!kxp6c^쫗QT{hFc\H](C{ |?x7!JrR޴ yl Q܌P3/}0DaP b$-F @kdu-kQa͝QAH&Uw*buKP۷nU"}c MҎ8 /b\OBW6=Y-ncK {5wO)2I0ebwoW|w~G;_$,G QDuYz0Š'L%=[\ຖзAf&$ls1[rȣnQ^j^=JNܿJr$q╘¤֐+,GŕwLDȭܼJ|S{Ov߯zLAW{TP1!%1Î"9ysи^.BrEZKMEh#]kWdfu!0!AY4Z8.>Fz/Qng,OqK}O-i4s.}ru2 ujZ(yj|F%UU/OpZ!AD.|*V$c &*ldZ2؆12#)N^ Ki,[ꇍ:e+ڈp!PS I}LY <1M̻]@JE9Rdq?(DKNJ}'Q̦?8\~wy/OF'xj۴wc=y5X'I/Hm,pq:+nmt`\2D]܈\ e l:4=|C/qCO^IJBZDL =*z*DR\q{hbbũzPl&%.أ:}5!#bwj̭SX.dU2v ΏOwrLΦhyE-9Vĭ\,# 4} _,\,qaLFYUIiqL VZFM`hzofdauʣG1I=ۯ~KQ,chZeҰ.-CHuZ"E^utC.o0wC tV&SNU$./pU|.ˤ)/gz!FPZ ;؂<z>{_GO.qv!5vRlb޸NADѰwb̫1ֈ"^Gfim9Ƶ-n\-ol`fRe)ӲIϫ7__cMtL+~r55QYA\a F,GgAoe^MɪZZ+)[ NYJtW>>K>7]f3.[οI׌y ~9$pi21Rjw7]5oϟ ò,eU rgbnk%zZ75eMtEU !/ /PDې|o34%.!*iE-Iu=CDd|^AF؊$7!yE)~vҷl߽ɟoͿ!‘HadZVDkV_"a:/ᒧ/y qp|ɼs,6,Je^i 0C#&5 EY8±l:\ (`Ai67L2˘ƳW1)!Î2~OrRg،xzevci$':wP&&Zx>a,k0چj BRCɾf~@]iRת1К vɋ={/_repX!J+2- }Q^˞ˁóW|i]g!(5^CNVl)jT^f'G\IvE+- N]Q?؇@z =oSyWÖ5y=NaWdbhhyrUd6hlRQWӺ`gs*v&lO52>(T`K}]VS1_xoc/#FD3! ϛGma^ S0b_L(kQދŘUATcpcDt Eo7t]KX7s)#r-E -,7IqԍsȌ:g{>???oPWfYOi,oEem`P8:kxoo8_6 Ql:!pWe[-0*E7gŨbc R&NT*6w9:ckNНC$XX=l[S0Jv\qȄf(ɗ5:(Rδr]x:c^qtrŜyC:"p>0b3jczHjsfZY7mq nve"5"IIIi) C硬-wgP$+7жf^,^='6y5E#W^tmޒpJ{ತwbQy.1ki1>eh/17pRX;H ݂ihQ2/?d3'SA2h6?Oqe)deTRUDAkM&0t9^Σ t^ N C^[Ҵk\[ɋ@S2eS#9!/xqp[;<{gՂJUXڈc2͹uC0(]RDA7?-.2NQD+?3h=X]qyd=`\JOf2t^bگ,ћ46k]ѣNB78^%6XctdC$RL//%xdTY~Ã=޼pΌ͊,/k!؂aI]3Nbqq1jk32#A'/^MkjU8KA kV>d@x|gF33A=?~/9_t.\:0[J`ٶ"5"Vɽ%7kfLdBN=oN[&+^ۏxuvvZd"0:gpsRnxQ@%WlFQs"F/RGYb*4!l%6JঌË.x) {C;)l l^NG2.R<i 闢 =_%~H c,CJ)rʆ4-A}X^2{=>Cڥ.pӌF3E1'|c/CVB t@7ɳ,M!uid>oqA-b\JŒo︹c֌!ղUj:6ރwx67Kn^ϙ ڐ[9$%7'ų\8 _~s~ǒets=ȭdGe7kBH=ihSdf18\#>1"eaɭ(wfkc2L&v/i<咋 NOO9>EVJt. Zi rMn-13TUb,X Q)YC7D{?c#ySnX8悡dh.KCs\9]`:'5C@-$KLL !,|omq#>GNtj(B4, V+J{ o=^^r:`QU^P95\ -?, d@b E^Pbs+n+JHC4_E5DG~PX` l^%t6&唼QyIoKM~&i`2BxEQ(fFEA>Q =7ƽ~vć(B\ }#q!^)} {\euW1Iœk)ǧrvv0D Kn:`̐9e]0)36JV5)V96K'REf!<;zVC@ZaXV:51+P 2nm뉽#=e?'bRz:y@T%ݐ&k lAu63>ɧ|{ܿ{ Yȴ:%RA^vbwG|3^p2_Һ( \愡!7I6=cwkƵYŲ̆ d89[\)2#;M%Qa,ϩIH<@|o2_ۖLXTՌt(( tJQWdUM#s j)t>YҁIym0Y5!jM5vhEALЄ5'3aL4bx$=s41$8-QM9g^rɃyt:UiP O;%_F63NΌ69==h]2_CJqͧFc 2Z4 :)7*Vw^V:M=X_W'TE*MH"$fsݛ7xܿ{v xQdifA3^Q~|>_paQ|$0x?hՖmn\6eI}S8=;g .`+Bg<-˵hpeM!:,˦i9>_p5naggmMY̆.)($lS(+K){E2C-6|9͊PCfEc&@ $&k(YR u,^CIMeZ5'O xEi V'O݁S`gGF\("H,'f& f[[]P_)GӤ1+Ih>xujҳcR8|LJnZrT"z%o36nŝxC>vw6sg UD.@LQh#~Fa-,{;t-'||9˞h tV`9Htȵg{:egg[7o֝kܹVEɷlrȌ3Xp|| g_=~y#"&*J#a.Y!Y1T4P "ETox;#j 6Wde9^rCgEI^Udu*)g9(2] {!:|\f+FEP>McZ]>NFwmaX3?2H n{m>OǟG;ŵ͜i-%eUJEln ࢃׇ-j||óL1ADLf;7w3f 8*,gl֚Y n^֍]fEQ˼])lx0^ R:OL(MEiBԸ t|脃KൢaR̦5U] nQM6(ˊ,esBƁ9Qg+ݘǵKpɻ8~`L1oGp{gb{sٴfcZ-IKRS1bLR98蕗]h$q8g,'g\;>njh"6WTVQFC yar̢ 䵰˺Lˊ׶S"WV>129 FlJ~8(RĕoO1-bnű+9Q&\;O~c>x]ݽ΍]ˬVLJyr+;A,<8`o@CР3"ٵ Vf]p5>~->|.}yCX> {( Bf eq6[[טV%E&t˛ NƮpxOrڌ<+.(KG9, A,1PVԕ('UEYL*%e;)Y"E Dኤk O<{>y)Lr956efP!H1"V\%Ј10(Y)m=+>@w\.\qtrKVeQ8&ILe9yQ%Ӊ\D0ʼ㳜h 5pqt4 Bz01?7PGEk%iJ ,WV1Jj??у{ܼbVsA' `J3wr_|_|%7xr\Xi"~{ݏ= 5~6\\v\\t,sJ%Svwqmf)1FepcsȲGL {&tGl[ڮ'/''K..θX62ln԰1ͩm,L򌺮NfTmvr 2U#{]%&dBj>Gw# oA ,ޝlL4))GwlzݏHZ?|1F!YY%Co=ރYNAJL)lf/.88<p&3$(Km ˭fs3RfCY(JrlmLNڨjR\Mz!/p(=wj'=ČW!*lp;||ͭ[ڪؚf:sꓴُ)ZʉU|c~^39CJ=7RG6'%ܿçÇ\ƶ}/HY4w/~wO_r%S`rT&4^Iwbmd:ATi^:#VibJ$ riM7 4MK,JjR6 GGs~vJ? d*5L"yAQO)'d ]nPL6)gdMB^6ڜ8dO~3v%>%)mө r؜\ŵ-&uղ0l*cd9\Dq|TX]4*I1ҷk] ?q͍TS\aMZ<~Syy|04NL 6ڌ2t_^QZ~;||h-iE e%q< ;~ŷ<8;dG]1)LgrQdYes|TKھA[z &侐#f• !H`?ƹaExkۖ/:tƴT2LVZh[)='鄢0e,X)'= *C)A_~cKQCF:%?$pOUQWjBUEzUZk(ef3QG2#5d&%t>-tϊʠ%Č)m`t|乔UEFQEn41Eδ) eQQM)dl2C1/YIF&̬qoCf pRRzH,MSbf3wWuToG'7]!SU,(nD^5wdJJHGOƤ)|֑N@)O_ꋯ~/[>S^y6co&ψD3/)=߽9 M&͢rfձrе0.Eܦm-! s=gt(m(*:JAOx^Ջ}ՀMw\YL&d`6e~߰5=zwmwX^wtws!-DmuX́IoJg(%:Ʉd=ųzGrp֎Ѹe&mWr CkNh=py ?^O?e۰I\FR[RT9[F4I*#v-Ah$#VWqXuq};↛TL(\*KtB'w u8~$:ܘ Ap8>%kJw JIҩ`:s &{k8^q*ZvWAc`04n#фI|{ݪvph]ЮY,zUPa>^Ytl=P%<7Zf:ۖhʤ8GNϮ/>R LS&:%zbsxs#Nk^_6v  jnw^z ˠRK59gUmF8at\24ek2dw{|o=}OjINLkIROY'eK줢铧<{OptxD3|$A ˘]~~x}̼ sd_iZU?]iH}(  /,Ͷe暑K8 UpX i KΨdg{ [tcX5X$vMliVcKw2c c l Bfq*@eĤ9Y{ IHZYCSCdT1ܵi:uMlz9gq_*FGdK1)7'Pr۳ TGG#x oF:&]qGa4Wל_[,IƐꎔzjn&*9`*߷ 7Ġ8=O_姇<\0BlJNPLbZv1a>TN nf!wKVu!j=咮]:ڮ'doP7 e>[[lͶ8}vwwM,U%^39Vɀ(rDQw-uK=oo/PR~|9HJIYv%ߔ͝:,.떓+~<']vR9aK-OpzD\ҜZiI24 KËGG|GGRӊ7>^\r=XHCUG cb{6a8(W+umF6BBAx'ebk^;f``b=F@[FP1qP:Ŵ ZG[/#fj.V5!Ѕ4\Ѧj0%'+x}rCG̅0Jv~l4ؙ2QF=;BPD%za5|<'t)}va\ȬHvҔRNIU0*[;-;.oqAѶmŋšά<( &!2&[qPCn tr$Z$DRQQ^l?|=gKR1T.yine%0$Ep(#B7,}ҳ-^nYA,G׶\PYM<ɭ-?' \1jW79=bHuڰwxȧ/٧-vWۜ]89G/] lSudnja0߿>y| 1xYUJ35+'CR#\vXqؙl bt-wgtŪG|x4ckh)gXt.=Z1.,J<0\ȝM4P÷+zE)džʝiYsQsI IDAT$UUժtIT'KD:T OpEQLv&l@ IdPhL<$&GlơFb:ILc\P];VuGvt> M1I5 [bV񠐆C(,٤`4deRCpܥ|Px/{?5tQa 1)( A%_bJQ0VU8=~|~CX؞.(#XU\_r}y&ͮq ܒtWϟɄP)#ZUx/( [dE\oҕ3蘍GX.27)n;sx@MЦ,\Woiv ]]ã1q[cth0@iai\򂳣]^r3fw6bX jyq}Q2ҡi`\q}~b~E-ZRJty{%QiM7ޝ1f2TBvӊgfIYBGE%XJhQa;Kוt.0_幏1 kUeXՉѨ`XdT>پ4גmRN[t&%|7NYEERKlTU4dh`#fJB *<]uԗǼ=YN8??'yG -7lO| OmShH*K|ɿ|?~ϼQbYD|ýjPѠ`81 )ʒyY[O6SXU2KN?CF>~#U:f3xSXsEQXp{t-m *%īNQe C)3s,__q{{랣lU5YL6ii1Ax@HrRi[BVtP=G+*ڂPLS' OW@lίVpGoA, D-n.["Y AZރu ( SmS=TдVFa*2* zxZ"ٞ*-'5!NOP{ݜ_ȟ{ͷ߱\CM`:0Js]\+ɬّBhZq~umȁ\\_+\0* BtVsp˗/x耢*_=o{ίnJ绺8&ew:#~Kxu3E0dHNNθnj` =$vXB! \o8?;sE), uD4eErjY6*IS%ŰllO%E#_1qA⁁.)jrߤ"ub %GG897sք AeSO_P Aw1Jc2WUN \]9gWƖT`S!u [(ʑGF$H+B|hrvr[/l5R=V"b&/=u!ʝ(,$'=O,҉3;\J$2:O eDvֻBZj#>\U>.'Uf^eY>rv )C4,W5>DI[[lmϨ"q l)L W4{\$bPR( |ʗ<]v ӕW0_DNN|=?y֣aY _'E2LRz.(4\N\WKq9dk""{++"P>P&fcbnbҌ-R8/B=)ٝt@abk:`wƳG<z. Qd灥OJ6x^2ۮxUフV95\]`C+W֘iVfe3?y~s^gk"kdFZ\O]3^Û\] INUL)PaPT deD_]jO l+vgr\eZAD[-qR!a onW5ߦӤ pgoƔŀm?>#NMdUv.`#;cFy + 0*ŬpNS~Σ("Z x4 Glbϟ̴yt7uftHI[}(i%2xr +( {#:q0Q<}-a$͵\.{n\EXآ_-y59ʬКBCS;Q6h N=v<=:1 |A@Jeq}dM((=鐗lGMNEIBrsI~+B6ؙNN f3/%?9l-U:m)7'uDPf&}5:siV;; [ +OYGwmbרw@?*$VuNMF>rQP-WW^]>Sۼ RF2S'F,pQ70 [b>TأH =E"H:Ɗ{(|ʷ~bXɘC;d:|GEm3r(I){-߿=Bp麢(dh;i}km(K#$yd=p#^ֵa6A~OW<z_eV,3bSR,W-W-Ӫd4 &Ծ4وݝf;##yp{99Nι#`|lzɓ]5;#{N/p˫k1ǟ}`9DBha43У'FG /]':BŲ7(GCw9iYCn7 ~O҃BgZ1\sIRoI 8ye2,9AeZRy rԛ(΁5Rw-YQT E5N.Ѕ͎s&y}*#CRR[3E4eX65g {?`*J+زRElRQ1mfY9}߼j'+}]Uʬ&4{ٔu]K6tmM׬*Ӥ Kph+ָ֞ՂLJ;nɮaI]&I`ն//Zf3WR<ݸb؍PBAvV-On8>삳W5]Ws}pww%Ɉ)fjtɔĸneHr_3ut$]oZBm5e5d4O|Ǔ=ʒ< X/@Fl!Rڬ֞AtpSPmfDv]FQr ks{ޣ022\CG\u9w-`ES5T>V֤ .Z4V-cuSY.I6hZ^W3^>=)~jU(E8\ݛc-'g-:0 3@tARl6zwb!ː~MJ뭯Hё#I)ԍע"uN@GeI39OzIt-Ok}Y(M2.uSzj껎e'dE6#A݀_+[F5*B[N]jbFˉ@L.3=簾QW`KB/VMͪiY,ؚVXo&9 ߇:/^%K嘁 υBK'd hcrບYLxb5$@+VM-~ވ'kM[2,-;[#*XIeH%ǧWӳKNί q#ɘ8&Nb`;ܨ6)gYf8RH%t+Bsj޵$a),WLv!giۆ@TuX?-^&-o96RYOԃE9/nrf°/Kŧ^#m[瓿ZK;UA芓T=;1Հk]жD`XE 2"ҳU@a!_AI݂yxO^ɣCFlM,Lkq ߿=x뻆:dt pӴ wGe6[Yd]ɯKMzPR&YkjU%9 J1`"ĖV'^w]%eQ`~otΤb:xo\]Ϲ5W-xC&T [dGO^%C@zwyx-N S[Rm]M薄vIhk B́cwαn`O ~MZ0͟W}?FF/O8g//Ge Yy٧|xs-!~RUX\> @S{0#zGk1c XI)c _+{AZ%Ɣe|>萗/hkGnŔכcaVX%c I*R艾!eojk7jffq->:)-< KkO ":ȩO/u>u+w\L9ڝp0c<n8;_r}wϢv>bK`xT&w-R?UPDUrNIMnIj4zZLf< MZRA!$hr^lIm:jӳÅYpIdz9!oT,ۛϾ1iP`homP{J#t-ѵxעe5rFVT}Caי,7Kk y_$wp#v='gW_͹Y4>,K }'g\Ι/j.18;Jy{d-5u+\~!d)\'_ݪm)f5 o$C’<)1%[>ak:Zssqu|Q**2cj)7tJHeX}'/[J}m ;| T;^%|^h1_*đd# Z :%N'&Nj{xzOy*=IrbÏ}P/IAČ*o&Vv1`s`ڋ`2)7|<䜼Ó#j3!6UFekݣ˶X(ߢUVrt&FAxҗk|x^99xЦB'qqWS[ ΃*GJr1(37kξVYz0\C7(xdϻ&S@jjh2/tƳ'Oؚmӥ% %{c{Nbk_su{5Ʉ,hjbv~=G ZT![-"iTAsq=N9/ztuC\$5+i C'=fNgwt%Է̶xG;kOZw~L1S{p,M㞐~:1߯`ukG{a _}%w?αY=K&CM2r aH7Ģ$ Fjc=̃#b4[(:gUW좧[T@…fj%:@:/ Zfd7(Taљg Zk]kWtNZ놛z0Q*dH|'M2o⛯dow*Rכ rf qӐbf6|LtVה;h:G6)#!ڐ$%c_.j,f9;\&%-2 LzkBIk!u9mJ)h o[b]~էY| ]WĞmq]ε[ 9 JkTz7TT"39v'Z{J j>mG2!|G׬*Zb"vMo* D|FʟvY2ow<~2Ňjуh9JK )]7Z\ϝDpA`r:do.b -֖ y0?u'NˢRax͆(wi /خdgFNjJ6^bݶlϿWaiɦ2iS9E #M|(IRq!$"57YI $G)'6G3 cE mG|0_a5\IJy}kX);߰7/.fJ;8Fr69O[4-!(1IV`$) Zyv5&zHn IDATI4+B5K\rVzZ4e4L&#^xoۿ X.#ʱ=3{3al) lQ:C;Y^X#m5bBİ''YJ3 :Q ,PaJ]dWq]w M R n Nvi(@B]HTR:FO9滟- % +B,l:ˉ-HN{ %UI4UJR.x#77pr7%a%t̆P}$nZʍ>19ڒPTF%DJTA 7; D*M~RɋS7 Op#7{x6K|ے4M1nZ2w\s mTȄ_L㚤,!I^ȇC)O˟}W|'lovS1h}s8T&2 huvM&AJf]u7|5=>ٵ_AZַvE9cxq#ѳ1Ui8ڟ>{#lO:d%s=|A`Xbǖ'62T ۔wit %)nTxnZufIf\!'-{ {UߙgZSsAߴI9O?}o[ot:$8NPSX`ѣҺkWX`cW($iI5H0x񔇶j3R]>])$Dt-Y!h󀺷ڽ@nc"+bjh|/O?ygW˵MZ8=,$0I />Gn +8>>^rq}C3L+8R(wׁbX7\s3G<9U263M̪Ev[CHJKFCp`8f:nݐJN6jʂ>;MoN9)7` *'隤 C0V2͋[PJ}}/J# ŀAOG)\\So׿}HWd簂 ZuJMAKlhVڦ2W쫄Qzmr$MgYE-Hl ָՒkd|)wNtaQt_}}/pBBnEoYQQcYV]`..g o>;nKE S=LogG\?X"X.i\-o89\qG{ 6l%Mgd$8`AؚMx2U 'ۊMb^ ~q'r2*R0*UlƛŖ;EJx9]X6 1l>*s RbgX;.toɊY䍊IFIE>3;^|'CjC1FI'݀9Kxw^cjAS-ZLLudzQ{=#9P~/.cfzċOOx1vM4ES.S |$W"#ʐtC:ZJEo> s.nQWE$C" %mGRjb!;A%KN[t7\\\xrX5va3">lQRȠ={ RFFCp4`00oKshu{<ŗrJ~(Ufh0U5QkicE۬g2a5l4vR B[oXʆ11;88:ck6J$) Q%^!"z ZE Owv;#a Tѡ e%Mi9:oӂQ)fbeX<.:G;|בǷ}Yg_m.,^g^ҬO4A#;>ӧ|ϟѾ5Nya{]^UjB$P>[Qί=g9;-uӡu)(ė\I9`JK,/EDm.jqwj!ׇMٞi&#!ZF p2Zt:`v[+f9"s eK->Y]zis|()W 7)*Tp${P&yz(xlQk(s%))'ŗ_W+ Fd"vmHeMFܔSFJPgw7|8=Bl ̎::%SۼLFg2I2BE8bsN,OL>Vuh~*lGy)~W/q!d PV@1(NM2an$)ήoNnx[ޝ\R&D(Ĥh#Dq#5N#@LM΀e~vdDRbF=$9tE%ZѼ;$3PKW p<"Y[FUgfsymr(W,W;S.f/\\̦2*P$9t"1QEO1j+2Y/Xٔ҄R9VGRرynIRHA+h =bE_Z Gn$sᢆۿɷ}t2f2ӂo~6J6p,kU]'pAV`tk6%Αr7Qq\m &7U"JB}:?xB>qWWW{o^ŌQ#'BEQ`u5v XWp;OFGkS{\2K 6:Gl_zpl2FAU4ii͞?_sytƻwy}BeQ\ 0J,,e<3xf-ՖjI1*ȋbB=aH2x3qׯpz񂫗<9', ; Z@b6VRZ.iv7_x+pb]B)U]K`|҇p B2w\_WA DpFjP6IS 8Ӎ0d2w3o޾ zL !2df)s^[^~łO 1"a3[4^Q7G>_|ie_>9@Y<%M2jз֣EiG^}z7rc8`.B-G{/^êess^\ry6i¸Zn@;L,g Ybݲ+&lpnqs.z}(^ƘIZ B],<=9 5J[681=/\zɿ曷︼<'SQt g;+DU>P_nG>ܱZ3wfmg9"ǘS`Cyfi!d]c-xj>3c%Xpqq˗x o߼%ňX3[Ʃ!5B߲xk5Kd׍;ڳ{>]=7|JP$ Qmې|\pzv|2"M4m׬l;J7"JZ*?}5B+9u  H4UIJ0-䓁êX)2r6y:oN٬Y~d|b^v;zw@١b{ aGͶS#xwa81Mqo^pyyFFBXO4Bv$O/\Yz/ |G>~e)hk%cUP˚nzЪ: wx)mUTdYݔ8<]o^ X WT튺fjقWs/R&0zEa c c^~tzcsv%xN6~dYQn7 v"J(Qlϖ k&42ab_~ o߽a>& F;Ŝ}^WPz𰂛=?O|y`{qD}n<]z(L7 ;Tz>nOMJMdD݉7:$'z~󖷯_Œhb2J=$yoQ1v)1}Db:Q/|O7|}\>45[S.o8=Ypv`2JI#:9|~z՚GjMմFkLz\6Qhc Qz1/Z|/<#ޱ쁶]Y%U ;??wqqqd:!lbTB ^(p ӧG~|˗{>)8ꪌ#f Tm/@Td8ZlIޮ_yܲZ>4Z1x7/xIl~wDr$;a:+F9=}]Kp\#w G-4ip-+9 ='ULlTaٳ{6w^Oyy|1a'F%N L1SBOaSɈfa6cz\Okv{r/']uB4w es䏽\lňlΛ~_?ׯ_rr`<󈕏J[ޡG+GbWDG|Dr}~?~/w%oyo߼W~yIle0JDטYXF Nxž7k/w#?iC5[f tBH&M$#2$#r,$96˄Q""G)(1^p3d鋗7׿NMGdž,I8k#;Iڨ NfW{xX7×;;vq}dp?Jzc} #btuIR6rL<>Nɔ%W7z邳ӄY!ld 2ݷ*"NRB-0vjwO[>~kֱ=+Ч|d!+ҜdÕ> IDAT|8ۉ{k26fydh4-V6C-Nݛ>bb.RX-p΅(1JcMBCTHRqS@UKqi6\N938\QqIFClv9lhv+qE{TrhQY\T3ƒG &9/^oīׯ+N$UvrȔ!8<:F.Ғ 0cn>>|~i+1GwHG]o1YbrUڷF]A%™ L 9fvI6;✗/.zyūe7ؗcC~eTߖpBp"hֈ5MR,!/r&ɘt|>eqrl2H V먗'V/m У0D~̇O7?l*[c1QqqQ>JPy`/^n(w[v'hk|Sa ܐSyS1Tٸ1:3/8|%WW\s2p~6d3.E*d]޲Qz  զaŗ{>_r}d)A([] D* }Vc5ʺ:+m{-\Bxʦe_;v,E HީrVcƓ4c1-x<|qjV8H&vum|vdΘ)A3(,beږ6XR1]U^MP v +PUT'3+@Z+wD@`l Բ=X}ٲ/vݞþДWK"6Q cdR4%/rFqQPE<ՌRin4s~a\b\r槟?Ӈ\?NAn޸*PGN!ފd_|nfݐJ m-g!s*BVlN bB6QLN|<=b6eR̦i.+O8kL; <(W(*ٖp ͟5Z(_?t\;YNN/T}{epж-}㚧eYtp2+YBIV0f f'섋YIn(, )um} qVQ5C -8;!)e4{ R$/3Q#HʦEtD]bjQ6cD۔4d&S-6n*?(%VYuWO}\l2q)դ%OܐY٨1o=d}lM@gjG>|~yhg$wy2S^} nz;2E[|bTaBXn HI2ň$ ɜyƼI\Xy6,6bwziG0UqW) |>H:knK6Tʈ^Mǯb+K"( uR6-ew9qۈҩ´)AgTO_lċ9g3*c>K ^DtAA H5Ҍ1p1,[YVe)Ծ{' ^In3RCĽQV5CS5Ok׌ lbI|j AnVg{Uu+ZE*\R)u.2,#v n]W. ]mYcʝ[hqpiam!qJyt0,Uʫ"`c&}=c4?Ԭ՛ur_ڑ***1$!I$(sEx3,I2MrYDRN W&*mCEDjr~ſ=}š[7)4JF"z?+0Ơ#ۍ}P6-.Z/m-ubI6(/w% xřydMa\ ]KlB,61O )?^/KMzFJ8`mw(ĽEwZ7l"ݞNfLON-L٣sUJ ibHMY)RøHy%4hl0j.;4̐Z^v/7\>д4ޘBa6NG g ;СϤE^˖ޑ^NotGt BbFٞͺ~9g'P_) +1IMY=6[f/Iݐqж+2tR}N/͕`xx!Aa!ړZo0ABL,hgnQuj-ޕO^q{+DD@E㜂 ,Wnnxx\QU%6K:!:':xI$ƎdO:,+)Ҭp0a\7kO+W:,P"RU$Vсh2 Ej(RCV;RbZеtQDJ-M+}OkC 7Oa2ұzΕPD,vؕ*6/@]:ū#בoH;u"!/O/Fqֶ`Ix9I2tHŠ~clu4",uA\# 5A&!$8S\T^^??r{@]Nl"QQC_ॖVFSMGk~ߖ ? 'Yhr#mhB;WcX=\`?&ewlk%zvi邋N' "Q"3ME$ Ynw`h-n䡉;j5$HˉZmӣ '{Fsu2tۂ4 u\NkD#ц9F-7B}wbq/}Nh&IrĀsV\ 4vGj ƈq.1Iʧrtm4VkXnut=b]7%!xk[)qO??'v=Z[4CLt-f J"4Mίwv* +pҵ4Ua!rZw *&D3a::+K^&V:MMٖMa3NkfѨ@fliM ibIӔ4I1US_BnPNs<^a^.Av{4" a'ޟθ:1l5PP-,I1?s賧RF HO͔>1C֚xb1(r mQ.(wbgiM=Ц)e6Ce NFcuC^z޹ګZ: !@.!(87L)[UXn\G|n(%WhU6 ^1Cut40:_{e/UB$.;/ɚ㔏vҩH RiًR P]rkOV5۽ac;W5N/HkHk4Mj6Fk~JR kl79>c@z{'AT u\l!vs>j>K&TU 3$͙687;`I}lx8”WEDQ39?;e:pXq;Y\{\AU1h0 %6dNZgXk\⠢ӷ3*;q~ ]U kQBNf.U2wigx%T5ɭBV:EǴFo*k DHQ/MTHx7:/y "wbW! SO-8=.fAV*4gy@}H9ԧ>e2 %^9<șuJ7 L-boqkںjB3pDF5 kX><~gLtGMȆ=?F9=xXHDY;&#.YOzoS;Z̮PͽtLt ) hS\U*BWQ G:f#[\vձwKwJXp2DyC#eR&ŷu_{^V:M2LLk- glI+ hRن6TLYQsBY bw."t43c87p(-_x,nr9Jnonwp-D =|jJ~xG[K>Ch+hE|6uU%*#Ơ)j#4U+ TGǝ0SG=ۑf0Upzݮ?6zj#.OSŔ+nVwB6+?bd iNHs':mBd0_C>&_x#t璆o9{C)-IGh*|ky;~OPڡDbik˽n*g8uĆPw/&}iIyxvy$հ=j=-Ol+Ǩ(bpE+,W+vKw{-^w^[]V\U*.45n(DY'é:Į>=j:3jD,j(2bq«W|w=mC%ӽ w=4+:i0*kQwA j:֟-r:ZԪ6w.$FЧ+9YH"|q$hCd 4=bĔ~!_?UZ,y0PpP\HTF{t(˒B1{cCE pեT7e مH|ɹ O~mJ< $"ݻ7kV;PEz脚QcBgHZkt{X|Cq?in̳%<_~Jq|)dQs6~gyOS׸,RJA#]%\I~X@T採9qW616xwYQyOj,yX. afs^^ONR䆳S޽{ˇoU{;\S?Xc9꾰C Vl%Cݽ󎇁`8CG~D`6ySYR)EhFD^G;1HY([2Զ[j $(k>HN4gGb;@):mYXBYnh'> غz2v5tJ|1smӗM\(srǭdbhGX$Zpφ¿q 3YE`xrzݷ߲=owFm>1S縈1SۋM!bK$O馞cw؋Ay}xg]O6(mbD|JY )EOJ oV)|Ev _<v8s7np߻6U/P\\l]@EIPWјK^`:)"~ԞuQ Gdk" P0Mw߱k6v/]iq#K"4ݮe?YlZahw 췫q:,t9/./yd_ ID^!p>uDk(`r%&*Ă89h& *)$)$b8SUX`amG;ЍY *B8zc]d*]TM MaPzO,-dK1rEǏqz_ ]ì&?nop@ `"]J\"Q&t\ {rgk,LMK ӕ8M'M%S# _ ɹ+&UQ=*IT2Oec3MDw/S`USibHȏ 6mW&j.v*lk<4ڼ = >aQ*׶puR3X" B4h؎`cK=ѡVAp{5r poO-0=cu UT(h =D6 ʥ"/?Lo3y-!oQ2$Zhn@JT*n}M:Adj0e W/^amqj>  QΞy&J*3Y93%H{K\]LcL*"frbf X$6ӵgΝh HVU鿸!{[8uƠJb7R֦a7C*tI*IK05Nl.iA+u7BCU$Y29@SDES$!hTlHoY%Ln}>OnrM8^DNh- Z*P7mTMr3`PVd+u Za;V.PH_ggqZr)6:wSuvªO6GRTs) uRP)$vHNS\p>^~QE%>-r7^cnhDv`3`#` h#u2F=BO09~5+BaE92a0!)0{\24 N1vTaNz=J_`f'";U4m^ ` J&h8؂ @m顳Cуَ.-1^)0 zOs82`#:4t$`׮Mp.6#Q?sC r5޽g|)O88LH;5N'.Zd?-/]ʽE4 FiC;{ֹe'aⱑf.fP*Ã]$Se*u WS4Tv:8Tlr`4FcS6tU'9'J b^lr1gK0>Zkwj{*OklF92@$%<;"idq>]Qxh (=M~,Er]M@Ɂ?o޻?Gkxvihx9:JWkO6( \ v=:z[X<*wxB@K~jv6%",-ac1ʧ;.'0k5j,u.V7VY)ֽ);5v6qrhXMSsf֩h:Ј m[d&ESo ҢB_k3S&)n#6@mv @ް*,Y: j { ZzHC:=maB*5n.$QUWۃU_I%RYlr{)Isc!Nxx7^EP3;y+ yh {wjR_H xPU#~+ =:ʳ0ɍ,-mR.;#m&3sL1MW w(U>,Krv?MM6<~(mQsu,GR L[jh dd ;:*ͤJ]E(^ ɝ-~6 ="PK|~]M ?3M4wQ?AocFj1wF\A1U ;9DGD*4EU?kBxbD#>kd %:IqerD6=: Ƽ5|LmOa{+F%@:u=d.K[Y&rBo]["@cp{bvUj@h@A"Ъ%=J:M%JE۠PLlQ^*Jh/ .`f=Ml࣯?C/oؙN@-M~sPnnʦK&FQ)l/SMcW h(p㲰'7 4:CĢ>%;];У-T:¬Qϥ)ŗ0im{GϜ#cڼ޹A"A[3xx;޷LbvK`U d۔-;)Fb(m$0K)jHS;_W86˭*?pb E@ p5 }|a6rs6Ώ>X'_w8wvѧP\~/tļ wn)>|]Fsm׏xQt\͇@6c_'<.?ƅ :Cg n.\yIZ0 ,E**#s/?Ùċw7r}leZC w6e*aMY^Z"Qn\wAa}BBC*~>ůcҘ˅u62EFwyyZee#z1-Oۿ}:4O[ )Nk'L4V4מ=@SHCm>==Mlom)wWx;Alo3N Fk>ęcKEꇓlUIbO{+*9vw5HbȕjD]756 O@Onm %!O n-g_GzNqmfoyUSQ.ǯ>ȳ'HW2IbtD5b 67oP4:zhAB0x5*[6uGŴ A!…KSuBD B&\|^Or0!5~kBWe?o:.#._ɇܢn^"{wA*r|w *6tx.EI(V#7Cq{p.loJN*6'@K,oH(yJz:/='OZ07d6j"ghW稗`JJ:AX m#f-G^Nq 3Ku!tu/ʻ?yZ!Q=@PU`:Ԋ) ;+,BoW adoȕ-LT۠*x5p3Z(OO'VqLiK궅Es:9t!{(*$6ԫ(uY":BHcO|9~G:(Uj|tW;Gi8d7cY 73L/kk>Kz83#DtPOL#hb4E[=N qxhFF=>$.VfQWc#Udo}-a޺2i;)6۫C/1Ҥ`G8Bj{}-fNC\X͒ȔHdKĴ 28~!@$>įGF |<\Z!@Ar$AOKGRl .]C( YHSX_';@'ґDSܚYFhYHHftXVvE Q+װ} _K}8rxۓSs J$:X/n<x|\ZbniV*('WP7j~B`XM¨y逢itf^#Rw~q$"lzN"]q_xx{oC58r3?օ=HYpKJ!E5~;ۢ_|ຐ*\B}k^T]GS@ead61kR{_y~\$ˇ{Wdbۋi6l[PXKed"f1Uc8*T R #X-1k_ڗysPO/. uyG[Ѣ5EU)Q%4XuBC8>6P/\Ksܺ~)8Y30gϜ;+%+;&W0 8+o^%eAxCt<~v0P틳)ΫOn6sh0j15{'vpP4F<|?6oI& vXs#}GK\!Y,ݟ6RXخJķ'5ֶ)jxU&qhwE^odec?gGx}{?Rk^$W4*xT>{ϝƇ$UQ4EjDT``W6|GG[tM\E ˉC{0BUh*ĂQu>}-%PUQP\W:L.S-YɱV2Ǯ=8knO'I ( xT(7e+ămtxZ]hD+n0\Csb O {\UUUSЄk󓸚i4<~dIgS؎D sLlnn&QJSMlP39Ɓ]1| B {y}#J6܁؎ E"pqP(uRSdcxuT H&͐-QĵL\ORKyp$MQ4E BRB(s1ofӷv V^`_cIENDB`Quickcal_Calculator-2.1-7077da9bfefcb59da0eb133bd7c06013a99c05ce/setup.py000066400000000000000000000011261322051455000247160ustar00rootroot00000000000000from distutils.core import setup setup(name = "quickcal", version = "2.1", description = "fast and easy to use calculator with support for filing", author = "Nathan SR", author_email = "cquickcal@gmail.com", url = "https://git.fosscommunity.in/SRNathan/Quickcal_Calculator", license='GPLv3', scripts=['quickcal'], data_files = [ ("lib/quickcal", ["quickcal.glade"]), ("share/pixmaps", ["quickcal.png"]), ("share/pixmaps", ["quickcal_small.png"]), ("share/man/man1", ["quickcal.1.gz"]), ("share/man/man1", ["num.1.gz"]), ("bin", ["num"]), ("share/applications", ["quickcal.desktop"]) ] )