upstream/0000775000175000017500000000000014637212425011364 5ustar kaolkaolupstream/custom.properties0000664000175000017500000000013514637212246015014 0ustar kaolkaolbooktitle=rocFFT API Guide spreadsheet.xml=docs/classification-map.xml document.locale=enusupstream/.clang-format0000664000175000017500000000654214637212246013747 0ustar kaolkaol# Style file for MLSE Libraries based on the modified rocBLAS style # Common settings BasedOnStyle: WebKit TabWidth: 4 IndentWidth: 4 UseTab: Never ColumnLimit: 100 # Other languages JavaScript, Proto --- Language: Cpp # http://releases.llvm.org/6.0.1/tools/clang/docs/ClangFormatStyleOptions.html#disabling-formatting-on-a-piece-of-code # int formatted_code; # // clang-format off # void unformatted_code ; # // clang-format on # void formatted_code_again; DisableFormat: false Standard: Cpp11 AccessModifierOffset: -4 AlignAfterOpenBracket: Align AlignConsecutiveAssignments: true AlignConsecutiveDeclarations: true AlignEscapedNewlines: Left AlignOperands: true AlignTrailingComments: false AllowAllArgumentsOnNextLine: true AllowAllConstructorInitializersOnNextLine: true AllowAllParametersOfDeclarationOnNextLine: true AllowShortBlocksOnASingleLine: false AllowShortCaseLabelsOnASingleLine: false AllowShortFunctionsOnASingleLine: Empty AllowShortIfStatementsOnASingleLine: false AllowShortLoopsOnASingleLine: false AlwaysBreakAfterDefinitionReturnType: false AlwaysBreakAfterReturnType: None AlwaysBreakBeforeMultilineStrings: false AlwaysBreakTemplateDeclarations: true BinPackArguments: false BinPackParameters: false # Configure each individual brace in BraceWrapping BreakBeforeBraces: Custom # Control of individual brace wrapping cases BraceWrapping: { AfterCaseLabel: 'true' AfterClass: 'true' AfterControlStatement: 'true' AfterEnum : 'true' AfterFunction : 'true' AfterNamespace : 'true' AfterStruct : 'true' AfterUnion : 'true' BeforeCatch : 'true' BeforeElse : 'true' IndentBraces : 'false' # AfterExternBlock : 'true' } #BreakAfterJavaFieldAnnotations: true #BreakBeforeInheritanceComma: false #BreakBeforeBinaryOperators: None #BreakBeforeTernaryOperators: true #BreakConstructorInitializersBeforeComma: true #BreakStringLiterals: true CommentPragmas: '^ IWYU pragma:' #CompactNamespaces: false ConstructorInitializerAllOnOneLineOrOnePerLine: false ConstructorInitializerIndentWidth: 4 ContinuationIndentWidth: 4 Cpp11BracedListStyle: true SpaceBeforeCpp11BracedList: false DerivePointerAlignment: false ExperimentalAutoDetectBinPacking: false ForEachMacros: [ foreach, Q_FOREACH, BOOST_FOREACH ] IndentCaseLabels: false IndentPPDirectives: None #FixNamespaceComments: true IndentWrappedFunctionNames: true KeepEmptyLinesAtTheStartOfBlocks: true MacroBlockBegin: '' MacroBlockEnd: '' #JavaScriptQuotes: Double MaxEmptyLinesToKeep: 1 NamespaceIndentation: All ObjCBlockIndentWidth: 4 #ObjCSpaceAfterProperty: true #ObjCSpaceBeforeProtocolList: true PenaltyBreakBeforeFirstCallParameter: 19 PenaltyBreakComment: 300 PenaltyBreakFirstLessLess: 120 PenaltyBreakString: 1000 PenaltyExcessCharacter: 1000000 PenaltyReturnTypeOnItsOwnLine: 60 PointerAlignment: Left SpaceAfterCStyleCast: false SpaceBeforeAssignmentOperators: true SpaceBeforeParens: Never SpaceInEmptyBlock: false SpaceInEmptyParentheses: false SpacesBeforeTrailingComments: 1 SpacesInAngles: false SpacesInContainerLiterals: true SpacesInCStyleCastParentheses: false SpacesInParentheses: false SpacesInSquareBrackets: false #SpaceAfterTemplateKeyword: true #SpaceBeforeInheritanceColon: true #SortUsingDeclarations: true SortIncludes: true # Comments are for developers, they should arrange them ReflowComments: false #IncludeBlocks: Preserve --- upstream/solution_map/0000775000175000017500000000000014637212425014075 5ustar kaolkaolupstream/solution_map/gfx90a_rocfft_solution_map.dat0000664000175000017500000074503614637212425022040 0ustar kaolkaol{"Version":3, "Data":[ {"Problem":{"arch":"gfx90a","token":"kernel_token_builtin_kernel"}, "Solutions":[ {"sol_node_type":"SOL_BUILTIN_KERNEL"} ]}, {"Problem":{"arch":"gfx90a","token":"kernel_len100_double_sbrr"}, "Solutions":[ {"sol_node_type":"SOL_KERNEL_ONLY","kernel_key":{"lengths":[ 100,0 ],"precision":"double","scheme":"CS_KERNEL_STOCKHAM","sbrc_trans":"NONE","kernelConfig":{"use_3steps":false,"half_lds":true,"dir_reg":true,"buffer_inst":false,"tpb":10,"wgs":250,"tpt":[ 25,0 ],"factors":[ 5,4,5 ],"ebtype":"R2C_POST","direction":-1,"static_dim":1,"placement":"OP","iAryType":"CI","oAryType":"CI"}}} ]}, {"Problem":{"arch":"gfx90a","token":"kernel_len100_single_sbrr"}, "Solutions":[ {"sol_node_type":"SOL_KERNEL_ONLY","kernel_key":{"lengths":[ 100,0 ],"precision":"single","scheme":"CS_KERNEL_STOCKHAM","sbrc_trans":"NONE","kernelConfig":{"use_3steps":false,"half_lds":true,"dir_reg":true,"buffer_inst":false,"tpb":20,"wgs":200,"tpt":[ 10,0 ],"factors":[ 10,10 ],"ebtype":"R2C_POST","direction":-1,"static_dim":1,"placement":"OP","iAryType":"CI","oAryType":"CI"}}} ,{"sol_node_type":"SOL_KERNEL_ONLY","kernel_key":{"lengths":[ 100,0 ],"precision":"single","scheme":"CS_KERNEL_STOCKHAM","sbrc_trans":"NONE","kernelConfig":{"use_3steps":false,"half_lds":false,"dir_reg":true,"buffer_inst":false,"tpb":6,"wgs":120,"tpt":[ 20,0 ],"factors":[ 4,5,5 ],"ebtype":"C2R_PRE","direction":1,"static_dim":1,"placement":"OP","iAryType":"CI","oAryType":"CI"}}} ,{"sol_node_type":"SOL_KERNEL_ONLY","kernel_key":{"lengths":[ 100,0 ],"precision":"single","scheme":"CS_KERNEL_STOCKHAM","sbrc_trans":"NONE","kernelConfig":{"use_3steps":false,"half_lds":true,"dir_reg":true,"buffer_inst":false,"tpb":19,"wgs":190,"tpt":[ 10,0 ],"factors":[ 10,10 ],"ebtype":"R2C_POST","direction":-1,"static_dim":1,"placement":"OP","iAryType":"CI","oAryType":"CI"}}} ]}, {"Problem":{"arch":"gfx90a","token":"kernel_len108_single_sbrr"}, "Solutions":[ {"sol_node_type":"SOL_KERNEL_ONLY","kernel_key":{"lengths":[ 108,0 ],"precision":"single","scheme":"CS_KERNEL_STOCKHAM","sbrc_trans":"NONE","kernelConfig":{"use_3steps":false,"half_lds":true,"dir_reg":true,"buffer_inst":false,"tpb":16,"wgs":192,"tpt":[ 12,0 ],"factors":[ 9,4,3 ],"ebtype":"R2C_POST","direction":-1,"static_dim":1,"placement":"OP","iAryType":"CI","oAryType":"CI"}}} ,{"sol_node_type":"SOL_KERNEL_ONLY","kernel_key":{"lengths":[ 108,0 ],"precision":"single","scheme":"CS_KERNEL_STOCKHAM","sbrc_trans":"NONE","kernelConfig":{"use_3steps":false,"half_lds":false,"dir_reg":true,"buffer_inst":false,"tpb":10,"wgs":120,"tpt":[ 12,0 ],"factors":[ 9,4,3 ],"ebtype":"C2R_PRE","direction":1,"static_dim":1,"placement":"OP","iAryType":"CI","oAryType":"CI"}}} ]}, {"Problem":{"arch":"gfx90a","token":"kernel_len112_single_sbrr"}, "Solutions":[ {"sol_node_type":"SOL_KERNEL_ONLY","kernel_key":{"lengths":[ 112,0 ],"precision":"single","scheme":"CS_KERNEL_STOCKHAM","sbrc_trans":"NONE","kernelConfig":{"use_3steps":false,"half_lds":true,"dir_reg":true,"buffer_inst":false,"tpb":18,"wgs":126,"tpt":[ 7,0 ],"factors":[ 4,4,7 ],"ebtype":"R2C_POST","direction":-1,"static_dim":1,"placement":"OP","iAryType":"CI","oAryType":"CI"}}} ,{"sol_node_type":"SOL_KERNEL_ONLY","kernel_key":{"lengths":[ 112,0 ],"precision":"single","scheme":"CS_KERNEL_STOCKHAM","sbrc_trans":"NONE","kernelConfig":{"use_3steps":false,"half_lds":false,"dir_reg":true,"buffer_inst":false,"tpb":8,"wgs":64,"tpt":[ 8,0 ],"factors":[ 2,7,8 ],"ebtype":"C2R_PRE","direction":1,"static_dim":1,"placement":"OP","iAryType":"CI","oAryType":"CI"}}} ]}, {"Problem":{"arch":"gfx90a","token":"kernel_len125_single_sbrr"}, "Solutions":[ {"sol_node_type":"SOL_KERNEL_ONLY","kernel_key":{"lengths":[ 125,0 ],"precision":"single","scheme":"CS_KERNEL_STOCKHAM","sbrc_trans":"NONE","kernelConfig":{"use_3steps":false,"half_lds":true,"dir_reg":true,"buffer_inst":false,"tpb":10,"wgs":250,"tpt":[ 25,0 ],"factors":[ 5,5,5 ],"ebtype":"NONE","direction":-1,"static_dim":0,"placement":"NA","iAryType":"CI","oAryType":"CI"}}} ,{"sol_node_type":"SOL_KERNEL_ONLY","kernel_key":{"lengths":[ 125,0 ],"precision":"single","scheme":"CS_KERNEL_STOCKHAM","sbrc_trans":"NONE","kernelConfig":{"use_3steps":false,"half_lds":false,"dir_reg":true,"buffer_inst":false,"tpb":10,"wgs":250,"tpt":[ 25,0 ],"factors":[ 5,5,5 ],"ebtype":"NONE","direction":-1,"static_dim":0,"placement":"NA","iAryType":"CI","oAryType":"CI"}}} ]}, {"Problem":{"arch":"gfx90a","token":"kernel_len128_single_sbrr"}, "Solutions":[ {"sol_node_type":"SOL_KERNEL_ONLY","kernel_key":{"lengths":[ 128,0 ],"precision":"single","scheme":"CS_KERNEL_STOCKHAM","sbrc_trans":"NONE","kernelConfig":{"use_3steps":false,"half_lds":false,"dir_reg":true,"buffer_inst":false,"tpb":8,"wgs":128,"tpt":[ 16,0 ],"factors":[ 8,2,8 ],"ebtype":"R2C_POST","direction":-1,"static_dim":1,"placement":"OP","iAryType":"CI","oAryType":"CI"}}} ,{"sol_node_type":"SOL_KERNEL_ONLY","kernel_key":{"lengths":[ 128,0 ],"precision":"single","scheme":"CS_KERNEL_STOCKHAM","sbrc_trans":"NONE","kernelConfig":{"use_3steps":false,"half_lds":false,"dir_reg":true,"buffer_inst":false,"tpb":8,"wgs":128,"tpt":[ 16,0 ],"factors":[ 8,2,8 ],"ebtype":"C2R_PRE","direction":-1,"static_dim":1,"placement":"OP","iAryType":"CI","oAryType":"CI"}}} ,{"sol_node_type":"SOL_KERNEL_ONLY","kernel_key":{"lengths":[ 128,0 ],"precision":"single","scheme":"CS_KERNEL_STOCKHAM","sbrc_trans":"NONE","kernelConfig":{"use_3steps":false,"half_lds":false,"dir_reg":true,"buffer_inst":false,"tpb":8,"wgs":128,"tpt":[ 16,0 ],"factors":[ 2,8,8 ],"ebtype":"R2C_POST","direction":-1,"static_dim":1,"placement":"OP","iAryType":"CI","oAryType":"CI"}}} ]}, {"Problem":{"arch":"gfx90a","token":"kernel_len192_single_sbrr"}, "Solutions":[ {"sol_node_type":"SOL_KERNEL_ONLY","kernel_key":{"lengths":[ 192,0 ],"precision":"single","scheme":"CS_KERNEL_STOCKHAM","sbrc_trans":"NONE","kernelConfig":{"use_3steps":false,"half_lds":false,"dir_reg":true,"buffer_inst":false,"tpb":16,"wgs":256,"tpt":[ 16,0 ],"factors":[ 2,3,4,8 ],"ebtype":"NONE","direction":-1,"static_dim":2,"placement":"IP","iAryType":"CI","oAryType":"HI"}}} ,{"sol_node_type":"SOL_KERNEL_ONLY","kernel_key":{"lengths":[ 192,0 ],"precision":"single","scheme":"CS_KERNEL_STOCKHAM","sbrc_trans":"NONE","kernelConfig":{"use_3steps":false,"half_lds":true,"dir_reg":true,"buffer_inst":false,"tpb":16,"wgs":256,"tpt":[ 16,0 ],"factors":[ 2,4,4,3,2 ],"ebtype":"NONE","direction":-1,"static_dim":3,"placement":"IP","iAryType":"CI","oAryType":"CI"}}} ,{"sol_node_type":"SOL_KERNEL_ONLY","kernel_key":{"lengths":[ 192,0 ],"precision":"single","scheme":"CS_KERNEL_STOCKHAM","sbrc_trans":"NONE","kernelConfig":{"use_3steps":false,"half_lds":true,"dir_reg":true,"buffer_inst":false,"tpb":16,"wgs":256,"tpt":[ 16,0 ],"factors":[ 2,2,3,4,4 ],"ebtype":"NONE","direction":1,"static_dim":3,"placement":"IP","iAryType":"CI","oAryType":"CI"}}} ,{"sol_node_type":"SOL_KERNEL_ONLY","kernel_key":{"lengths":[ 192,0 ],"precision":"single","scheme":"CS_KERNEL_STOCKHAM","sbrc_trans":"NONE","kernelConfig":{"use_3steps":false,"half_lds":true,"dir_reg":true,"buffer_inst":false,"tpb":16,"wgs":256,"tpt":[ 16,0 ],"factors":[ 3,4,4,4 ],"ebtype":"NONE","direction":-1,"static_dim":3,"placement":"IP","iAryType":"CI","oAryType":"CI"}}} ,{"sol_node_type":"SOL_KERNEL_ONLY","kernel_key":{"lengths":[ 192,0 ],"precision":"single","scheme":"CS_KERNEL_STOCKHAM","sbrc_trans":"NONE","kernelConfig":{"use_3steps":false,"half_lds":false,"dir_reg":true,"buffer_inst":false,"tpb":16,"wgs":192,"tpt":[ 12,0 ],"factors":[ 2,2,3,4,4 ],"ebtype":"NONE","direction":1,"static_dim":3,"placement":"IP","iAryType":"CI","oAryType":"CI"}}} ]}, {"Problem":{"arch":"gfx90a","token":"kernel_len2187_single_sbrr"}, "Solutions":[ {"sol_node_type":"SOL_KERNEL_ONLY","kernel_key":{"lengths":[ 2187,0 ],"precision":"single","scheme":"CS_KERNEL_STOCKHAM","sbrc_trans":"NONE","kernelConfig":{"use_3steps":false,"half_lds":false,"dir_reg":true,"buffer_inst":false,"tpb":1,"wgs":243,"tpt":[ 243,0 ],"factors":[ 9,9,9,3 ],"ebtype":"NONE","direction":-1,"static_dim":0,"placement":"NA","iAryType":"CI","oAryType":"CI"}}} ,{"sol_node_type":"SOL_KERNEL_ONLY","kernel_key":{"lengths":[ 2187,0 ],"precision":"single","scheme":"CS_KERNEL_STOCKHAM","sbrc_trans":"NONE","kernelConfig":{"use_3steps":false,"half_lds":true,"dir_reg":true,"buffer_inst":false,"tpb":1,"wgs":243,"tpt":[ 243,0 ],"factors":[ 9,3,3,3,3,3 ],"ebtype":"NONE","direction":-1,"static_dim":0,"placement":"NA","iAryType":"CI","oAryType":"CI"}}} ]}, {"Problem":{"arch":"gfx90a","token":"kernel_len243_double_sbrr"}, "Solutions":[ {"sol_node_type":"SOL_KERNEL_ONLY","kernel_key":{"lengths":[ 243,0 ],"precision":"double","scheme":"CS_KERNEL_STOCKHAM","sbrc_trans":"NONE","kernelConfig":{"use_3steps":false,"half_lds":false,"dir_reg":true,"buffer_inst":false,"tpb":3,"wgs":243,"tpt":[ 81,0 ],"factors":[ 3,3,3,3,3 ],"ebtype":"NONE","direction":-1,"static_dim":1,"placement":"IP","iAryType":"CI","oAryType":"CI"}}} ,{"sol_node_type":"SOL_KERNEL_ONLY","kernel_key":{"lengths":[ 243,0 ],"precision":"double","scheme":"CS_KERNEL_STOCKHAM","sbrc_trans":"NONE","kernelConfig":{"use_3steps":false,"half_lds":true,"dir_reg":true,"buffer_inst":false,"tpb":4,"wgs":108,"tpt":[ 27,0 ],"factors":[ 9,9,3 ],"ebtype":"R2C_POST","direction":-1,"static_dim":1,"placement":"OP","iAryType":"CI","oAryType":"HI"}}} ,{"sol_node_type":"SOL_KERNEL_ONLY","kernel_key":{"lengths":[ 243,0 ],"precision":"double","scheme":"CS_KERNEL_STOCKHAM","sbrc_trans":"NONE","kernelConfig":{"use_3steps":false,"half_lds":false,"dir_reg":true,"buffer_inst":false,"tpb":7,"wgs":189,"tpt":[ 27,0 ],"factors":[ 9,3,9 ],"ebtype":"NONE","direction":-1,"static_dim":1,"placement":"IP","iAryType":"CI","oAryType":"CI"}}} ,{"sol_node_type":"SOL_KERNEL_ONLY","kernel_key":{"lengths":[ 243,0 ],"precision":"double","scheme":"CS_KERNEL_STOCKHAM","sbrc_trans":"NONE","kernelConfig":{"use_3steps":false,"half_lds":false,"dir_reg":true,"buffer_inst":false,"tpb":2,"wgs":162,"tpt":[ 81,0 ],"factors":[ 3,3,3,3,3 ],"ebtype":"NONE","direction":1,"static_dim":1,"placement":"IP","iAryType":"CI","oAryType":"CI"}}} ,{"sol_node_type":"SOL_KERNEL_ONLY","kernel_key":{"lengths":[ 243,0 ],"precision":"double","scheme":"CS_KERNEL_STOCKHAM","sbrc_trans":"NONE","kernelConfig":{"use_3steps":false,"half_lds":true,"dir_reg":true,"buffer_inst":false,"tpb":2,"wgs":162,"tpt":[ 81,0 ],"factors":[ 3,3,3,3,3 ],"ebtype":"C2R_PRE","direction":1,"static_dim":1,"placement":"IP","iAryType":"HI","oAryType":"CI"}}} ,{"sol_node_type":"SOL_KERNEL_ONLY","kernel_key":{"lengths":[ 243,0 ],"precision":"double","scheme":"CS_KERNEL_STOCKHAM","sbrc_trans":"NONE","kernelConfig":{"use_3steps":false,"half_lds":true,"dir_reg":true,"buffer_inst":false,"tpb":4,"wgs":108,"tpt":[ 27,0 ],"factors":[ 3,9,9 ],"ebtype":"C2R_PRE","direction":1,"static_dim":1,"placement":"OP","iAryType":"HI","oAryType":"CI"}}} ]}, {"Problem":{"arch":"gfx90a","token":"kernel_len243_single_sbrr"}, "Solutions":[ {"sol_node_type":"SOL_KERNEL_ONLY","kernel_key":{"lengths":[ 243,0 ],"precision":"single","scheme":"CS_KERNEL_STOCKHAM","sbrc_trans":"NONE","kernelConfig":{"use_3steps":false,"half_lds":false,"dir_reg":false,"buffer_inst":false,"tpb":4,"wgs":108,"tpt":[ 27,0 ],"factors":[ 9,9,3 ],"ebtype":"NONE","direction":-1,"static_dim":0,"placement":"NA","iAryType":"CI","oAryType":"CI"}}} ,{"sol_node_type":"SOL_KERNEL_ONLY","kernel_key":{"lengths":[ 243,0 ],"precision":"single","scheme":"CS_KERNEL_STOCKHAM","sbrc_trans":"NONE","kernelConfig":{"use_3steps":false,"half_lds":false,"dir_reg":true,"buffer_inst":false,"tpb":7,"wgs":189,"tpt":[ 27,0 ],"factors":[ 9,9,3 ],"ebtype":"NONE","direction":-1,"static_dim":0,"placement":"NA","iAryType":"CI","oAryType":"CI"}}} ]}, {"Problem":{"arch":"gfx90a","token":"kernel_len256_single_sbrr"}, "Solutions":[ {"sol_node_type":"SOL_KERNEL_ONLY","kernel_key":{"lengths":[ 256,0 ],"precision":"single","scheme":"CS_KERNEL_STOCKHAM","sbrc_trans":"NONE","kernelConfig":{"use_3steps":false,"half_lds":false,"dir_reg":true,"buffer_inst":false,"tpb":4,"wgs":128,"tpt":[ 32,0 ],"factors":[ 2,4,4,8 ],"ebtype":"NONE","direction":-1,"static_dim":0,"placement":"NA","iAryType":"CI","oAryType":"CI"}}} ]}, {"Problem":{"arch":"gfx90a","token":"kernel_len25_single_sbrr"}, "Solutions":[ {"sol_node_type":"SOL_KERNEL_ONLY","kernel_key":{"lengths":[ 25,0 ],"precision":"single","scheme":"CS_KERNEL_STOCKHAM","sbrc_trans":"NONE","kernelConfig":{"use_3steps":false,"half_lds":false,"dir_reg":true,"buffer_inst":false,"tpb":13,"wgs":65,"tpt":[ 5,0 ],"factors":[ 5,5 ],"ebtype":"NONE","direction":-1,"static_dim":3,"placement":"IP","iAryType":"CI","oAryType":"HI"}}} ,{"sol_node_type":"SOL_KERNEL_ONLY","kernel_key":{"lengths":[ 25,0 ],"precision":"single","scheme":"CS_KERNEL_STOCKHAM","sbrc_trans":"NONE","kernelConfig":{"use_3steps":false,"half_lds":false,"dir_reg":true,"buffer_inst":false,"tpb":25,"wgs":125,"tpt":[ 5,0 ],"factors":[ 5,5 ],"ebtype":"NONE","direction":1,"static_dim":2,"placement":"IP","iAryType":"CI","oAryType":"CI"}}} ]}, {"Problem":{"arch":"gfx90a","token":"kernel_len26_double_sbrr"}, "Solutions":[ {"sol_node_type":"SOL_KERNEL_ONLY","kernel_key":{"lengths":[ 26,0 ],"precision":"double","scheme":"CS_KERNEL_STOCKHAM","sbrc_trans":"NONE","kernelConfig":{"use_3steps":false,"half_lds":false,"dir_reg":true,"buffer_inst":false,"tpb":32,"wgs":64,"tpt":[ 2,0 ],"factors":[ 13,2 ],"ebtype":"R2C_POST","direction":-1,"static_dim":1,"placement":"OP","iAryType":"CI","oAryType":"CI"}}} ,{"sol_node_type":"SOL_KERNEL_ONLY","kernel_key":{"lengths":[ 26,0 ],"precision":"double","scheme":"CS_KERNEL_STOCKHAM","sbrc_trans":"NONE","kernelConfig":{"use_3steps":false,"half_lds":false,"dir_reg":true,"buffer_inst":false,"tpb":64,"wgs":128,"tpt":[ 2,0 ],"factors":[ 13,2 ],"ebtype":"NONE","direction":-1,"static_dim":2,"placement":"IP","iAryType":"CI","oAryType":"HI"}}} ]}, {"Problem":{"arch":"gfx90a","token":"kernel_len4096_single_sbrr"}, "Solutions":[ {"sol_node_type":"SOL_KERNEL_ONLY","kernel_key":{"lengths":[ 4096,0 ],"precision":"single","scheme":"CS_KERNEL_STOCKHAM","sbrc_trans":"NONE","kernelConfig":{"use_3steps":false,"half_lds":true,"dir_reg":true,"buffer_inst":false,"tpb":2,"wgs":256,"tpt":[ 128,0 ],"factors":[ 8,16,4,8 ],"ebtype":"NONE","direction":-1,"static_dim":0,"placement":"NA","iAryType":"CI","oAryType":"CI"}}} ]}, {"Problem":{"arch":"gfx90a","token":"kernel_len40_single_sbrr"}, "Solutions":[ {"sol_node_type":"SOL_KERNEL_ONLY","kernel_key":{"lengths":[ 40,0 ],"precision":"single","scheme":"CS_KERNEL_STOCKHAM","sbrc_trans":"NONE","kernelConfig":{"use_3steps":false,"half_lds":true,"dir_reg":true,"buffer_inst":false,"tpb":20,"wgs":200,"tpt":[ 10,0 ],"factors":[ 5,4,2 ],"ebtype":"R2C_POST","direction":-1,"static_dim":1,"placement":"OP","iAryType":"CI","oAryType":"CI"}}} ]}, {"Problem":{"arch":"gfx90a","token":"kernel_len42_single_sbrr"}, "Solutions":[ {"sol_node_type":"SOL_KERNEL_ONLY","kernel_key":{"lengths":[ 42,0 ],"precision":"single","scheme":"CS_KERNEL_STOCKHAM","sbrc_trans":"NONE","kernelConfig":{"use_3steps":false,"half_lds":false,"dir_reg":true,"buffer_inst":false,"tpb":9,"wgs":63,"tpt":[ 7,0 ],"factors":[ 2,3,7 ],"ebtype":"NONE","direction":1,"static_dim":2,"placement":"IP","iAryType":"CI","oAryType":"CI"}}} ]}, {"Problem":{"arch":"gfx90a","token":"kernel_len48_single_sbrr"}, "Solutions":[ {"sol_node_type":"SOL_KERNEL_ONLY","kernel_key":{"lengths":[ 48,0 ],"precision":"single","scheme":"CS_KERNEL_STOCKHAM","sbrc_trans":"NONE","kernelConfig":{"use_3steps":false,"half_lds":false,"dir_reg":true,"buffer_inst":false,"tpb":10,"wgs":60,"tpt":[ 6,0 ],"factors":[ 2,4,6 ],"ebtype":"R2C_POST","direction":-1,"static_dim":1,"placement":"OP","iAryType":"CI","oAryType":"CI"}}} ,{"sol_node_type":"SOL_KERNEL_ONLY","kernel_key":{"lengths":[ 48,0 ],"precision":"single","scheme":"CS_KERNEL_STOCKHAM","sbrc_trans":"NONE","kernelConfig":{"use_3steps":false,"half_lds":true,"dir_reg":true,"buffer_inst":false,"tpb":10,"wgs":60,"tpt":[ 6,0 ],"factors":[ 4,2,6 ],"ebtype":"C2R_PRE","direction":1,"static_dim":1,"placement":"OP","iAryType":"CI","oAryType":"CI"}}} ,{"sol_node_type":"SOL_KERNEL_ONLY","kernel_key":{"lengths":[ 48,0 ],"precision":"single","scheme":"CS_KERNEL_STOCKHAM","sbrc_trans":"NONE","kernelConfig":{"use_3steps":false,"half_lds":false,"dir_reg":true,"buffer_inst":false,"tpb":10,"wgs":60,"tpt":[ 6,0 ],"factors":[ 4,2,6 ],"ebtype":"C2R_PRE","direction":1,"static_dim":1,"placement":"OP","iAryType":"CI","oAryType":"CI"}}} ,{"sol_node_type":"SOL_KERNEL_ONLY","kernel_key":{"lengths":[ 48,0 ],"precision":"single","scheme":"CS_KERNEL_STOCKHAM","sbrc_trans":"NONE","kernelConfig":{"use_3steps":false,"half_lds":false,"dir_reg":true,"buffer_inst":false,"tpb":10,"wgs":60,"tpt":[ 6,0 ],"factors":[ 2,4,6 ],"ebtype":"C2R_PRE","direction":1,"static_dim":1,"placement":"OP","iAryType":"CI","oAryType":"CI"}}} ]}, {"Problem":{"arch":"gfx90a","token":"kernel_len50_double_sbrr"}, "Solutions":[ {"sol_node_type":"SOL_KERNEL_ONLY","kernel_key":{"lengths":[ 50,0 ],"precision":"double","scheme":"CS_KERNEL_STOCKHAM","sbrc_trans":"NONE","kernelConfig":{"use_3steps":false,"half_lds":true,"dir_reg":true,"buffer_inst":false,"tpb":25,"wgs":125,"tpt":[ 5,0 ],"factors":[ 10,5 ],"ebtype":"R2C_POST","direction":-1,"static_dim":1,"placement":"OP","iAryType":"CI","oAryType":"CI"}}} ,{"sol_node_type":"SOL_KERNEL_ONLY","kernel_key":{"lengths":[ 50,0 ],"precision":"double","scheme":"CS_KERNEL_STOCKHAM","sbrc_trans":"NONE","kernelConfig":{"use_3steps":false,"half_lds":true,"dir_reg":true,"buffer_inst":false,"tpb":6,"wgs":60,"tpt":[ 10,0 ],"factors":[ 2,5,5 ],"ebtype":"C2R_PRE","direction":1,"static_dim":1,"placement":"OP","iAryType":"CI","oAryType":"CI"}}} ]}, {"Problem":{"arch":"gfx90a","token":"kernel_len50_single_sbrr"}, "Solutions":[ {"sol_node_type":"SOL_KERNEL_ONLY","kernel_key":{"lengths":[ 50,0 ],"precision":"single","scheme":"CS_KERNEL_STOCKHAM","sbrc_trans":"NONE","kernelConfig":{"use_3steps":false,"half_lds":false,"dir_reg":true,"buffer_inst":false,"tpb":6,"wgs":60,"tpt":[ 10,0 ],"factors":[ 5,2,5 ],"ebtype":"R2C_POST","direction":-1,"static_dim":1,"placement":"OP","iAryType":"CI","oAryType":"CI"}}} ,{"sol_node_type":"SOL_KERNEL_ONLY","kernel_key":{"lengths":[ 50,0 ],"precision":"single","scheme":"CS_KERNEL_STOCKHAM","sbrc_trans":"NONE","kernelConfig":{"use_3steps":false,"half_lds":true,"dir_reg":true,"buffer_inst":false,"tpb":6,"wgs":60,"tpt":[ 10,0 ],"factors":[ 2,5,5 ],"ebtype":"C2R_PRE","direction":1,"static_dim":1,"placement":"OP","iAryType":"CI","oAryType":"CI"}}} ,{"sol_node_type":"SOL_KERNEL_ONLY","kernel_key":{"lengths":[ 50,0 ],"precision":"single","scheme":"CS_KERNEL_STOCKHAM","sbrc_trans":"NONE","kernelConfig":{"use_3steps":false,"half_lds":false,"dir_reg":true,"buffer_inst":false,"tpb":6,"wgs":60,"tpt":[ 10,0 ],"factors":[ 5,5,2 ],"ebtype":"R2C_POST","direction":-1,"static_dim":1,"placement":"OP","iAryType":"CI","oAryType":"CI"}}} ,{"sol_node_type":"SOL_KERNEL_ONLY","kernel_key":{"lengths":[ 50,0 ],"precision":"single","scheme":"CS_KERNEL_STOCKHAM","sbrc_trans":"NONE","kernelConfig":{"use_3steps":false,"half_lds":false,"dir_reg":true,"buffer_inst":false,"tpb":6,"wgs":60,"tpt":[ 10,0 ],"factors":[ 2,5,5 ],"ebtype":"C2R_PRE","direction":1,"static_dim":1,"placement":"OP","iAryType":"CI","oAryType":"CI"}}} ,{"sol_node_type":"SOL_KERNEL_ONLY","kernel_key":{"lengths":[ 50,0 ],"precision":"single","scheme":"CS_KERNEL_STOCKHAM","sbrc_trans":"NONE","kernelConfig":{"use_3steps":false,"half_lds":true,"dir_reg":true,"buffer_inst":false,"tpb":12,"wgs":60,"tpt":[ 5,0 ],"factors":[ 2,5,5 ],"ebtype":"C2R_PRE","direction":1,"static_dim":1,"placement":"OP","iAryType":"CI","oAryType":"CI"}}} ,{"sol_node_type":"SOL_KERNEL_ONLY","kernel_key":{"lengths":[ 50,0 ],"precision":"single","scheme":"CS_KERNEL_STOCKHAM","sbrc_trans":"NONE","kernelConfig":{"use_3steps":false,"half_lds":false,"dir_reg":true,"buffer_inst":false,"tpb":25,"wgs":125,"tpt":[ 5,0 ],"factors":[ 2,5,5 ],"ebtype":"R2C_POST","direction":-1,"static_dim":1,"placement":"OP","iAryType":"CI","oAryType":"CI"}}} ]}, {"Problem":{"arch":"gfx90a","token":"kernel_len52_double_sbrr"}, "Solutions":[ {"sol_node_type":"SOL_KERNEL_ONLY","kernel_key":{"lengths":[ 52,0 ],"precision":"double","scheme":"CS_KERNEL_STOCKHAM","sbrc_trans":"NONE","kernelConfig":{"use_3steps":false,"half_lds":true,"dir_reg":true,"buffer_inst":false,"tpb":16,"wgs":64,"tpt":[ 4,0 ],"factors":[ 13,4 ],"ebtype":"R2C_POST","direction":-1,"static_dim":1,"placement":"OP","iAryType":"CI","oAryType":"CI"}}} ,{"sol_node_type":"SOL_KERNEL_ONLY","kernel_key":{"lengths":[ 52,0 ],"precision":"double","scheme":"CS_KERNEL_STOCKHAM","sbrc_trans":"NONE","kernelConfig":{"use_3steps":false,"half_lds":true,"dir_reg":true,"buffer_inst":false,"tpb":32,"wgs":128,"tpt":[ 4,0 ],"factors":[ 13,4 ],"ebtype":"C2R_PRE","direction":1,"static_dim":1,"placement":"OP","iAryType":"CI","oAryType":"CI"}}} ]}, {"Problem":{"arch":"gfx90a","token":"kernel_len52_single_sbrr"}, "Solutions":[ {"sol_node_type":"SOL_KERNEL_ONLY","kernel_key":{"lengths":[ 52,0 ],"precision":"single","scheme":"CS_KERNEL_STOCKHAM","sbrc_trans":"NONE","kernelConfig":{"use_3steps":false,"half_lds":true,"dir_reg":true,"buffer_inst":false,"tpb":9,"wgs":117,"tpt":[ 13,0 ],"factors":[ 4,13 ],"ebtype":"R2C_POST","direction":-1,"static_dim":1,"placement":"OP","iAryType":"CI","oAryType":"CI"}}} ]}, {"Problem":{"arch":"gfx90a","token":"kernel_len54_single_sbrr"}, "Solutions":[ {"sol_node_type":"SOL_KERNEL_ONLY","kernel_key":{"lengths":[ 54,0 ],"precision":"single","scheme":"CS_KERNEL_STOCKHAM","sbrc_trans":"NONE","kernelConfig":{"use_3steps":false,"half_lds":true,"dir_reg":true,"buffer_inst":false,"tpb":21,"wgs":126,"tpt":[ 6,0 ],"factors":[ 3,3,6 ],"ebtype":"R2C_POST","direction":-1,"static_dim":1,"placement":"OP","iAryType":"CI","oAryType":"CI"}}} ,{"sol_node_type":"SOL_KERNEL_ONLY","kernel_key":{"lengths":[ 54,0 ],"precision":"single","scheme":"CS_KERNEL_STOCKHAM","sbrc_trans":"NONE","kernelConfig":{"use_3steps":false,"half_lds":false,"dir_reg":true,"buffer_inst":false,"tpb":10,"wgs":60,"tpt":[ 6,0 ],"factors":[ 2,3,3,3 ],"ebtype":"C2R_PRE","direction":1,"static_dim":1,"placement":"OP","iAryType":"CI","oAryType":"CI"}}} ,{"sol_node_type":"SOL_KERNEL_ONLY","kernel_key":{"lengths":[ 54,0 ],"precision":"single","scheme":"CS_KERNEL_STOCKHAM","sbrc_trans":"NONE","kernelConfig":{"use_3steps":false,"half_lds":false,"dir_reg":true,"buffer_inst":false,"tpb":10,"wgs":60,"tpt":[ 6,0 ],"factors":[ 3,3,6 ],"ebtype":"R2C_POST","direction":-1,"static_dim":1,"placement":"OP","iAryType":"CI","oAryType":"CI"}}} ,{"sol_node_type":"SOL_KERNEL_ONLY","kernel_key":{"lengths":[ 54,0 ],"precision":"single","scheme":"CS_KERNEL_STOCKHAM","sbrc_trans":"NONE","kernelConfig":{"use_3steps":false,"half_lds":true,"dir_reg":true,"buffer_inst":false,"tpb":10,"wgs":60,"tpt":[ 6,0 ],"factors":[ 2,3,3,3 ],"ebtype":"C2R_PRE","direction":1,"static_dim":1,"placement":"OP","iAryType":"CI","oAryType":"CI"}}} ]}, {"Problem":{"arch":"gfx90a","token":"kernel_len55_double_sbrr"}, "Solutions":[ {"sol_node_type":"SOL_KERNEL_ONLY","kernel_key":{"lengths":[ 55,0 ],"precision":"double","scheme":"CS_KERNEL_STOCKHAM","sbrc_trans":"NONE","kernelConfig":{"use_3steps":false,"half_lds":true,"dir_reg":true,"buffer_inst":false,"tpb":12,"wgs":132,"tpt":[ 11,0 ],"factors":[ 5,11 ],"ebtype":"NONE","direction":1,"static_dim":1,"placement":"IP","iAryType":"CI","oAryType":"CI"}}} ,{"sol_node_type":"SOL_KERNEL_ONLY","kernel_key":{"lengths":[ 55,0 ],"precision":"double","scheme":"CS_KERNEL_STOCKHAM","sbrc_trans":"NONE","kernelConfig":{"use_3steps":false,"half_lds":false,"dir_reg":true,"buffer_inst":false,"tpb":5,"wgs":55,"tpt":[ 11,0 ],"factors":[ 5,11 ],"ebtype":"NONE","direction":1,"static_dim":1,"placement":"IP","iAryType":"CI","oAryType":"CI"}}} ,{"sol_node_type":"SOL_KERNEL_ONLY","kernel_key":{"lengths":[ 55,0 ],"precision":"double","scheme":"CS_KERNEL_STOCKHAM","sbrc_trans":"NONE","kernelConfig":{"use_3steps":false,"half_lds":false,"dir_reg":true,"buffer_inst":false,"tpb":12,"wgs":132,"tpt":[ 11,0 ],"factors":[ 5,11 ],"ebtype":"NONE","direction":1,"static_dim":1,"placement":"IP","iAryType":"CI","oAryType":"CI"}}} ]}, {"Problem":{"arch":"gfx90a","token":"kernel_len55_single_sbrr"}, "Solutions":[ {"sol_node_type":"SOL_KERNEL_ONLY","kernel_key":{"lengths":[ 55,0 ],"precision":"single","scheme":"CS_KERNEL_STOCKHAM","sbrc_trans":"NONE","kernelConfig":{"use_3steps":false,"half_lds":true,"dir_reg":true,"buffer_inst":false,"tpb":12,"wgs":132,"tpt":[ 11,0 ],"factors":[ 5,11 ],"ebtype":"NONE","direction":-1,"static_dim":1,"placement":"IP","iAryType":"CI","oAryType":"CI"}}} ,{"sol_node_type":"SOL_KERNEL_ONLY","kernel_key":{"lengths":[ 55,0 ],"precision":"single","scheme":"CS_KERNEL_STOCKHAM","sbrc_trans":"NONE","kernelConfig":{"use_3steps":false,"half_lds":true,"dir_reg":true,"buffer_inst":false,"tpb":11,"wgs":121,"tpt":[ 11,0 ],"factors":[ 5,11 ],"ebtype":"NONE","direction":-1,"static_dim":1,"placement":"IP","iAryType":"CI","oAryType":"CI"}}} ,{"sol_node_type":"SOL_KERNEL_ONLY","kernel_key":{"lengths":[ 55,0 ],"precision":"single","scheme":"CS_KERNEL_STOCKHAM","sbrc_trans":"NONE","kernelConfig":{"use_3steps":false,"half_lds":false,"dir_reg":true,"buffer_inst":false,"tpb":11,"wgs":121,"tpt":[ 11,0 ],"factors":[ 5,11 ],"ebtype":"NONE","direction":1,"static_dim":1,"placement":"IP","iAryType":"CI","oAryType":"CI"}}} ,{"sol_node_type":"SOL_KERNEL_ONLY","kernel_key":{"lengths":[ 55,0 ],"precision":"single","scheme":"CS_KERNEL_STOCKHAM","sbrc_trans":"NONE","kernelConfig":{"use_3steps":false,"half_lds":false,"dir_reg":true,"buffer_inst":false,"tpb":6,"wgs":66,"tpt":[ 11,0 ],"factors":[ 5,11 ],"ebtype":"NONE","direction":1,"static_dim":1,"placement":"IP","iAryType":"CI","oAryType":"CI"}}} ]}, {"Problem":{"arch":"gfx90a","token":"kernel_len56_double_sbrr"}, "Solutions":[ {"sol_node_type":"SOL_KERNEL_ONLY","kernel_key":{"lengths":[ 56,0 ],"precision":"double","scheme":"CS_KERNEL_STOCKHAM","sbrc_trans":"NONE","kernelConfig":{"use_3steps":false,"half_lds":false,"dir_reg":true,"buffer_inst":false,"tpb":16,"wgs":128,"tpt":[ 8,0 ],"factors":[ 7,4,2 ],"ebtype":"NONE","direction":-1,"static_dim":0,"placement":"NA","iAryType":"CI","oAryType":"CI"}}} ,{"sol_node_type":"SOL_KERNEL_ONLY","kernel_key":{"lengths":[ 56,0 ],"precision":"double","scheme":"CS_KERNEL_STOCKHAM","sbrc_trans":"NONE","kernelConfig":{"use_3steps":false,"half_lds":false,"dir_reg":true,"buffer_inst":false,"tpb":8,"wgs":64,"tpt":[ 8,0 ],"factors":[ 7,4,2 ],"ebtype":"NONE","direction":-1,"static_dim":0,"placement":"NA","iAryType":"CI","oAryType":"CI"}}} ,{"sol_node_type":"SOL_KERNEL_ONLY","kernel_key":{"lengths":[ 56,0 ],"precision":"double","scheme":"CS_KERNEL_STOCKHAM","sbrc_trans":"NONE","kernelConfig":{"use_3steps":false,"half_lds":false,"dir_reg":true,"buffer_inst":false,"tpb":24,"wgs":192,"tpt":[ 8,0 ],"factors":[ 7,2,4 ],"ebtype":"NONE","direction":-1,"static_dim":0,"placement":"NA","iAryType":"CI","oAryType":"CI"}}} ,{"sol_node_type":"SOL_KERNEL_ONLY","kernel_key":{"lengths":[ 56,0 ],"precision":"double","scheme":"CS_KERNEL_STOCKHAM","sbrc_trans":"NONE","kernelConfig":{"use_3steps":false,"half_lds":false,"dir_reg":true,"buffer_inst":false,"tpb":16,"wgs":128,"tpt":[ 8,0 ],"factors":[ 4,7,2 ],"ebtype":"NONE","direction":-1,"static_dim":0,"placement":"NA","iAryType":"CI","oAryType":"CI"}}} ]}, {"Problem":{"arch":"gfx90a","token":"kernel_len56_single_sbrr"}, "Solutions":[ {"sol_node_type":"SOL_KERNEL_ONLY","kernel_key":{"lengths":[ 56,0 ],"precision":"single","scheme":"CS_KERNEL_STOCKHAM","sbrc_trans":"NONE","kernelConfig":{"use_3steps":false,"half_lds":true,"dir_reg":true,"buffer_inst":false,"tpb":18,"wgs":252,"tpt":[ 14,0 ],"factors":[ 7,4,2 ],"ebtype":"R2C_POST","direction":-1,"static_dim":1,"placement":"OP","iAryType":"CI","oAryType":"CI"}}} ,{"sol_node_type":"SOL_KERNEL_ONLY","kernel_key":{"lengths":[ 56,0 ],"precision":"single","scheme":"CS_KERNEL_STOCKHAM","sbrc_trans":"NONE","kernelConfig":{"use_3steps":false,"half_lds":false,"dir_reg":true,"buffer_inst":false,"tpb":4,"wgs":56,"tpt":[ 14,0 ],"factors":[ 2,4,7 ],"ebtype":"C2R_PRE","direction":1,"static_dim":1,"placement":"OP","iAryType":"CI","oAryType":"CI"}}} ]}, {"Problem":{"arch":"gfx90a","token":"kernel_len64_single_sbrr"}, "Solutions":[ {"sol_node_type":"SOL_KERNEL_ONLY","kernel_key":{"lengths":[ 64,0 ],"precision":"single","scheme":"CS_KERNEL_STOCKHAM","sbrc_trans":"NONE","kernelConfig":{"use_3steps":false,"half_lds":true,"dir_reg":true,"buffer_inst":false,"tpb":8,"wgs":64,"tpt":[ 8,0 ],"factors":[ 8,8 ],"ebtype":"R2C_POST","direction":-1,"static_dim":1,"placement":"OP","iAryType":"CI","oAryType":"CI"}}} ,{"sol_node_type":"SOL_KERNEL_ONLY","kernel_key":{"lengths":[ 64,0 ],"precision":"single","scheme":"CS_KERNEL_STOCKHAM","sbrc_trans":"NONE","kernelConfig":{"use_3steps":false,"half_lds":false,"dir_reg":true,"buffer_inst":false,"tpb":8,"wgs":64,"tpt":[ 8,0 ],"factors":[ 8,8 ],"ebtype":"C2R_PRE","direction":1,"static_dim":1,"placement":"OP","iAryType":"CI","oAryType":"CI"}}} ]}, {"Problem":{"arch":"gfx90a","token":"kernel_len72_single_sbrr"}, "Solutions":[ {"sol_node_type":"SOL_KERNEL_ONLY","kernel_key":{"lengths":[ 72,0 ],"precision":"single","scheme":"CS_KERNEL_STOCKHAM","sbrc_trans":"NONE","kernelConfig":{"use_3steps":false,"half_lds":true,"dir_reg":true,"buffer_inst":false,"tpb":8,"wgs":64,"tpt":[ 8,0 ],"factors":[ 3,3,8 ],"ebtype":"R2C_POST","direction":-1,"static_dim":1,"placement":"OP","iAryType":"CI","oAryType":"CI"}}} ,{"sol_node_type":"SOL_KERNEL_ONLY","kernel_key":{"lengths":[ 72,0 ],"precision":"single","scheme":"CS_KERNEL_STOCKHAM","sbrc_trans":"NONE","kernelConfig":{"use_3steps":false,"half_lds":false,"dir_reg":true,"buffer_inst":false,"tpb":8,"wgs":64,"tpt":[ 8,0 ],"factors":[ 2,3,3,4 ],"ebtype":"C2R_PRE","direction":1,"static_dim":1,"placement":"OP","iAryType":"CI","oAryType":"CI"}}} ]}, {"Problem":{"arch":"gfx90a","token":"kernel_len75_double_sbrr"}, "Solutions":[ {"sol_node_type":"SOL_KERNEL_ONLY","kernel_key":{"lengths":[ 75,0 ],"precision":"double","scheme":"CS_KERNEL_STOCKHAM","sbrc_trans":"NONE","kernelConfig":{"use_3steps":false,"half_lds":false,"dir_reg":true,"buffer_inst":false,"tpb":13,"wgs":195,"tpt":[ 15,0 ],"factors":[ 5,3,5 ],"ebtype":"NONE","direction":1,"static_dim":1,"placement":"IP","iAryType":"CI","oAryType":"CI"}}} ]}, {"Problem":{"arch":"gfx90a","token":"kernel_len75_single_sbrr"}, "Solutions":[ {"sol_node_type":"SOL_KERNEL_ONLY","kernel_key":{"lengths":[ 75,0 ],"precision":"single","scheme":"CS_KERNEL_STOCKHAM","sbrc_trans":"NONE","kernelConfig":{"use_3steps":false,"half_lds":false,"dir_reg":true,"buffer_inst":false,"tpb":8,"wgs":120,"tpt":[ 15,0 ],"factors":[ 3,5,5 ],"ebtype":"NONE","direction":-1,"static_dim":1,"placement":"IP","iAryType":"CI","oAryType":"CI"}}} ]}, {"Problem":{"arch":"gfx90a","token":"kernel_len81_double_sbrr"}, "Solutions":[ {"sol_node_type":"SOL_KERNEL_ONLY","kernel_key":{"lengths":[ 81,0 ],"precision":"double","scheme":"CS_KERNEL_STOCKHAM","sbrc_trans":"NONE","kernelConfig":{"use_3steps":false,"half_lds":false,"dir_reg":true,"buffer_inst":false,"tpb":21,"wgs":189,"tpt":[ 9,0 ],"factors":[ 9,3,3 ],"ebtype":"NONE","direction":-1,"static_dim":0,"placement":"NA","iAryType":"CI","oAryType":"CI"}}} ]}, {"Problem":{"arch":"gfx90a","token":"kernel_len84_single_sbrr"}, "Solutions":[ {"sol_node_type":"SOL_KERNEL_ONLY","kernel_key":{"lengths":[ 84,0 ],"precision":"single","scheme":"CS_KERNEL_STOCKHAM","sbrc_trans":"NONE","kernelConfig":{"use_3steps":false,"half_lds":true,"dir_reg":true,"buffer_inst":false,"tpb":9,"wgs":126,"tpt":[ 14,0 ],"factors":[ 2,6,7 ],"ebtype":"R2C_POST","direction":-1,"static_dim":1,"placement":"OP","iAryType":"CI","oAryType":"CI"}}} ,{"sol_node_type":"SOL_KERNEL_ONLY","kernel_key":{"lengths":[ 84,0 ],"precision":"single","scheme":"CS_KERNEL_STOCKHAM","sbrc_trans":"NONE","kernelConfig":{"use_3steps":false,"half_lds":false,"dir_reg":true,"buffer_inst":false,"tpb":9,"wgs":126,"tpt":[ 14,0 ],"factors":[ 2,6,7 ],"ebtype":"C2R_PRE","direction":1,"static_dim":1,"placement":"OP","iAryType":"CI","oAryType":"CI"}}} ,{"sol_node_type":"SOL_KERNEL_ONLY","kernel_key":{"lengths":[ 84,0 ],"precision":"single","scheme":"CS_KERNEL_STOCKHAM","sbrc_trans":"NONE","kernelConfig":{"use_3steps":false,"half_lds":false,"dir_reg":true,"buffer_inst":false,"tpb":18,"wgs":252,"tpt":[ 14,0 ],"factors":[ 7,6,2 ],"ebtype":"R2C_POST","direction":-1,"static_dim":1,"placement":"OP","iAryType":"CI","oAryType":"CI"}}} ,{"sol_node_type":"SOL_KERNEL_ONLY","kernel_key":{"lengths":[ 84,0 ],"precision":"single","scheme":"CS_KERNEL_STOCKHAM","sbrc_trans":"NONE","kernelConfig":{"use_3steps":false,"half_lds":true,"dir_reg":true,"buffer_inst":false,"tpb":9,"wgs":126,"tpt":[ 14,0 ],"factors":[ 2,6,7 ],"ebtype":"C2R_PRE","direction":1,"static_dim":1,"placement":"OP","iAryType":"CI","oAryType":"CI"}}} ]}, {"Problem":{"arch":"gfx90a","token":"kernel_len96_single_sbrr"}, "Solutions":[ {"sol_node_type":"SOL_KERNEL_ONLY","kernel_key":{"lengths":[ 96,0 ],"precision":"single","scheme":"CS_KERNEL_STOCKHAM","sbrc_trans":"NONE","kernelConfig":{"use_3steps":false,"half_lds":false,"dir_reg":true,"buffer_inst":false,"tpb":8,"wgs":64,"tpt":[ 8,0 ],"factors":[ 2,2,3,8 ],"ebtype":"R2C_POST","direction":-1,"static_dim":1,"placement":"OP","iAryType":"CI","oAryType":"CI"}}} ,{"sol_node_type":"SOL_KERNEL_ONLY","kernel_key":{"lengths":[ 96,0 ],"precision":"single","scheme":"CS_KERNEL_STOCKHAM","sbrc_trans":"NONE","kernelConfig":{"use_3steps":false,"half_lds":false,"dir_reg":true,"buffer_inst":false,"tpb":8,"wgs":64,"tpt":[ 8,0 ],"factors":[ 2,3,4,4 ],"ebtype":"C2R_PRE","direction":1,"static_dim":1,"placement":"OP","iAryType":"CI","oAryType":"CI"}}} ,{"sol_node_type":"SOL_KERNEL_ONLY","kernel_key":{"lengths":[ 96,0 ],"precision":"single","scheme":"CS_KERNEL_STOCKHAM","sbrc_trans":"NONE","kernelConfig":{"use_3steps":false,"half_lds":false,"dir_reg":true,"buffer_inst":false,"tpb":8,"wgs":64,"tpt":[ 8,0 ],"factors":[ 2,6,8 ],"ebtype":"R2C_POST","direction":-1,"static_dim":1,"placement":"OP","iAryType":"CI","oAryType":"CI"}}} ,{"sol_node_type":"SOL_KERNEL_ONLY","kernel_key":{"lengths":[ 96,0 ],"precision":"single","scheme":"CS_KERNEL_STOCKHAM","sbrc_trans":"NONE","kernelConfig":{"use_3steps":false,"half_lds":true,"dir_reg":true,"buffer_inst":false,"tpb":8,"wgs":64,"tpt":[ 8,0 ],"factors":[ 2,3,4,4 ],"ebtype":"C2R_PRE","direction":1,"static_dim":1,"placement":"OP","iAryType":"CI","oAryType":"CI"}}} ,{"sol_node_type":"SOL_KERNEL_ONLY","kernel_key":{"lengths":[ 96,0 ],"precision":"single","scheme":"CS_KERNEL_STOCKHAM","sbrc_trans":"NONE","kernelConfig":{"use_3steps":false,"half_lds":true,"dir_reg":true,"buffer_inst":false,"tpb":8,"wgs":64,"tpt":[ 8,0 ],"factors":[ 2,6,8 ],"ebtype":"R2C_POST","direction":-1,"static_dim":1,"placement":"OP","iAryType":"CI","oAryType":"CI"}}} ,{"sol_node_type":"SOL_KERNEL_ONLY","kernel_key":{"lengths":[ 96,0 ],"precision":"single","scheme":"CS_KERNEL_STOCKHAM","sbrc_trans":"NONE","kernelConfig":{"use_3steps":false,"half_lds":true,"dir_reg":true,"buffer_inst":false,"tpb":16,"wgs":128,"tpt":[ 8,0 ],"factors":[ 2,3,4,4 ],"ebtype":"C2R_PRE","direction":1,"static_dim":1,"placement":"OP","iAryType":"CI","oAryType":"CI"}}} ,{"sol_node_type":"SOL_KERNEL_ONLY","kernel_key":{"lengths":[ 96,0 ],"precision":"single","scheme":"CS_KERNEL_STOCKHAM","sbrc_trans":"NONE","kernelConfig":{"use_3steps":false,"half_lds":true,"dir_reg":true,"buffer_inst":false,"tpb":8,"wgs":64,"tpt":[ 8,0 ],"factors":[ 4,3,8 ],"ebtype":"R2C_POST","direction":-1,"static_dim":1,"placement":"OP","iAryType":"CI","oAryType":"CI"}}} ,{"sol_node_type":"SOL_KERNEL_ONLY","kernel_key":{"lengths":[ 96,0 ],"precision":"single","scheme":"CS_KERNEL_STOCKHAM","sbrc_trans":"NONE","kernelConfig":{"use_3steps":false,"half_lds":false,"dir_reg":true,"buffer_inst":false,"tpb":16,"wgs":128,"tpt":[ 8,0 ],"factors":[ 4,3,8 ],"ebtype":"C2R_PRE","direction":1,"static_dim":1,"placement":"OP","iAryType":"CI","oAryType":"CI"}}} ]}, {"Problem":{"arch":"gfx90a","token":"kernel_len100_double_sbcc"}, "Solutions":[ {"sol_node_type":"SOL_KERNEL_ONLY","kernel_key":{"lengths":[ 100,0 ],"precision":"double","scheme":"CS_KERNEL_STOCKHAM_BLOCK_CC","sbrc_trans":"NONE","kernelConfig":{"use_3steps":true,"half_lds":true,"dir_reg":true,"buffer_inst":false,"tpb":12,"wgs":120,"tpt":[ 10,0 ],"factors":[ 10,2,5 ],"ebtype":"NONE","direction":-1,"static_dim":0,"placement":"NA","iAryType":"CI","oAryType":"CI"}}} ,{"sol_node_type":"SOL_KERNEL_ONLY","kernel_key":{"lengths":[ 100,0 ],"precision":"double","scheme":"CS_KERNEL_STOCKHAM_BLOCK_CC","sbrc_trans":"NONE","kernelConfig":{"use_3steps":false,"half_lds":false,"dir_reg":true,"buffer_inst":false,"tpb":20,"wgs":200,"tpt":[ 10,0 ],"factors":[ 2,10,5 ],"ebtype":"NONE","direction":-1,"static_dim":3,"placement":"IP","iAryType":"CI","oAryType":"CI"}}} ,{"sol_node_type":"SOL_KERNEL_ONLY","kernel_key":{"lengths":[ 100,0 ],"precision":"double","scheme":"CS_KERNEL_STOCKHAM_BLOCK_CC","sbrc_trans":"NONE","kernelConfig":{"use_3steps":false,"half_lds":false,"dir_reg":true,"buffer_inst":false,"tpb":13,"wgs":130,"tpt":[ 10,0 ],"factors":[ 2,2,5,5 ],"ebtype":"NONE","direction":-1,"static_dim":2,"placement":"IP","iAryType":"CI","oAryType":"HI"}}} ,{"sol_node_type":"SOL_KERNEL_ONLY","kernel_key":{"lengths":[ 100,0 ],"precision":"double","scheme":"CS_KERNEL_STOCKHAM_BLOCK_CC","sbrc_trans":"NONE","kernelConfig":{"use_3steps":false,"half_lds":false,"dir_reg":true,"buffer_inst":true,"tpb":19,"wgs":190,"tpt":[ 10,0 ],"factors":[ 10,5,2 ],"ebtype":"NONE","direction":1,"static_dim":3,"placement":"IP","iAryType":"HI","oAryType":"CI"}}} ,{"sol_node_type":"SOL_KERNEL_ONLY","kernel_key":{"lengths":[ 100,0 ],"precision":"double","scheme":"CS_KERNEL_STOCKHAM_BLOCK_CC","sbrc_trans":"NONE","kernelConfig":{"use_3steps":false,"half_lds":false,"dir_reg":true,"buffer_inst":true,"tpb":19,"wgs":190,"tpt":[ 10,0 ],"factors":[ 10,10 ],"ebtype":"NONE","direction":1,"static_dim":2,"placement":"IP","iAryType":"CI","oAryType":"CI"}}} ]}, {"Problem":{"arch":"gfx90a","token":"kernel_len100_single_sbcc"}, "Solutions":[ {"sol_node_type":"SOL_KERNEL_ONLY","kernel_key":{"lengths":[ 100,0 ],"precision":"single","scheme":"CS_KERNEL_STOCKHAM_BLOCK_CC","sbrc_trans":"NONE","kernelConfig":{"use_3steps":false,"half_lds":false,"dir_reg":true,"buffer_inst":false,"tpb":25,"wgs":250,"tpt":[ 10,0 ],"factors":[ 10,5,2 ],"ebtype":"NONE","direction":-1,"static_dim":3,"placement":"IP","iAryType":"CI","oAryType":"CI"}}} ,{"sol_node_type":"SOL_KERNEL_ONLY","kernel_key":{"lengths":[ 100,0 ],"precision":"single","scheme":"CS_KERNEL_STOCKHAM_BLOCK_CC","sbrc_trans":"NONE","kernelConfig":{"use_3steps":false,"half_lds":false,"dir_reg":true,"buffer_inst":false,"tpb":25,"wgs":250,"tpt":[ 10,0 ],"factors":[ 5,2,10 ],"ebtype":"NONE","direction":-1,"static_dim":2,"placement":"IP","iAryType":"CI","oAryType":"HI"}}} ,{"sol_node_type":"SOL_KERNEL_ONLY","kernel_key":{"lengths":[ 100,0 ],"precision":"single","scheme":"CS_KERNEL_STOCKHAM_BLOCK_CC","sbrc_trans":"NONE","kernelConfig":{"use_3steps":false,"half_lds":false,"dir_reg":true,"buffer_inst":true,"tpb":25,"wgs":250,"tpt":[ 10,0 ],"factors":[ 5,10,2 ],"ebtype":"NONE","direction":1,"static_dim":3,"placement":"IP","iAryType":"HI","oAryType":"CI"}}} ,{"sol_node_type":"SOL_KERNEL_ONLY","kernel_key":{"lengths":[ 100,0 ],"precision":"single","scheme":"CS_KERNEL_STOCKHAM_BLOCK_CC","sbrc_trans":"NONE","kernelConfig":{"use_3steps":false,"half_lds":false,"dir_reg":true,"buffer_inst":true,"tpb":25,"wgs":250,"tpt":[ 10,0 ],"factors":[ 10,10 ],"ebtype":"NONE","direction":1,"static_dim":2,"placement":"IP","iAryType":"CI","oAryType":"CI"}}} ]}, {"Problem":{"arch":"gfx90a","token":"kernel_len104_double_sbcc"}, "Solutions":[ {"sol_node_type":"SOL_KERNEL_ONLY","kernel_key":{"lengths":[ 104,0 ],"precision":"double","scheme":"CS_KERNEL_STOCKHAM_BLOCK_CC","sbrc_trans":"NONE","kernelConfig":{"use_3steps":false,"half_lds":false,"dir_reg":true,"buffer_inst":true,"tpb":19,"wgs":152,"tpt":[ 8,0 ],"factors":[ 2,2,13,2 ],"ebtype":"NONE","direction":-1,"static_dim":2,"placement":"IP","iAryType":"CI","oAryType":"HI"}}} ,{"sol_node_type":"SOL_KERNEL_ONLY","kernel_key":{"lengths":[ 104,0 ],"precision":"double","scheme":"CS_KERNEL_STOCKHAM_BLOCK_CC","sbrc_trans":"NONE","kernelConfig":{"use_3steps":false,"half_lds":false,"dir_reg":true,"buffer_inst":true,"tpb":8,"wgs":208,"tpt":[ 26,0 ],"factors":[ 4,13,2 ],"ebtype":"NONE","direction":1,"static_dim":2,"placement":"IP","iAryType":"CI","oAryType":"CI"}}} ]}, {"Problem":{"arch":"gfx90a","token":"kernel_len104_single_sbcc"}, "Solutions":[ {"sol_node_type":"SOL_KERNEL_ONLY","kernel_key":{"lengths":[ 104,0 ],"precision":"single","scheme":"CS_KERNEL_STOCKHAM_BLOCK_CC","sbrc_trans":"NONE","kernelConfig":{"use_3steps":false,"half_lds":true,"dir_reg":true,"buffer_inst":true,"tpb":8,"wgs":208,"tpt":[ 26,0 ],"factors":[ 4,13,2 ],"ebtype":"NONE","direction":-1,"static_dim":2,"placement":"IP","iAryType":"CI","oAryType":"HI"}}} ,{"sol_node_type":"SOL_KERNEL_ONLY","kernel_key":{"lengths":[ 104,0 ],"precision":"single","scheme":"CS_KERNEL_STOCKHAM_BLOCK_CC","sbrc_trans":"NONE","kernelConfig":{"use_3steps":false,"half_lds":true,"dir_reg":true,"buffer_inst":false,"tpb":8,"wgs":208,"tpt":[ 26,0 ],"factors":[ 2,13,2,2 ],"ebtype":"NONE","direction":1,"static_dim":2,"placement":"IP","iAryType":"CI","oAryType":"CI"}}} ]}, {"Problem":{"arch":"gfx90a","token":"kernel_len108_single_sbcc"}, "Solutions":[ {"sol_node_type":"SOL_KERNEL_ONLY","kernel_key":{"lengths":[ 108,0 ],"precision":"single","scheme":"CS_KERNEL_STOCKHAM_BLOCK_CC","sbrc_trans":"NONE","kernelConfig":{"use_3steps":false,"half_lds":false,"dir_reg":true,"buffer_inst":true,"tpb":21,"wgs":252,"tpt":[ 12,0 ],"factors":[ 9,2,6 ],"ebtype":"NONE","direction":-1,"static_dim":3,"placement":"IP","iAryType":"CI","oAryType":"CI"}}} ,{"sol_node_type":"SOL_KERNEL_ONLY","kernel_key":{"lengths":[ 108,0 ],"precision":"single","scheme":"CS_KERNEL_STOCKHAM_BLOCK_CC","sbrc_trans":"NONE","kernelConfig":{"use_3steps":false,"half_lds":false,"dir_reg":true,"buffer_inst":false,"tpb":21,"wgs":252,"tpt":[ 12,0 ],"factors":[ 9,3,4 ],"ebtype":"NONE","direction":-1,"static_dim":2,"placement":"IP","iAryType":"CI","oAryType":"HI"}}} ,{"sol_node_type":"SOL_KERNEL_ONLY","kernel_key":{"lengths":[ 108,0 ],"precision":"single","scheme":"CS_KERNEL_STOCKHAM_BLOCK_CC","sbrc_trans":"NONE","kernelConfig":{"use_3steps":false,"half_lds":false,"dir_reg":true,"buffer_inst":false,"tpb":22,"wgs":198,"tpt":[ 9,0 ],"factors":[ 3,4,9 ],"ebtype":"NONE","direction":-1,"static_dim":2,"placement":"IP","iAryType":"CI","oAryType":"HI"}}} ,{"sol_node_type":"SOL_KERNEL_ONLY","kernel_key":{"lengths":[ 108,0 ],"precision":"single","scheme":"CS_KERNEL_STOCKHAM_BLOCK_CC","sbrc_trans":"NONE","kernelConfig":{"use_3steps":false,"half_lds":false,"dir_reg":true,"buffer_inst":false,"tpb":28,"wgs":252,"tpt":[ 9,0 ],"factors":[ 2,6,9 ],"ebtype":"NONE","direction":-1,"static_dim":2,"placement":"IP","iAryType":"CI","oAryType":"HI"}}} ,{"sol_node_type":"SOL_KERNEL_ONLY","kernel_key":{"lengths":[ 108,0 ],"precision":"single","scheme":"CS_KERNEL_STOCKHAM_BLOCK_CC","sbrc_trans":"NONE","kernelConfig":{"use_3steps":false,"half_lds":true,"dir_reg":true,"buffer_inst":false,"tpb":28,"wgs":252,"tpt":[ 9,0 ],"factors":[ 2,2,3,9 ],"ebtype":"NONE","direction":1,"static_dim":2,"placement":"IP","iAryType":"CI","oAryType":"CI"}}} ,{"sol_node_type":"SOL_KERNEL_ONLY","kernel_key":{"lengths":[ 108,0 ],"precision":"single","scheme":"CS_KERNEL_STOCKHAM_BLOCK_CC","sbrc_trans":"NONE","kernelConfig":{"use_3steps":false,"half_lds":false,"dir_reg":true,"buffer_inst":false,"tpb":28,"wgs":252,"tpt":[ 9,0 ],"factors":[ 4,3,9 ],"ebtype":"NONE","direction":-1,"static_dim":2,"placement":"IP","iAryType":"CI","oAryType":"HI"}}} ,{"sol_node_type":"SOL_KERNEL_ONLY","kernel_key":{"lengths":[ 108,0 ],"precision":"single","scheme":"CS_KERNEL_STOCKHAM_BLOCK_CC","sbrc_trans":"NONE","kernelConfig":{"use_3steps":false,"half_lds":false,"dir_reg":true,"buffer_inst":false,"tpb":28,"wgs":252,"tpt":[ 9,0 ],"factors":[ 6,2,9 ],"ebtype":"NONE","direction":1,"static_dim":2,"placement":"IP","iAryType":"CI","oAryType":"CI"}}} ]}, {"Problem":{"arch":"gfx90a","token":"kernel_len112_single_sbcc"}, "Solutions":[ {"sol_node_type":"SOL_KERNEL_ONLY","kernel_key":{"lengths":[ 112,0 ],"precision":"single","scheme":"CS_KERNEL_STOCKHAM_BLOCK_CC","sbrc_trans":"NONE","kernelConfig":{"use_3steps":false,"half_lds":true,"dir_reg":true,"buffer_inst":false,"tpb":28,"wgs":196,"tpt":[ 7,0 ],"factors":[ 4,2,2,7 ],"ebtype":"NONE","direction":-1,"static_dim":2,"placement":"IP","iAryType":"CI","oAryType":"HI"}}} ,{"sol_node_type":"SOL_KERNEL_ONLY","kernel_key":{"lengths":[ 112,0 ],"precision":"single","scheme":"CS_KERNEL_STOCKHAM_BLOCK_CC","sbrc_trans":"NONE","kernelConfig":{"use_3steps":false,"half_lds":false,"dir_reg":true,"buffer_inst":false,"tpb":19,"wgs":133,"tpt":[ 7,0 ],"factors":[ 4,4,7 ],"ebtype":"NONE","direction":1,"static_dim":2,"placement":"IP","iAryType":"CI","oAryType":"CI"}}} ,{"sol_node_type":"SOL_KERNEL_ONLY","kernel_key":{"lengths":[ 112,0 ],"precision":"single","scheme":"CS_KERNEL_STOCKHAM_BLOCK_CC","sbrc_trans":"NONE","kernelConfig":{"use_3steps":false,"half_lds":false,"dir_reg":true,"buffer_inst":true,"tpb":19,"wgs":133,"tpt":[ 7,0 ],"factors":[ 4,4,7 ],"ebtype":"NONE","direction":-1,"static_dim":2,"placement":"IP","iAryType":"CI","oAryType":"HI"}}} ]}, {"Problem":{"arch":"gfx90a","token":"kernel_len125_double_sbcc"}, "Solutions":[ {"sol_node_type":"SOL_KERNEL_ONLY","kernel_key":{"lengths":[ 125,0 ],"precision":"double","scheme":"CS_KERNEL_STOCKHAM_BLOCK_CC","sbrc_trans":"NONE","kernelConfig":{"use_3steps":true,"half_lds":false,"dir_reg":true,"buffer_inst":true,"tpb":9,"wgs":225,"tpt":[ 25,0 ],"factors":[ 5,5,5 ],"ebtype":"NONE","direction":-1,"static_dim":0,"placement":"NA","iAryType":"CI","oAryType":"CI"}}} ,{"sol_node_type":"SOL_KERNEL_ONLY","kernel_key":{"lengths":[ 125,0 ],"precision":"double","scheme":"CS_KERNEL_STOCKHAM_BLOCK_CC","sbrc_trans":"NONE","kernelConfig":{"use_3steps":true,"half_lds":false,"dir_reg":true,"buffer_inst":true,"tpb":10,"wgs":250,"tpt":[ 25,0 ],"factors":[ 5,5,5 ],"ebtype":"NONE","direction":-1,"static_dim":0,"placement":"NA","iAryType":"CI","oAryType":"CI"}}} ]}, {"Problem":{"arch":"gfx90a","token":"kernel_len125_single_sbcc"}, "Solutions":[ {"sol_node_type":"SOL_KERNEL_ONLY","kernel_key":{"lengths":[ 125,0 ],"precision":"single","scheme":"CS_KERNEL_STOCKHAM_BLOCK_CC","sbrc_trans":"NONE","kernelConfig":{"use_3steps":false,"half_lds":false,"dir_reg":true,"buffer_inst":true,"tpb":32,"wgs":160,"tpt":[ 5,0 ],"factors":[ 5,5,5 ],"ebtype":"NONE","direction":-1,"static_dim":0,"placement":"NA","iAryType":"CI","oAryType":"CI"}}} ,{"sol_node_type":"SOL_KERNEL_ONLY","kernel_key":{"lengths":[ 125,0 ],"precision":"single","scheme":"CS_KERNEL_STOCKHAM_BLOCK_CC","sbrc_trans":"NONE","kernelConfig":{"use_3steps":false,"half_lds":false,"dir_reg":true,"buffer_inst":true,"tpb":20,"wgs":500,"tpt":[ 25,0 ],"factors":[ 5,5,5 ],"ebtype":"NONE","direction":-1,"static_dim":0,"placement":"NA","iAryType":"CI","oAryType":"CI"}}} ,{"sol_node_type":"SOL_KERNEL_ONLY","kernel_key":{"lengths":[ 125,0 ],"precision":"single","scheme":"CS_KERNEL_STOCKHAM_BLOCK_CC","sbrc_trans":"NONE","kernelConfig":{"use_3steps":false,"half_lds":false,"dir_reg":true,"buffer_inst":false,"tpb":20,"wgs":500,"tpt":[ 25,0 ],"factors":[ 5,5,5 ],"ebtype":"NONE","direction":-1,"static_dim":0,"placement":"NA","iAryType":"CI","oAryType":"CI"}}} ]}, {"Problem":{"arch":"gfx90a","token":"kernel_len128_single_sbcc"}, "Solutions":[ {"sol_node_type":"SOL_KERNEL_ONLY","kernel_key":{"lengths":[ 128,0 ],"precision":"single","scheme":"CS_KERNEL_STOCKHAM_BLOCK_CC","sbrc_trans":"NONE","kernelConfig":{"use_3steps":false,"half_lds":false,"dir_reg":true,"buffer_inst":true,"tpb":16,"wgs":128,"tpt":[ 8,0 ],"factors":[ 8,4,2,2 ],"ebtype":"NONE","direction":-1,"static_dim":3,"placement":"IP","iAryType":"CI","oAryType":"CI"}}} ,{"sol_node_type":"SOL_KERNEL_ONLY","kernel_key":{"lengths":[ 128,0 ],"precision":"single","scheme":"CS_KERNEL_STOCKHAM_BLOCK_CC","sbrc_trans":"NONE","kernelConfig":{"use_3steps":false,"half_lds":false,"dir_reg":true,"buffer_inst":true,"tpb":32,"wgs":128,"tpt":[ 4,0 ],"factors":[ 2,4,4,4 ],"ebtype":"NONE","direction":-1,"static_dim":2,"placement":"IP","iAryType":"CI","oAryType":"HI"}}} ,{"sol_node_type":"SOL_KERNEL_ONLY","kernel_key":{"lengths":[ 128,0 ],"precision":"single","scheme":"CS_KERNEL_STOCKHAM_BLOCK_CC","sbrc_trans":"NONE","kernelConfig":{"use_3steps":false,"half_lds":false,"dir_reg":true,"buffer_inst":true,"tpb":16,"wgs":128,"tpt":[ 8,0 ],"factors":[ 4,4,4,2 ],"ebtype":"NONE","direction":-1,"static_dim":2,"placement":"IP","iAryType":"CI","oAryType":"HI"}}} ,{"sol_node_type":"SOL_KERNEL_ONLY","kernel_key":{"lengths":[ 128,0 ],"precision":"single","scheme":"CS_KERNEL_STOCKHAM_BLOCK_CC","sbrc_trans":"NONE","kernelConfig":{"use_3steps":false,"half_lds":false,"dir_reg":true,"buffer_inst":false,"tpb":32,"wgs":128,"tpt":[ 4,0 ],"factors":[ 2,4,4,4 ],"ebtype":"NONE","direction":1,"static_dim":2,"placement":"IP","iAryType":"CI","oAryType":"CI"}}} ]}, {"Problem":{"arch":"gfx90a","token":"kernel_len160_single_sbcc"}, "Solutions":[ {"sol_node_type":"SOL_KERNEL_ONLY","kernel_key":{"lengths":[ 160,0 ],"precision":"single","scheme":"CS_KERNEL_STOCKHAM_BLOCK_CC","sbrc_trans":"NONE","kernelConfig":{"use_3steps":false,"half_lds":false,"dir_reg":true,"buffer_inst":true,"tpb":20,"wgs":200,"tpt":[ 10,0 ],"factors":[ 2,8,10 ],"ebtype":"NONE","direction":-1,"static_dim":3,"placement":"IP","iAryType":"CI","oAryType":"CI"}}} ,{"sol_node_type":"SOL_KERNEL_ONLY","kernel_key":{"lengths":[ 160,0 ],"precision":"single","scheme":"CS_KERNEL_STOCKHAM_BLOCK_CC","sbrc_trans":"NONE","kernelConfig":{"use_3steps":false,"half_lds":false,"dir_reg":true,"buffer_inst":false,"tpb":19,"wgs":190,"tpt":[ 10,0 ],"factors":[ 4,4,10 ],"ebtype":"NONE","direction":-1,"static_dim":2,"placement":"IP","iAryType":"CI","oAryType":"HI"}}} ,{"sol_node_type":"SOL_KERNEL_ONLY","kernel_key":{"lengths":[ 160,0 ],"precision":"single","scheme":"CS_KERNEL_STOCKHAM_BLOCK_CC","sbrc_trans":"NONE","kernelConfig":{"use_3steps":false,"half_lds":false,"dir_reg":true,"buffer_inst":false,"tpb":25,"wgs":250,"tpt":[ 10,0 ],"factors":[ 2,4,4,5 ],"ebtype":"NONE","direction":-1,"static_dim":3,"placement":"IP","iAryType":"CI","oAryType":"CI"}}} ,{"sol_node_type":"SOL_KERNEL_ONLY","kernel_key":{"lengths":[ 160,0 ],"precision":"single","scheme":"CS_KERNEL_STOCKHAM_BLOCK_CC","sbrc_trans":"NONE","kernelConfig":{"use_3steps":false,"half_lds":false,"dir_reg":true,"buffer_inst":false,"tpb":20,"wgs":200,"tpt":[ 10,0 ],"factors":[ 2,4,4,5 ],"ebtype":"NONE","direction":1,"static_dim":3,"placement":"IP","iAryType":"CI","oAryType":"CI"}}} ,{"sol_node_type":"SOL_KERNEL_ONLY","kernel_key":{"lengths":[ 160,0 ],"precision":"single","scheme":"CS_KERNEL_STOCKHAM_BLOCK_CC","sbrc_trans":"NONE","kernelConfig":{"use_3steps":false,"half_lds":true,"dir_reg":true,"buffer_inst":true,"tpb":20,"wgs":200,"tpt":[ 10,0 ],"factors":[ 2,4,4,5 ],"ebtype":"NONE","direction":1,"static_dim":2,"placement":"IP","iAryType":"CI","oAryType":"CI"}}} ,{"sol_node_type":"SOL_KERNEL_ONLY","kernel_key":{"lengths":[ 160,0 ],"precision":"single","scheme":"CS_KERNEL_STOCKHAM_BLOCK_CC","sbrc_trans":"NONE","kernelConfig":{"use_3steps":false,"half_lds":false,"dir_reg":true,"buffer_inst":false,"tpb":19,"wgs":190,"tpt":[ 10,0 ],"factors":[ 2,8,10 ],"ebtype":"NONE","direction":-1,"static_dim":3,"placement":"IP","iAryType":"CI","oAryType":"HI"}}} ]}, {"Problem":{"arch":"gfx90a","token":"kernel_len168_double_sbcc"}, "Solutions":[ {"sol_node_type":"SOL_KERNEL_ONLY","kernel_key":{"lengths":[ 168,0 ],"precision":"double","scheme":"CS_KERNEL_STOCKHAM_BLOCK_CC","sbrc_trans":"NONE","kernelConfig":{"use_3steps":true,"half_lds":true,"dir_reg":true,"buffer_inst":true,"tpb":12,"wgs":252,"tpt":[ 21,0 ],"factors":[ 7,8,3 ],"ebtype":"NONE","direction":-1,"static_dim":0,"placement":"NA","iAryType":"CI","oAryType":"CI"}}} ,{"sol_node_type":"SOL_KERNEL_ONLY","kernel_key":{"lengths":[ 168,0 ],"precision":"double","scheme":"CS_KERNEL_STOCKHAM_BLOCK_CC","sbrc_trans":"NONE","kernelConfig":{"use_3steps":true,"half_lds":true,"dir_reg":true,"buffer_inst":true,"tpb":12,"wgs":168,"tpt":[ 14,0 ],"factors":[ 7,6,2,2 ],"ebtype":"NONE","direction":-1,"static_dim":0,"placement":"NA","iAryType":"CI","oAryType":"CI"}}} ,{"sol_node_type":"SOL_KERNEL_ONLY","kernel_key":{"lengths":[ 168,0 ],"precision":"double","scheme":"CS_KERNEL_STOCKHAM_BLOCK_CC","sbrc_trans":"NONE","kernelConfig":{"use_3steps":true,"half_lds":true,"dir_reg":true,"buffer_inst":true,"tpb":12,"wgs":168,"tpt":[ 14,0 ],"factors":[ 2,7,6,2 ],"ebtype":"NONE","direction":-1,"static_dim":0,"placement":"NA","iAryType":"CI","oAryType":"CI"}}} ]}, {"Problem":{"arch":"gfx90a","token":"kernel_len168_single_sbcc"}, "Solutions":[ {"sol_node_type":"SOL_KERNEL_ONLY","kernel_key":{"lengths":[ 168,0 ],"precision":"single","scheme":"CS_KERNEL_STOCKHAM_BLOCK_CC","sbrc_trans":"NONE","kernelConfig":{"use_3steps":false,"half_lds":false,"dir_reg":true,"buffer_inst":false,"tpb":18,"wgs":252,"tpt":[ 14,0 ],"factors":[ 7,4,3,2 ],"ebtype":"NONE","direction":-1,"static_dim":2,"placement":"IP","iAryType":"CI","oAryType":"HI"}}} ,{"sol_node_type":"SOL_KERNEL_ONLY","kernel_key":{"lengths":[ 168,0 ],"precision":"single","scheme":"CS_KERNEL_STOCKHAM_BLOCK_CC","sbrc_trans":"NONE","kernelConfig":{"use_3steps":false,"half_lds":false,"dir_reg":true,"buffer_inst":true,"tpb":10,"wgs":140,"tpt":[ 14,0 ],"factors":[ 2,3,4,7 ],"ebtype":"NONE","direction":1,"static_dim":2,"placement":"IP","iAryType":"CI","oAryType":"CI"}}} ,{"sol_node_type":"SOL_KERNEL_ONLY","kernel_key":{"lengths":[ 168,0 ],"precision":"single","scheme":"CS_KERNEL_STOCKHAM_BLOCK_CC","sbrc_trans":"NONE","kernelConfig":{"use_3steps":false,"half_lds":false,"dir_reg":true,"buffer_inst":false,"tpb":14,"wgs":196,"tpt":[ 14,0 ],"factors":[ 2,2,6,7 ],"ebtype":"NONE","direction":-1,"static_dim":2,"placement":"IP","iAryType":"CI","oAryType":"HI"}}} ,{"sol_node_type":"SOL_KERNEL_ONLY","kernel_key":{"lengths":[ 168,0 ],"precision":"single","scheme":"CS_KERNEL_STOCKHAM_BLOCK_CC","sbrc_trans":"NONE","kernelConfig":{"use_3steps":false,"half_lds":false,"dir_reg":true,"buffer_inst":false,"tpb":10,"wgs":140,"tpt":[ 14,0 ],"factors":[ 2,6,7,2 ],"ebtype":"NONE","direction":1,"static_dim":2,"placement":"IP","iAryType":"CI","oAryType":"CI"}}} ,{"sol_node_type":"SOL_KERNEL_ONLY","kernel_key":{"lengths":[ 168,0 ],"precision":"single","scheme":"CS_KERNEL_STOCKHAM_BLOCK_CC","sbrc_trans":"NONE","kernelConfig":{"use_3steps":false,"half_lds":false,"dir_reg":true,"buffer_inst":false,"tpb":14,"wgs":196,"tpt":[ 14,0 ],"factors":[ 2,3,4,7 ],"ebtype":"NONE","direction":-1,"static_dim":3,"placement":"IP","iAryType":"CI","oAryType":"CI"}}} ,{"sol_node_type":"SOL_KERNEL_ONLY","kernel_key":{"lengths":[ 168,0 ],"precision":"single","scheme":"CS_KERNEL_STOCKHAM_BLOCK_CC","sbrc_trans":"NONE","kernelConfig":{"use_3steps":false,"half_lds":false,"dir_reg":true,"buffer_inst":true,"tpb":14,"wgs":196,"tpt":[ 14,0 ],"factors":[ 7,6,2,2 ],"ebtype":"NONE","direction":-1,"static_dim":2,"placement":"IP","iAryType":"CI","oAryType":"HI"}}} ,{"sol_node_type":"SOL_KERNEL_ONLY","kernel_key":{"lengths":[ 168,0 ],"precision":"single","scheme":"CS_KERNEL_STOCKHAM_BLOCK_CC","sbrc_trans":"NONE","kernelConfig":{"use_3steps":false,"half_lds":false,"dir_reg":true,"buffer_inst":false,"tpb":24,"wgs":192,"tpt":[ 8,0 ],"factors":[ 3,7,8 ],"ebtype":"NONE","direction":1,"static_dim":2,"placement":"IP","iAryType":"CI","oAryType":"CI"}}} ,{"sol_node_type":"SOL_KERNEL_ONLY","kernel_key":{"lengths":[ 168,0 ],"precision":"single","scheme":"CS_KERNEL_STOCKHAM_BLOCK_CC","sbrc_trans":"NONE","kernelConfig":{"use_3steps":false,"half_lds":false,"dir_reg":true,"buffer_inst":true,"tpb":14,"wgs":196,"tpt":[ 14,0 ],"factors":[ 3,4,7,2 ],"ebtype":"NONE","direction":1,"static_dim":3,"placement":"IP","iAryType":"CI","oAryType":"CI"}}} ,{"sol_node_type":"SOL_KERNEL_ONLY","kernel_key":{"lengths":[ 168,0 ],"precision":"single","scheme":"CS_KERNEL_STOCKHAM_BLOCK_CC","sbrc_trans":"NONE","kernelConfig":{"use_3steps":false,"half_lds":false,"dir_reg":true,"buffer_inst":true,"tpb":18,"wgs":252,"tpt":[ 14,0 ],"factors":[ 7,4,3,2 ],"ebtype":"NONE","direction":-1,"static_dim":3,"placement":"IP","iAryType":"CI","oAryType":"HI"}}} ,{"sol_node_type":"SOL_KERNEL_ONLY","kernel_key":{"lengths":[ 168,0 ],"precision":"single","scheme":"CS_KERNEL_STOCKHAM_BLOCK_CC","sbrc_trans":"NONE","kernelConfig":{"use_3steps":false,"half_lds":false,"dir_reg":true,"buffer_inst":false,"tpb":18,"wgs":252,"tpt":[ 14,0 ],"factors":[ 2,3,4,7 ],"ebtype":"NONE","direction":1,"static_dim":2,"placement":"IP","iAryType":"CI","oAryType":"CI"}}} ]}, {"Problem":{"arch":"gfx90a","token":"kernel_len192_single_sbcc"}, "Solutions":[ {"sol_node_type":"SOL_KERNEL_ONLY","kernel_key":{"lengths":[ 192,0 ],"precision":"single","scheme":"CS_KERNEL_STOCKHAM_BLOCK_CC","sbrc_trans":"NONE","kernelConfig":{"use_3steps":false,"half_lds":false,"dir_reg":true,"buffer_inst":true,"tpb":16,"wgs":256,"tpt":[ 16,0 ],"factors":[ 4,3,16 ],"ebtype":"NONE","direction":-1,"static_dim":3,"placement":"IP","iAryType":"CI","oAryType":"CI"}}} ,{"sol_node_type":"SOL_KERNEL_ONLY","kernel_key":{"lengths":[ 192,0 ],"precision":"single","scheme":"CS_KERNEL_STOCKHAM_BLOCK_CC","sbrc_trans":"NONE","kernelConfig":{"use_3steps":false,"half_lds":false,"dir_reg":true,"buffer_inst":false,"tpb":16,"wgs":256,"tpt":[ 16,0 ],"factors":[ 4,2,2,3,4 ],"ebtype":"NONE","direction":-1,"static_dim":2,"placement":"IP","iAryType":"CI","oAryType":"HI"}}} ,{"sol_node_type":"SOL_KERNEL_ONLY","kernel_key":{"lengths":[ 192,0 ],"precision":"single","scheme":"CS_KERNEL_STOCKHAM_BLOCK_CC","sbrc_trans":"NONE","kernelConfig":{"use_3steps":false,"half_lds":false,"dir_reg":true,"buffer_inst":true,"tpb":16,"wgs":256,"tpt":[ 16,0 ],"factors":[ 2,6,16 ],"ebtype":"NONE","direction":1,"static_dim":3,"placement":"IP","iAryType":"CI","oAryType":"CI"}}} ,{"sol_node_type":"SOL_KERNEL_ONLY","kernel_key":{"lengths":[ 192,0 ],"precision":"single","scheme":"CS_KERNEL_STOCKHAM_BLOCK_CC","sbrc_trans":"NONE","kernelConfig":{"use_3steps":false,"half_lds":false,"dir_reg":true,"buffer_inst":true,"tpb":16,"wgs":256,"tpt":[ 16,0 ],"factors":[ 2,2,6,8 ],"ebtype":"NONE","direction":1,"static_dim":2,"placement":"IP","iAryType":"CI","oAryType":"CI"}}} ,{"sol_node_type":"SOL_KERNEL_ONLY","kernel_key":{"lengths":[ 192,0 ],"precision":"single","scheme":"CS_KERNEL_STOCKHAM_BLOCK_CC","sbrc_trans":"NONE","kernelConfig":{"use_3steps":false,"half_lds":false,"dir_reg":true,"buffer_inst":false,"tpb":16,"wgs":192,"tpt":[ 12,0 ],"factors":[ 8,2,3,4 ],"ebtype":"NONE","direction":-1,"static_dim":2,"placement":"IP","iAryType":"CI","oAryType":"HI"}}} ,{"sol_node_type":"SOL_KERNEL_ONLY","kernel_key":{"lengths":[ 192,0 ],"precision":"single","scheme":"CS_KERNEL_STOCKHAM_BLOCK_CC","sbrc_trans":"NONE","kernelConfig":{"use_3steps":false,"half_lds":false,"dir_reg":true,"buffer_inst":false,"tpb":21,"wgs":252,"tpt":[ 12,0 ],"factors":[ 4,4,2,2,3 ],"ebtype":"NONE","direction":1,"static_dim":3,"placement":"IP","iAryType":"CI","oAryType":"CI"}}} ,{"sol_node_type":"SOL_KERNEL_ONLY","kernel_key":{"lengths":[ 192,0 ],"precision":"single","scheme":"CS_KERNEL_STOCKHAM_BLOCK_CC","sbrc_trans":"NONE","kernelConfig":{"use_3steps":false,"half_lds":false,"dir_reg":true,"buffer_inst":false,"tpb":16,"wgs":256,"tpt":[ 16,0 ],"factors":[ 2,2,3,4,4 ],"ebtype":"NONE","direction":1,"static_dim":2,"placement":"IP","iAryType":"CI","oAryType":"CI"}}} ,{"sol_node_type":"SOL_KERNEL_ONLY","kernel_key":{"lengths":[ 192,0 ],"precision":"single","scheme":"CS_KERNEL_STOCKHAM_BLOCK_CC","sbrc_trans":"NONE","kernelConfig":{"use_3steps":false,"half_lds":false,"dir_reg":true,"buffer_inst":true,"tpb":16,"wgs":192,"tpt":[ 12,0 ],"factors":[ 16,4,3 ],"ebtype":"NONE","direction":-1,"static_dim":3,"placement":"IP","iAryType":"CI","oAryType":"HI"}}} ,{"sol_node_type":"SOL_KERNEL_ONLY","kernel_key":{"lengths":[ 192,0 ],"precision":"single","scheme":"CS_KERNEL_STOCKHAM_BLOCK_CC","sbrc_trans":"NONE","kernelConfig":{"use_3steps":false,"half_lds":true,"dir_reg":true,"buffer_inst":true,"tpb":16,"wgs":256,"tpt":[ 16,0 ],"factors":[ 4,4,3,4 ],"ebtype":"NONE","direction":1,"static_dim":2,"placement":"IP","iAryType":"CI","oAryType":"CI"}}} ,{"sol_node_type":"SOL_KERNEL_ONLY","kernel_key":{"lengths":[ 192,0 ],"precision":"single","scheme":"CS_KERNEL_STOCKHAM_BLOCK_CC","sbrc_trans":"NONE","kernelConfig":{"use_3steps":false,"half_lds":false,"dir_reg":true,"buffer_inst":false,"tpb":16,"wgs":192,"tpt":[ 12,0 ],"factors":[ 4,4,6,2 ],"ebtype":"NONE","direction":-1,"static_dim":3,"placement":"IP","iAryType":"CI","oAryType":"HI"}}} ,{"sol_node_type":"SOL_KERNEL_ONLY","kernel_key":{"lengths":[ 192,0 ],"precision":"single","scheme":"CS_KERNEL_STOCKHAM_BLOCK_CC","sbrc_trans":"NONE","kernelConfig":{"use_3steps":false,"half_lds":true,"dir_reg":true,"buffer_inst":true,"tpb":16,"wgs":256,"tpt":[ 16,0 ],"factors":[ 2,2,3,4,4 ],"ebtype":"NONE","direction":1,"static_dim":2,"placement":"IP","iAryType":"CI","oAryType":"CI"}}} ]}, {"Problem":{"arch":"gfx90a","token":"kernel_len200_double_sbcc"}, "Solutions":[ {"sol_node_type":"SOL_KERNEL_ONLY","kernel_key":{"lengths":[ 200,0 ],"precision":"double","scheme":"CS_KERNEL_STOCKHAM_BLOCK_CC","sbrc_trans":"NONE","kernelConfig":{"use_3steps":false,"half_lds":false,"dir_reg":true,"buffer_inst":false,"tpb":10,"wgs":200,"tpt":[ 20,0 ],"factors":[ 10,2,10 ],"ebtype":"NONE","direction":-1,"static_dim":3,"placement":"IP","iAryType":"CI","oAryType":"CI"}}} ]}, {"Problem":{"arch":"gfx90a","token":"kernel_len200_single_sbcc"}, "Solutions":[ {"sol_node_type":"SOL_KERNEL_ONLY","kernel_key":{"lengths":[ 200,0 ],"precision":"single","scheme":"CS_KERNEL_STOCKHAM_BLOCK_CC","sbrc_trans":"NONE","kernelConfig":{"use_3steps":false,"half_lds":false,"dir_reg":true,"buffer_inst":true,"tpb":12,"wgs":240,"tpt":[ 20,0 ],"factors":[ 2,10,5,2 ],"ebtype":"NONE","direction":-1,"static_dim":2,"placement":"IP","iAryType":"CI","oAryType":"HI"}}} ,{"sol_node_type":"SOL_KERNEL_ONLY","kernel_key":{"lengths":[ 200,0 ],"precision":"single","scheme":"CS_KERNEL_STOCKHAM_BLOCK_CC","sbrc_trans":"NONE","kernelConfig":{"use_3steps":false,"half_lds":false,"dir_reg":true,"buffer_inst":true,"tpb":10,"wgs":200,"tpt":[ 20,0 ],"factors":[ 2,5,10,2 ],"ebtype":"NONE","direction":1,"static_dim":2,"placement":"IP","iAryType":"CI","oAryType":"CI"}}} ,{"sol_node_type":"SOL_KERNEL_ONLY","kernel_key":{"lengths":[ 200,0 ],"precision":"single","scheme":"CS_KERNEL_STOCKHAM_BLOCK_CC","sbrc_trans":"NONE","kernelConfig":{"use_3steps":false,"half_lds":false,"dir_reg":true,"buffer_inst":false,"tpb":12,"wgs":240,"tpt":[ 20,0 ],"factors":[ 2,2,5,10 ],"ebtype":"NONE","direction":-1,"static_dim":3,"placement":"IP","iAryType":"CI","oAryType":"CI"}}} ,{"sol_node_type":"SOL_KERNEL_ONLY","kernel_key":{"lengths":[ 200,0 ],"precision":"single","scheme":"CS_KERNEL_STOCKHAM_BLOCK_CC","sbrc_trans":"NONE","kernelConfig":{"use_3steps":false,"half_lds":false,"dir_reg":true,"buffer_inst":false,"tpb":12,"wgs":240,"tpt":[ 20,0 ],"factors":[ 2,10,10 ],"ebtype":"NONE","direction":1,"static_dim":3,"placement":"IP","iAryType":"CI","oAryType":"CI"}}} ]}, {"Problem":{"arch":"gfx90a","token":"kernel_len208_double_sbcc"}, "Solutions":[ {"sol_node_type":"SOL_KERNEL_ONLY","kernel_key":{"lengths":[ 208,0 ],"precision":"double","scheme":"CS_KERNEL_STOCKHAM_BLOCK_CC","sbrc_trans":"NONE","kernelConfig":{"use_3steps":false,"half_lds":false,"dir_reg":true,"buffer_inst":false,"tpb":9,"wgs":234,"tpt":[ 26,0 ],"factors":[ 13,8,2 ],"ebtype":"NONE","direction":-1,"static_dim":3,"placement":"IP","iAryType":"CI","oAryType":"CI"}}} ,{"sol_node_type":"SOL_KERNEL_ONLY","kernel_key":{"lengths":[ 208,0 ],"precision":"double","scheme":"CS_KERNEL_STOCKHAM_BLOCK_CC","sbrc_trans":"NONE","kernelConfig":{"use_3steps":false,"half_lds":false,"dir_reg":true,"buffer_inst":true,"tpb":9,"wgs":234,"tpt":[ 26,0 ],"factors":[ 13,8,2 ],"ebtype":"NONE","direction":1,"static_dim":3,"placement":"IP","iAryType":"HI","oAryType":"CI"}}} ]}, {"Problem":{"arch":"gfx90a","token":"kernel_len208_single_sbcc"}, "Solutions":[ {"sol_node_type":"SOL_KERNEL_ONLY","kernel_key":{"lengths":[ 208,0 ],"precision":"single","scheme":"CS_KERNEL_STOCKHAM_BLOCK_CC","sbrc_trans":"NONE","kernelConfig":{"use_3steps":false,"half_lds":false,"dir_reg":true,"buffer_inst":false,"tpb":19,"wgs":247,"tpt":[ 13,0 ],"factors":[ 2,2,4,13 ],"ebtype":"NONE","direction":-1,"static_dim":3,"placement":"IP","iAryType":"CI","oAryType":"CI"}}} ,{"sol_node_type":"SOL_KERNEL_ONLY","kernel_key":{"lengths":[ 208,0 ],"precision":"single","scheme":"CS_KERNEL_STOCKHAM_BLOCK_CC","sbrc_trans":"NONE","kernelConfig":{"use_3steps":false,"half_lds":false,"dir_reg":true,"buffer_inst":true,"tpb":19,"wgs":247,"tpt":[ 13,0 ],"factors":[ 2,2,4,13 ],"ebtype":"NONE","direction":1,"static_dim":3,"placement":"IP","iAryType":"HI","oAryType":"CI"}}} ,{"sol_node_type":"SOL_KERNEL_ONLY","kernel_key":{"lengths":[ 208,0 ],"precision":"single","scheme":"CS_KERNEL_STOCKHAM_BLOCK_CC","sbrc_trans":"NONE","kernelConfig":{"use_3steps":false,"half_lds":false,"dir_reg":true,"buffer_inst":true,"tpb":19,"wgs":247,"tpt":[ 13,0 ],"factors":[ 2,8,13 ],"ebtype":"NONE","direction":1,"static_dim":3,"placement":"IP","iAryType":"CI","oAryType":"CI"}}} ]}, {"Problem":{"arch":"gfx90a","token":"kernel_len216_single_sbcc"}, "Solutions":[ {"sol_node_type":"SOL_KERNEL_ONLY","kernel_key":{"lengths":[ 216,0 ],"precision":"single","scheme":"CS_KERNEL_STOCKHAM_BLOCK_CC","sbrc_trans":"NONE","kernelConfig":{"use_3steps":false,"half_lds":false,"dir_reg":true,"buffer_inst":false,"tpb":8,"wgs":144,"tpt":[ 18,0 ],"factors":[ 2,2,3,3,6 ],"ebtype":"NONE","direction":-1,"static_dim":3,"placement":"IP","iAryType":"CI","oAryType":"CI"}}} ,{"sol_node_type":"SOL_KERNEL_ONLY","kernel_key":{"lengths":[ 216,0 ],"precision":"single","scheme":"CS_KERNEL_STOCKHAM_BLOCK_CC","sbrc_trans":"NONE","kernelConfig":{"use_3steps":false,"half_lds":false,"dir_reg":true,"buffer_inst":false,"tpb":8,"wgs":144,"tpt":[ 18,0 ],"factors":[ 2,6,9,2 ],"ebtype":"NONE","direction":1,"static_dim":3,"placement":"IP","iAryType":"CI","oAryType":"CI"}}} ,{"sol_node_type":"SOL_KERNEL_ONLY","kernel_key":{"lengths":[ 216,0 ],"precision":"single","scheme":"CS_KERNEL_STOCKHAM_BLOCK_CC","sbrc_trans":"NONE","kernelConfig":{"use_3steps":false,"half_lds":false,"dir_reg":true,"buffer_inst":true,"tpb":14,"wgs":252,"tpt":[ 18,0 ],"factors":[ 9,6,2,2 ],"ebtype":"NONE","direction":-1,"static_dim":3,"placement":"IP","iAryType":"CI","oAryType":"CI"}}} ,{"sol_node_type":"SOL_KERNEL_ONLY","kernel_key":{"lengths":[ 216,0 ],"precision":"single","scheme":"CS_KERNEL_STOCKHAM_BLOCK_CC","sbrc_trans":"NONE","kernelConfig":{"use_3steps":false,"half_lds":false,"dir_reg":true,"buffer_inst":true,"tpb":14,"wgs":252,"tpt":[ 18,0 ],"factors":[ 3,2,6,6 ],"ebtype":"NONE","direction":-1,"static_dim":2,"placement":"IP","iAryType":"CI","oAryType":"HI"}}} ,{"sol_node_type":"SOL_KERNEL_ONLY","kernel_key":{"lengths":[ 216,0 ],"precision":"single","scheme":"CS_KERNEL_STOCKHAM_BLOCK_CC","sbrc_trans":"NONE","kernelConfig":{"use_3steps":false,"half_lds":false,"dir_reg":true,"buffer_inst":true,"tpb":14,"wgs":252,"tpt":[ 18,0 ],"factors":[ 9,4,3,2 ],"ebtype":"NONE","direction":1,"static_dim":3,"placement":"IP","iAryType":"CI","oAryType":"CI"}}} ,{"sol_node_type":"SOL_KERNEL_ONLY","kernel_key":{"lengths":[ 216,0 ],"precision":"single","scheme":"CS_KERNEL_STOCKHAM_BLOCK_CC","sbrc_trans":"NONE","kernelConfig":{"use_3steps":false,"half_lds":false,"dir_reg":true,"buffer_inst":false,"tpb":14,"wgs":252,"tpt":[ 18,0 ],"factors":[ 6,4,3,3 ],"ebtype":"NONE","direction":1,"static_dim":2,"placement":"IP","iAryType":"CI","oAryType":"CI"}}} ]}, {"Problem":{"arch":"gfx90a","token":"kernel_len224_double_sbcc"}, "Solutions":[ {"sol_node_type":"SOL_KERNEL_ONLY","kernel_key":{"lengths":[ 224,0 ],"precision":"double","scheme":"CS_KERNEL_STOCKHAM_BLOCK_CC","sbrc_trans":"NONE","kernelConfig":{"use_3steps":false,"half_lds":false,"dir_reg":true,"buffer_inst":false,"tpb":9,"wgs":252,"tpt":[ 28,0 ],"factors":[ 7,2,4,4 ],"ebtype":"NONE","direction":-1,"static_dim":0,"placement":"NA","iAryType":"CI","oAryType":"CI"}}} ]}, {"Problem":{"arch":"gfx90a","token":"kernel_len224_single_sbcc"}, "Solutions":[ {"sol_node_type":"SOL_KERNEL_ONLY","kernel_key":{"lengths":[ 224,0 ],"precision":"single","scheme":"CS_KERNEL_STOCKHAM_BLOCK_CC","sbrc_trans":"NONE","kernelConfig":{"use_3steps":false,"half_lds":false,"dir_reg":true,"buffer_inst":true,"tpb":18,"wgs":252,"tpt":[ 14,0 ],"factors":[ 2,4,4,7 ],"ebtype":"NONE","direction":-1,"static_dim":3,"placement":"IP","iAryType":"CI","oAryType":"CI"}}} ,{"sol_node_type":"SOL_KERNEL_ONLY","kernel_key":{"lengths":[ 224,0 ],"precision":"single","scheme":"CS_KERNEL_STOCKHAM_BLOCK_CC","sbrc_trans":"NONE","kernelConfig":{"use_3steps":false,"half_lds":false,"dir_reg":true,"buffer_inst":false,"tpb":14,"wgs":196,"tpt":[ 14,0 ],"factors":[ 4,7,2,2,2 ],"ebtype":"NONE","direction":-1,"static_dim":3,"placement":"IP","iAryType":"CI","oAryType":"CI"}}} ,{"sol_node_type":"SOL_KERNEL_ONLY","kernel_key":{"lengths":[ 224,0 ],"precision":"single","scheme":"CS_KERNEL_STOCKHAM_BLOCK_CC","sbrc_trans":"NONE","kernelConfig":{"use_3steps":false,"half_lds":false,"dir_reg":true,"buffer_inst":false,"tpb":14,"wgs":196,"tpt":[ 14,0 ],"factors":[ 2,2,2,4,7 ],"ebtype":"NONE","direction":1,"static_dim":3,"placement":"IP","iAryType":"CI","oAryType":"CI"}}} ,{"sol_node_type":"SOL_KERNEL_ONLY","kernel_key":{"lengths":[ 224,0 ],"precision":"single","scheme":"CS_KERNEL_STOCKHAM_BLOCK_CC","sbrc_trans":"NONE","kernelConfig":{"use_3steps":false,"half_lds":false,"dir_reg":true,"buffer_inst":false,"tpb":16,"wgs":256,"tpt":[ 16,0 ],"factors":[ 2,2,7,8 ],"ebtype":"NONE","direction":-1,"static_dim":2,"placement":"IP","iAryType":"CI","oAryType":"HI"}}} ,{"sol_node_type":"SOL_KERNEL_ONLY","kernel_key":{"lengths":[ 224,0 ],"precision":"single","scheme":"CS_KERNEL_STOCKHAM_BLOCK_CC","sbrc_trans":"NONE","kernelConfig":{"use_3steps":false,"half_lds":false,"dir_reg":true,"buffer_inst":false,"tpb":18,"wgs":252,"tpt":[ 14,0 ],"factors":[ 2,4,4,7 ],"ebtype":"NONE","direction":1,"static_dim":2,"placement":"IP","iAryType":"CI","oAryType":"CI"}}} ]}, {"Problem":{"arch":"gfx90a","token":"kernel_len240_single_sbcc"}, "Solutions":[ {"sol_node_type":"SOL_KERNEL_ONLY","kernel_key":{"lengths":[ 240,0 ],"precision":"single","scheme":"CS_KERNEL_STOCKHAM_BLOCK_CC","sbrc_trans":"NONE","kernelConfig":{"use_3steps":false,"half_lds":false,"dir_reg":true,"buffer_inst":true,"tpb":12,"wgs":240,"tpt":[ 20,0 ],"factors":[ 4,5,3,4 ],"ebtype":"NONE","direction":-1,"static_dim":3,"placement":"IP","iAryType":"CI","oAryType":"CI"}}} ,{"sol_node_type":"SOL_KERNEL_ONLY","kernel_key":{"lengths":[ 240,0 ],"precision":"single","scheme":"CS_KERNEL_STOCKHAM_BLOCK_CC","sbrc_trans":"NONE","kernelConfig":{"use_3steps":false,"half_lds":false,"dir_reg":true,"buffer_inst":true,"tpb":12,"wgs":240,"tpt":[ 20,0 ],"factors":[ 4,4,3,5 ],"ebtype":"NONE","direction":1,"static_dim":3,"placement":"IP","iAryType":"CI","oAryType":"CI"}}} ,{"sol_node_type":"SOL_KERNEL_ONLY","kernel_key":{"lengths":[ 240,0 ],"precision":"single","scheme":"CS_KERNEL_STOCKHAM_BLOCK_CC","sbrc_trans":"NONE","kernelConfig":{"use_3steps":false,"half_lds":false,"dir_reg":true,"buffer_inst":false,"tpb":7,"wgs":140,"tpt":[ 20,0 ],"factors":[ 2,3,4,5,2 ],"ebtype":"NONE","direction":-1,"static_dim":3,"placement":"IP","iAryType":"CI","oAryType":"CI"}}} ,{"sol_node_type":"SOL_KERNEL_ONLY","kernel_key":{"lengths":[ 240,0 ],"precision":"single","scheme":"CS_KERNEL_STOCKHAM_BLOCK_CC","sbrc_trans":"NONE","kernelConfig":{"use_3steps":false,"half_lds":false,"dir_reg":true,"buffer_inst":true,"tpb":9,"wgs":180,"tpt":[ 20,0 ],"factors":[ 2,3,10,2,2 ],"ebtype":"NONE","direction":-1,"static_dim":3,"placement":"IP","iAryType":"CI","oAryType":"CI"}}} ,{"sol_node_type":"SOL_KERNEL_ONLY","kernel_key":{"lengths":[ 240,0 ],"precision":"single","scheme":"CS_KERNEL_STOCKHAM_BLOCK_CC","sbrc_trans":"NONE","kernelConfig":{"use_3steps":false,"half_lds":false,"dir_reg":true,"buffer_inst":true,"tpb":9,"wgs":180,"tpt":[ 20,0 ],"factors":[ 2,2,10,3,2 ],"ebtype":"NONE","direction":1,"static_dim":3,"placement":"IP","iAryType":"CI","oAryType":"CI"}}} ,{"sol_node_type":"SOL_KERNEL_ONLY","kernel_key":{"lengths":[ 240,0 ],"precision":"single","scheme":"CS_KERNEL_STOCKHAM_BLOCK_CC","sbrc_trans":"NONE","kernelConfig":{"use_3steps":false,"half_lds":false,"dir_reg":true,"buffer_inst":false,"tpb":12,"wgs":240,"tpt":[ 20,0 ],"factors":[ 5,3,4,4 ],"ebtype":"NONE","direction":-1,"static_dim":3,"placement":"IP","iAryType":"CI","oAryType":"CI"}}} ,{"sol_node_type":"SOL_KERNEL_ONLY","kernel_key":{"lengths":[ 240,0 ],"precision":"single","scheme":"CS_KERNEL_STOCKHAM_BLOCK_CC","sbrc_trans":"NONE","kernelConfig":{"use_3steps":false,"half_lds":false,"dir_reg":true,"buffer_inst":false,"tpb":12,"wgs":240,"tpt":[ 20,0 ],"factors":[ 4,5,3,4 ],"ebtype":"NONE","direction":1,"static_dim":3,"placement":"IP","iAryType":"CI","oAryType":"CI"}}} ]}, {"Problem":{"arch":"gfx90a","token":"kernel_len243_double_sbcc"}, "Solutions":[ {"sol_node_type":"SOL_KERNEL_ONLY","kernel_key":{"lengths":[ 243,0 ],"precision":"double","scheme":"CS_KERNEL_STOCKHAM_BLOCK_CC","sbrc_trans":"NONE","kernelConfig":{"use_3steps":true,"half_lds":false,"dir_reg":true,"buffer_inst":false,"tpb":7,"wgs":189,"tpt":[ 27,0 ],"factors":[ 3,9,3,3 ],"ebtype":"NONE","direction":-1,"static_dim":0,"placement":"NA","iAryType":"CI","oAryType":"CI"}}} ,{"sol_node_type":"SOL_KERNEL_ONLY","kernel_key":{"lengths":[ 243,0 ],"precision":"double","scheme":"CS_KERNEL_STOCKHAM_BLOCK_CC","sbrc_trans":"NONE","kernelConfig":{"use_3steps":true,"half_lds":false,"dir_reg":true,"buffer_inst":false,"tpb":7,"wgs":189,"tpt":[ 27,0 ],"factors":[ 9,3,3,3 ],"ebtype":"NONE","direction":-1,"static_dim":0,"placement":"NA","iAryType":"CI","oAryType":"CI"}}} ,{"sol_node_type":"SOL_KERNEL_ONLY","kernel_key":{"lengths":[ 243,0 ],"precision":"double","scheme":"CS_KERNEL_STOCKHAM_BLOCK_CC","sbrc_trans":"NONE","kernelConfig":{"use_3steps":false,"half_lds":false,"dir_reg":true,"buffer_inst":false,"tpb":8,"wgs":216,"tpt":[ 27,0 ],"factors":[ 9,9,3 ],"ebtype":"NONE","direction":-1,"static_dim":0,"placement":"NA","iAryType":"CI","oAryType":"CI"}}} ]}, {"Problem":{"arch":"gfx90a","token":"kernel_len243_single_sbcc"}, "Solutions":[ {"sol_node_type":"SOL_KERNEL_ONLY","kernel_key":{"lengths":[ 243,0 ],"precision":"single","scheme":"CS_KERNEL_STOCKHAM_BLOCK_CC","sbrc_trans":"NONE","kernelConfig":{"use_3steps":false,"half_lds":false,"dir_reg":true,"buffer_inst":false,"tpb":16,"wgs":432,"tpt":[ 27,0 ],"factors":[ 3,3,9,3 ],"ebtype":"NONE","direction":-1,"static_dim":0,"placement":"NA","iAryType":"CI","oAryType":"CI"}}} ]}, {"Problem":{"arch":"gfx90a","token":"kernel_len280_single_sbcc"}, "Solutions":[ {"sol_node_type":"SOL_KERNEL_ONLY","kernel_key":{"lengths":[ 280,0 ],"precision":"single","scheme":"CS_KERNEL_STOCKHAM_BLOCK_CC","sbrc_trans":"NONE","kernelConfig":{"use_3steps":false,"half_lds":false,"dir_reg":true,"buffer_inst":true,"tpb":9,"wgs":252,"tpt":[ 28,0 ],"factors":[ 2,7,10,2 ],"ebtype":"NONE","direction":-1,"static_dim":3,"placement":"IP","iAryType":"CI","oAryType":"CI"}}} ,{"sol_node_type":"SOL_KERNEL_ONLY","kernel_key":{"lengths":[ 280,0 ],"precision":"single","scheme":"CS_KERNEL_STOCKHAM_BLOCK_CC","sbrc_trans":"NONE","kernelConfig":{"use_3steps":false,"half_lds":false,"dir_reg":true,"buffer_inst":true,"tpb":9,"wgs":252,"tpt":[ 28,0 ],"factors":[ 2,2,7,5,2 ],"ebtype":"NONE","direction":1,"static_dim":3,"placement":"IP","iAryType":"CI","oAryType":"CI"}}} ]}, {"Problem":{"arch":"gfx90a","token":"kernel_len336_double_sbcc"}, "Solutions":[ {"sol_node_type":"SOL_KERNEL_ONLY","kernel_key":{"lengths":[ 336,0 ],"precision":"double","scheme":"CS_KERNEL_STOCKHAM_BLOCK_CC","sbrc_trans":"NONE","kernelConfig":{"use_3steps":false,"half_lds":false,"dir_reg":true,"buffer_inst":false,"tpb":6,"wgs":168,"tpt":[ 28,0 ],"factors":[ 4,7,3,4 ],"ebtype":"NONE","direction":-1,"static_dim":0,"placement":"NA","iAryType":"CI","oAryType":"CI"}}} ,{"sol_node_type":"SOL_KERNEL_ONLY","kernel_key":{"lengths":[ 336,0 ],"precision":"double","scheme":"CS_KERNEL_STOCKHAM_BLOCK_CC","sbrc_trans":"NONE","kernelConfig":{"use_3steps":false,"half_lds":true,"dir_reg":true,"buffer_inst":false,"tpb":6,"wgs":126,"tpt":[ 21,0 ],"factors":[ 7,8,2,3 ],"ebtype":"NONE","direction":-1,"static_dim":0,"placement":"NA","iAryType":"CI","oAryType":"CI"}}} ,{"sol_node_type":"SOL_KERNEL_ONLY","kernel_key":{"lengths":[ 336,0 ],"precision":"double","scheme":"CS_KERNEL_STOCKHAM_BLOCK_CC","sbrc_trans":"NONE","kernelConfig":{"use_3steps":false,"half_lds":false,"dir_reg":true,"buffer_inst":true,"tpb":4,"wgs":112,"tpt":[ 28,0 ],"factors":[ 2,7,6,4 ],"ebtype":"NONE","direction":-1,"static_dim":0,"placement":"NA","iAryType":"CI","oAryType":"CI"}}} ,{"sol_node_type":"SOL_KERNEL_ONLY","kernel_key":{"lengths":[ 336,0 ],"precision":"double","scheme":"CS_KERNEL_STOCKHAM_BLOCK_CC","sbrc_trans":"NONE","kernelConfig":{"use_3steps":false,"half_lds":true,"dir_reg":true,"buffer_inst":false,"tpb":6,"wgs":252,"tpt":[ 42,0 ],"factors":[ 3,7,8,2 ],"ebtype":"NONE","direction":-1,"static_dim":0,"placement":"NA","iAryType":"CI","oAryType":"CI"}}} ,{"sol_node_type":"SOL_KERNEL_ONLY","kernel_key":{"lengths":[ 336,0 ],"precision":"double","scheme":"CS_KERNEL_STOCKHAM_BLOCK_CC","sbrc_trans":"NONE","kernelConfig":{"use_3steps":false,"half_lds":false,"dir_reg":true,"buffer_inst":false,"tpb":6,"wgs":168,"tpt":[ 28,0 ],"factors":[ 7,3,4,4 ],"ebtype":"NONE","direction":-1,"static_dim":0,"placement":"NA","iAryType":"CI","oAryType":"CI"}}} ,{"sol_node_type":"SOL_KERNEL_ONLY","kernel_key":{"lengths":[ 336,0 ],"precision":"double","scheme":"CS_KERNEL_STOCKHAM_BLOCK_CC","sbrc_trans":"NONE","kernelConfig":{"use_3steps":false,"half_lds":false,"dir_reg":true,"buffer_inst":false,"tpb":4,"wgs":168,"tpt":[ 42,0 ],"factors":[ 3,7,8,2 ],"ebtype":"NONE","direction":-1,"static_dim":0,"placement":"NA","iAryType":"CI","oAryType":"CI"}}} ,{"sol_node_type":"SOL_KERNEL_ONLY","kernel_key":{"lengths":[ 336,0 ],"precision":"double","scheme":"CS_KERNEL_STOCKHAM_BLOCK_CC","sbrc_trans":"NONE","kernelConfig":{"use_3steps":false,"half_lds":true,"dir_reg":true,"buffer_inst":true,"tpb":6,"wgs":252,"tpt":[ 42,0 ],"factors":[ 7,8,2,3 ],"ebtype":"NONE","direction":-1,"static_dim":0,"placement":"NA","iAryType":"CI","oAryType":"CI"}}} ,{"sol_node_type":"SOL_KERNEL_ONLY","kernel_key":{"lengths":[ 336,0 ],"precision":"double","scheme":"CS_KERNEL_STOCKHAM_BLOCK_CC","sbrc_trans":"NONE","kernelConfig":{"use_3steps":false,"half_lds":false,"dir_reg":true,"buffer_inst":true,"tpb":6,"wgs":126,"tpt":[ 21,0 ],"factors":[ 7,16,3 ],"ebtype":"NONE","direction":-1,"static_dim":0,"placement":"NA","iAryType":"CI","oAryType":"CI"}}} ]}, {"Problem":{"arch":"gfx90a","token":"kernel_len343_double_sbcc"}, "Solutions":[ {"sol_node_type":"SOL_KERNEL_ONLY","kernel_key":{"lengths":[ 343,0 ],"precision":"double","scheme":"CS_KERNEL_STOCKHAM_BLOCK_CC","sbrc_trans":"NONE","kernelConfig":{"use_3steps":true,"half_lds":false,"dir_reg":true,"buffer_inst":true,"tpb":4,"wgs":196,"tpt":[ 49,0 ],"factors":[ 7,7,7 ],"ebtype":"NONE","direction":-1,"static_dim":0,"placement":"NA","iAryType":"CI","oAryType":"CI"}}} ,{"sol_node_type":"SOL_KERNEL_ONLY","kernel_key":{"lengths":[ 343,0 ],"precision":"double","scheme":"CS_KERNEL_STOCKHAM_BLOCK_CC","sbrc_trans":"NONE","kernelConfig":{"use_3steps":true,"half_lds":false,"dir_reg":true,"buffer_inst":false,"tpb":4,"wgs":196,"tpt":[ 49,0 ],"factors":[ 7,7,7 ],"ebtype":"NONE","direction":1,"static_dim":2,"placement":"IP","iAryType":"CI","oAryType":"CI"}}} ]}, {"Problem":{"arch":"gfx90a","token":"kernel_len60_single_sbcc"}, "Solutions":[ {"sol_node_type":"SOL_KERNEL_ONLY","kernel_key":{"lengths":[ 60,0 ],"precision":"single","scheme":"CS_KERNEL_STOCKHAM_BLOCK_CC","sbrc_trans":"NONE","kernelConfig":{"use_3steps":false,"half_lds":true,"dir_reg":true,"buffer_inst":true,"tpb":11,"wgs":132,"tpt":[ 12,0 ],"factors":[ 5,2,6 ],"ebtype":"NONE","direction":-1,"static_dim":3,"placement":"IP","iAryType":"CI","oAryType":"HI"}}} ,{"sol_node_type":"SOL_KERNEL_ONLY","kernel_key":{"lengths":[ 60,0 ],"precision":"single","scheme":"CS_KERNEL_STOCKHAM_BLOCK_CC","sbrc_trans":"NONE","kernelConfig":{"use_3steps":false,"half_lds":true,"dir_reg":true,"buffer_inst":true,"tpb":38,"wgs":190,"tpt":[ 5,0 ],"factors":[ 3,4,5 ],"ebtype":"NONE","direction":1,"static_dim":2,"placement":"IP","iAryType":"CI","oAryType":"CI"}}} ]}, {"Problem":{"arch":"gfx90a","token":"kernel_len64_double_sbcc"}, "Solutions":[ {"sol_node_type":"SOL_KERNEL_ONLY","kernel_key":{"lengths":[ 64,0 ],"precision":"double","scheme":"CS_KERNEL_STOCKHAM_BLOCK_CC","sbrc_trans":"NONE","kernelConfig":{"use_3steps":true,"half_lds":true,"dir_reg":true,"buffer_inst":false,"tpb":16,"wgs":64,"tpt":[ 4,0 ],"factors":[ 8,2,4 ],"ebtype":"NONE","direction":-1,"static_dim":0,"placement":"NA","iAryType":"CI","oAryType":"CI"}}} ,{"sol_node_type":"SOL_KERNEL_ONLY","kernel_key":{"lengths":[ 64,0 ],"precision":"double","scheme":"CS_KERNEL_STOCKHAM_BLOCK_CC","sbrc_trans":"NONE","kernelConfig":{"use_3steps":true,"half_lds":true,"dir_reg":true,"buffer_inst":true,"tpb":16,"wgs":64,"tpt":[ 4,0 ],"factors":[ 4,4,2,2 ],"ebtype":"NONE","direction":-1,"static_dim":0,"placement":"NA","iAryType":"CI","oAryType":"CI"}}} ,{"sol_node_type":"SOL_KERNEL_ONLY","kernel_key":{"lengths":[ 64,0 ],"precision":"double","scheme":"CS_KERNEL_STOCKHAM_BLOCK_CC","sbrc_trans":"NONE","kernelConfig":{"use_3steps":false,"half_lds":false,"dir_reg":true,"buffer_inst":false,"tpb":16,"wgs":64,"tpt":[ 4,0 ],"factors":[ 8,2,2,2 ],"ebtype":"NONE","direction":-1,"static_dim":2,"placement":"OP","iAryType":"CI","oAryType":"CI"}}} ,{"sol_node_type":"SOL_KERNEL_ONLY","kernel_key":{"lengths":[ 64,0 ],"precision":"double","scheme":"CS_KERNEL_STOCKHAM_BLOCK_CC","sbrc_trans":"NONE","kernelConfig":{"use_3steps":false,"half_lds":false,"dir_reg":true,"buffer_inst":false,"tpb":16,"wgs":64,"tpt":[ 4,0 ],"factors":[ 2,2,8,2 ],"ebtype":"NONE","direction":-1,"static_dim":2,"placement":"OP","iAryType":"CI","oAryType":"CI"}}} ,{"sol_node_type":"SOL_KERNEL_ONLY","kernel_key":{"lengths":[ 64,0 ],"precision":"double","scheme":"CS_KERNEL_STOCKHAM_BLOCK_CC","sbrc_trans":"NONE","kernelConfig":{"use_3steps":false,"half_lds":false,"dir_reg":true,"buffer_inst":true,"tpb":16,"wgs":64,"tpt":[ 4,0 ],"factors":[ 4,4,2,2 ],"ebtype":"NONE","direction":1,"static_dim":2,"placement":"OP","iAryType":"CI","oAryType":"CI"}}} ]}, {"Problem":{"arch":"gfx90a","token":"kernel_len64_single_sbcc"}, "Solutions":[ {"sol_node_type":"SOL_KERNEL_ONLY","kernel_key":{"lengths":[ 64,0 ],"precision":"single","scheme":"CS_KERNEL_STOCKHAM_BLOCK_CC","sbrc_trans":"NONE","kernelConfig":{"use_3steps":false,"half_lds":true,"dir_reg":true,"buffer_inst":false,"tpb":16,"wgs":64,"tpt":[ 4,0 ],"factors":[ 2,2,4,4 ],"ebtype":"NONE","direction":-1,"static_dim":3,"placement":"IP","iAryType":"CI","oAryType":"HI"}}} ,{"sol_node_type":"SOL_KERNEL_ONLY","kernel_key":{"lengths":[ 64,0 ],"precision":"single","scheme":"CS_KERNEL_STOCKHAM_BLOCK_CC","sbrc_trans":"NONE","kernelConfig":{"use_3steps":false,"half_lds":true,"dir_reg":true,"buffer_inst":true,"tpb":16,"wgs":64,"tpt":[ 4,0 ],"factors":[ 4,4,4 ],"ebtype":"NONE","direction":1,"static_dim":2,"placement":"IP","iAryType":"CI","oAryType":"CI"}}} ,{"sol_node_type":"SOL_KERNEL_ONLY","kernel_key":{"lengths":[ 64,0 ],"precision":"single","scheme":"CS_KERNEL_STOCKHAM_BLOCK_CC","sbrc_trans":"NONE","kernelConfig":{"use_3steps":false,"half_lds":true,"dir_reg":true,"buffer_inst":true,"tpb":16,"wgs":64,"tpt":[ 4,0 ],"factors":[ 2,2,4,4 ],"ebtype":"NONE","direction":-1,"static_dim":3,"placement":"IP","iAryType":"CI","oAryType":"HI"}}} ]}, {"Problem":{"arch":"gfx90a","token":"kernel_len72_single_sbcc"}, "Solutions":[ {"sol_node_type":"SOL_KERNEL_ONLY","kernel_key":{"lengths":[ 72,0 ],"precision":"single","scheme":"CS_KERNEL_STOCKHAM_BLOCK_CC","sbrc_trans":"NONE","kernelConfig":{"use_3steps":false,"half_lds":true,"dir_reg":true,"buffer_inst":true,"tpb":32,"wgs":192,"tpt":[ 6,0 ],"factors":[ 2,6,3,2 ],"ebtype":"NONE","direction":-1,"static_dim":3,"placement":"IP","iAryType":"CI","oAryType":"HI"}}} ,{"sol_node_type":"SOL_KERNEL_ONLY","kernel_key":{"lengths":[ 72,0 ],"precision":"single","scheme":"CS_KERNEL_STOCKHAM_BLOCK_CC","sbrc_trans":"NONE","kernelConfig":{"use_3steps":false,"half_lds":true,"dir_reg":true,"buffer_inst":true,"tpb":32,"wgs":192,"tpt":[ 6,0 ],"factors":[ 2,6,6 ],"ebtype":"NONE","direction":1,"static_dim":2,"placement":"IP","iAryType":"CI","oAryType":"CI"}}} ,{"sol_node_type":"SOL_KERNEL_ONLY","kernel_key":{"lengths":[ 72,0 ],"precision":"single","scheme":"CS_KERNEL_STOCKHAM_BLOCK_CC","sbrc_trans":"NONE","kernelConfig":{"use_3steps":false,"half_lds":true,"dir_reg":true,"buffer_inst":false,"tpb":42,"wgs":252,"tpt":[ 6,0 ],"factors":[ 3,4,6 ],"ebtype":"NONE","direction":-1,"static_dim":3,"placement":"IP","iAryType":"CI","oAryType":"HI"}}} ,{"sol_node_type":"SOL_KERNEL_ONLY","kernel_key":{"lengths":[ 72,0 ],"precision":"single","scheme":"CS_KERNEL_STOCKHAM_BLOCK_CC","sbrc_trans":"NONE","kernelConfig":{"use_3steps":false,"half_lds":true,"dir_reg":true,"buffer_inst":false,"tpb":32,"wgs":192,"tpt":[ 6,0 ],"factors":[ 2,6,6 ],"ebtype":"NONE","direction":1,"static_dim":2,"placement":"IP","iAryType":"CI","oAryType":"CI"}}} ]}, {"Problem":{"arch":"gfx90a","token":"kernel_len80_single_sbcc"}, "Solutions":[ {"sol_node_type":"SOL_KERNEL_ONLY","kernel_key":{"lengths":[ 80,0 ],"precision":"single","scheme":"CS_KERNEL_STOCKHAM_BLOCK_CC","sbrc_trans":"NONE","kernelConfig":{"use_3steps":false,"half_lds":true,"dir_reg":true,"buffer_inst":false,"tpb":51,"wgs":255,"tpt":[ 5,0 ],"factors":[ 4,4,5 ],"ebtype":"NONE","direction":-1,"static_dim":3,"placement":"IP","iAryType":"CI","oAryType":"HI"}}} ,{"sol_node_type":"SOL_KERNEL_ONLY","kernel_key":{"lengths":[ 80,0 ],"precision":"single","scheme":"CS_KERNEL_STOCKHAM_BLOCK_CC","sbrc_trans":"NONE","kernelConfig":{"use_3steps":false,"half_lds":true,"dir_reg":true,"buffer_inst":false,"tpb":51,"wgs":255,"tpt":[ 5,0 ],"factors":[ 2,2,4,5 ],"ebtype":"NONE","direction":1,"static_dim":2,"placement":"IP","iAryType":"CI","oAryType":"CI"}}} ,{"sol_node_type":"SOL_KERNEL_ONLY","kernel_key":{"lengths":[ 80,0 ],"precision":"single","scheme":"CS_KERNEL_STOCKHAM_BLOCK_CC","sbrc_trans":"NONE","kernelConfig":{"use_3steps":false,"half_lds":true,"dir_reg":true,"buffer_inst":true,"tpb":38,"wgs":190,"tpt":[ 5,0 ],"factors":[ 4,4,5 ],"ebtype":"NONE","direction":1,"static_dim":2,"placement":"IP","iAryType":"CI","oAryType":"CI"}}} ,{"sol_node_type":"SOL_KERNEL_ONLY","kernel_key":{"lengths":[ 80,0 ],"precision":"single","scheme":"CS_KERNEL_STOCKHAM_BLOCK_CC","sbrc_trans":"NONE","kernelConfig":{"use_3steps":false,"half_lds":false,"dir_reg":true,"buffer_inst":false,"tpb":38,"wgs":190,"tpt":[ 5,0 ],"factors":[ 2,2,4,5 ],"ebtype":"NONE","direction":-1,"static_dim":3,"placement":"IP","iAryType":"CI","oAryType":"CI"}}} ,{"sol_node_type":"SOL_KERNEL_ONLY","kernel_key":{"lengths":[ 80,0 ],"precision":"single","scheme":"CS_KERNEL_STOCKHAM_BLOCK_CC","sbrc_trans":"NONE","kernelConfig":{"use_3steps":false,"half_lds":false,"dir_reg":true,"buffer_inst":false,"tpb":25,"wgs":125,"tpt":[ 5,0 ],"factors":[ 2,2,4,5 ],"ebtype":"NONE","direction":1,"static_dim":3,"placement":"IP","iAryType":"CI","oAryType":"CI"}}} ]}, {"Problem":{"arch":"gfx90a","token":"kernel_len81_double_sbcc"}, "Solutions":[ {"sol_node_type":"SOL_KERNEL_ONLY","kernel_key":{"lengths":[ 81,0 ],"precision":"double","scheme":"CS_KERNEL_STOCKHAM_BLOCK_CC","sbrc_trans":"NONE","kernelConfig":{"use_3steps":false,"half_lds":false,"dir_reg":false,"buffer_inst":false,"tpb":21,"wgs":189,"tpt":[ 9,0 ],"factors":[ 3,9,3 ],"ebtype":"NONE","direction":-1,"static_dim":0,"placement":"NA","iAryType":"CI","oAryType":"CI"}}} ]}, {"Problem":{"arch":"gfx90a","token":"kernel_len81_single_sbcc"}, "Solutions":[ {"sol_node_type":"SOL_KERNEL_ONLY","kernel_key":{"lengths":[ 81,0 ],"precision":"single","scheme":"CS_KERNEL_STOCKHAM_BLOCK_CC","sbrc_trans":"NONE","kernelConfig":{"use_3steps":true,"half_lds":false,"dir_reg":true,"buffer_inst":true,"tpb":21,"wgs":189,"tpt":[ 9,0 ],"factors":[ 9,3,3 ],"ebtype":"NONE","direction":-1,"static_dim":0,"placement":"NA","iAryType":"CI","oAryType":"CI"}}} ,{"sol_node_type":"SOL_KERNEL_ONLY","kernel_key":{"lengths":[ 81,0 ],"precision":"single","scheme":"CS_KERNEL_STOCKHAM_BLOCK_CC","sbrc_trans":"NONE","kernelConfig":{"use_3steps":true,"half_lds":false,"dir_reg":true,"buffer_inst":false,"tpb":21,"wgs":189,"tpt":[ 9,0 ],"factors":[ 3,9,3 ],"ebtype":"NONE","direction":-1,"static_dim":0,"placement":"NA","iAryType":"CI","oAryType":"CI"}}} ]}, {"Problem":{"arch":"gfx90a","token":"kernel_len84_single_sbcc"}, "Solutions":[ {"sol_node_type":"SOL_KERNEL_ONLY","kernel_key":{"lengths":[ 84,0 ],"precision":"single","scheme":"CS_KERNEL_STOCKHAM_BLOCK_CC","sbrc_trans":"NONE","kernelConfig":{"use_3steps":false,"half_lds":false,"dir_reg":true,"buffer_inst":false,"tpb":14,"wgs":196,"tpt":[ 14,0 ],"factors":[ 2,6,7 ],"ebtype":"NONE","direction":-1,"static_dim":3,"placement":"IP","iAryType":"CI","oAryType":"HI"}}} ,{"sol_node_type":"SOL_KERNEL_ONLY","kernel_key":{"lengths":[ 84,0 ],"precision":"single","scheme":"CS_KERNEL_STOCKHAM_BLOCK_CC","sbrc_trans":"NONE","kernelConfig":{"use_3steps":false,"half_lds":true,"dir_reg":true,"buffer_inst":false,"tpb":36,"wgs":252,"tpt":[ 7,0 ],"factors":[ 6,2,7 ],"ebtype":"NONE","direction":1,"static_dim":2,"placement":"IP","iAryType":"CI","oAryType":"CI"}}} ,{"sol_node_type":"SOL_KERNEL_ONLY","kernel_key":{"lengths":[ 84,0 ],"precision":"single","scheme":"CS_KERNEL_STOCKHAM_BLOCK_CC","sbrc_trans":"NONE","kernelConfig":{"use_3steps":false,"half_lds":false,"dir_reg":true,"buffer_inst":false,"tpb":27,"wgs":189,"tpt":[ 7,0 ],"factors":[ 3,4,7 ],"ebtype":"NONE","direction":-1,"static_dim":2,"placement":"IP","iAryType":"CI","oAryType":"HI"}}} ,{"sol_node_type":"SOL_KERNEL_ONLY","kernel_key":{"lengths":[ 84,0 ],"precision":"single","scheme":"CS_KERNEL_STOCKHAM_BLOCK_CC","sbrc_trans":"NONE","kernelConfig":{"use_3steps":false,"half_lds":false,"dir_reg":true,"buffer_inst":false,"tpb":27,"wgs":189,"tpt":[ 7,0 ],"factors":[ 2,6,7 ],"ebtype":"NONE","direction":1,"static_dim":2,"placement":"IP","iAryType":"CI","oAryType":"CI"}}} ]}, {"Problem":{"arch":"gfx90a","token":"kernel_len96_double_sbcc"}, "Solutions":[ {"sol_node_type":"SOL_KERNEL_ONLY","kernel_key":{"lengths":[ 96,0 ],"precision":"double","scheme":"CS_KERNEL_STOCKHAM_BLOCK_CC","sbrc_trans":"NONE","kernelConfig":{"use_3steps":false,"half_lds":false,"dir_reg":true,"buffer_inst":false,"tpb":16,"wgs":192,"tpt":[ 12,0 ],"factors":[ 8,3,4 ],"ebtype":"NONE","direction":-1,"static_dim":0,"placement":"NA","iAryType":"CI","oAryType":"CI"}}} ,{"sol_node_type":"SOL_KERNEL_ONLY","kernel_key":{"lengths":[ 96,0 ],"precision":"double","scheme":"CS_KERNEL_STOCKHAM_BLOCK_CC","sbrc_trans":"NONE","kernelConfig":{"use_3steps":false,"half_lds":true,"dir_reg":true,"buffer_inst":true,"tpb":16,"wgs":128,"tpt":[ 8,0 ],"factors":[ 4,2,3,4 ],"ebtype":"NONE","direction":-1,"static_dim":0,"placement":"NA","iAryType":"CI","oAryType":"CI"}}} ]}, {"Problem":{"arch":"gfx90a","token":"kernel_len96_single_sbcc"}, "Solutions":[ {"sol_node_type":"SOL_KERNEL_ONLY","kernel_key":{"lengths":[ 96,0 ],"precision":"single","scheme":"CS_KERNEL_STOCKHAM_BLOCK_CC","sbrc_trans":"NONE","kernelConfig":{"use_3steps":false,"half_lds":false,"dir_reg":true,"buffer_inst":true,"tpb":21,"wgs":126,"tpt":[ 6,0 ],"factors":[ 2,2,4,6 ],"ebtype":"NONE","direction":-1,"static_dim":2,"placement":"IP","iAryType":"CI","oAryType":"HI"}}} ,{"sol_node_type":"SOL_KERNEL_ONLY","kernel_key":{"lengths":[ 96,0 ],"precision":"single","scheme":"CS_KERNEL_STOCKHAM_BLOCK_CC","sbrc_trans":"NONE","kernelConfig":{"use_3steps":false,"half_lds":false,"dir_reg":true,"buffer_inst":false,"tpb":24,"wgs":192,"tpt":[ 8,0 ],"factors":[ 3,4,8 ],"ebtype":"NONE","direction":1,"static_dim":2,"placement":"IP","iAryType":"CI","oAryType":"CI"}}} ,{"sol_node_type":"SOL_KERNEL_ONLY","kernel_key":{"lengths":[ 96,0 ],"precision":"single","scheme":"CS_KERNEL_STOCKHAM_BLOCK_CC","sbrc_trans":"NONE","kernelConfig":{"use_3steps":false,"half_lds":false,"dir_reg":true,"buffer_inst":true,"tpb":24,"wgs":192,"tpt":[ 8,0 ],"factors":[ 2,6,8 ],"ebtype":"NONE","direction":-1,"static_dim":2,"placement":"IP","iAryType":"CI","oAryType":"HI"}}} ,{"sol_node_type":"SOL_KERNEL_ONLY","kernel_key":{"lengths":[ 96,0 ],"precision":"single","scheme":"CS_KERNEL_STOCKHAM_BLOCK_CC","sbrc_trans":"NONE","kernelConfig":{"use_3steps":false,"half_lds":false,"dir_reg":true,"buffer_inst":false,"tpb":24,"wgs":192,"tpt":[ 8,0 ],"factors":[ 4,3,8 ],"ebtype":"NONE","direction":-1,"static_dim":3,"placement":"IP","iAryType":"CI","oAryType":"CI"}}} ,{"sol_node_type":"SOL_KERNEL_ONLY","kernel_key":{"lengths":[ 96,0 ],"precision":"single","scheme":"CS_KERNEL_STOCKHAM_BLOCK_CC","sbrc_trans":"NONE","kernelConfig":{"use_3steps":false,"half_lds":false,"dir_reg":true,"buffer_inst":true,"tpb":24,"wgs":192,"tpt":[ 8,0 ],"factors":[ 3,4,8 ],"ebtype":"NONE","direction":1,"static_dim":3,"placement":"IP","iAryType":"CI","oAryType":"CI"}}} ,{"sol_node_type":"SOL_KERNEL_ONLY","kernel_key":{"lengths":[ 96,0 ],"precision":"single","scheme":"CS_KERNEL_STOCKHAM_BLOCK_CC","sbrc_trans":"NONE","kernelConfig":{"use_3steps":false,"half_lds":false,"dir_reg":true,"buffer_inst":true,"tpb":24,"wgs":192,"tpt":[ 8,0 ],"factors":[ 4,2,3,4 ],"ebtype":"NONE","direction":1,"static_dim":2,"placement":"IP","iAryType":"CI","oAryType":"CI"}}} ]}, {"Problem":{"arch":"gfx90a","token":"kernel_len100_double_sbrc"}, "Solutions":[ {"sol_node_type":"SOL_KERNEL_ONLY","kernel_key":{"lengths":[ 100,0 ],"precision":"double","scheme":"CS_KERNEL_STOCKHAM_BLOCK_RC","sbrc_trans":"TILE_ALIGNED","kernelConfig":{"use_3steps":false,"half_lds":false,"dir_reg":true,"buffer_inst":false,"tpb":20,"wgs":200,"tpt":[ 10,0 ],"factors":[ 10,5,2 ],"ebtype":"NONE","direction":-1,"static_dim":0,"placement":"NA","iAryType":"CI","oAryType":"CI"}}} ]}, {"Problem":{"arch":"gfx90a","token":"kernel_len112_double_sbrc"}, "Solutions":[ {"sol_node_type":"SOL_KERNEL_ONLY","kernel_key":{"lengths":[ 112,0 ],"precision":"double","scheme":"CS_KERNEL_STOCKHAM_BLOCK_RC","sbrc_trans":"TILE_ALIGNED","kernelConfig":{"use_3steps":false,"half_lds":false,"dir_reg":true,"buffer_inst":false,"tpb":16,"wgs":128,"tpt":[ 8,0 ],"factors":[ 7,2,8 ],"ebtype":"NONE","direction":-1,"static_dim":0,"placement":"NA","iAryType":"CI","oAryType":"CI"}}} ,{"sol_node_type":"SOL_KERNEL_ONLY","kernel_key":{"lengths":[ 112,0 ],"precision":"double","scheme":"CS_KERNEL_STOCKHAM_BLOCK_RC","sbrc_trans":"TILE_ALIGNED","kernelConfig":{"use_3steps":false,"half_lds":false,"dir_reg":true,"buffer_inst":false,"tpb":16,"wgs":128,"tpt":[ 8,0 ],"factors":[ 2,4,7,2 ],"ebtype":"NONE","direction":-1,"static_dim":0,"placement":"NA","iAryType":"CI","oAryType":"CI"}}} ,{"sol_node_type":"SOL_KERNEL_ONLY","kernel_key":{"lengths":[ 112,0 ],"precision":"double","scheme":"CS_KERNEL_STOCKHAM_BLOCK_RC","sbrc_trans":"TILE_ALIGNED","kernelConfig":{"use_3steps":false,"half_lds":false,"dir_reg":true,"buffer_inst":false,"tpb":8,"wgs":64,"tpt":[ 8,0 ],"factors":[ 7,2,8 ],"ebtype":"NONE","direction":-1,"static_dim":0,"placement":"NA","iAryType":"CI","oAryType":"CI"}}} ]}, {"Problem":{"arch":"gfx90a","token":"kernel_len125_double_sbrc"}, "Solutions":[ {"sol_node_type":"SOL_KERNEL_ONLY","kernel_key":{"lengths":[ 125,0 ],"precision":"double","scheme":"CS_KERNEL_STOCKHAM_BLOCK_RC","sbrc_trans":"TILE_UNALIGNED","kernelConfig":{"use_3steps":false,"half_lds":false,"dir_reg":true,"buffer_inst":false,"tpb":10,"wgs":250,"tpt":[ 25,0 ],"factors":[ 5,5,5 ],"ebtype":"NONE","direction":-1,"static_dim":0,"placement":"NA","iAryType":"CI","oAryType":"CI"}}} ]}, {"Problem":{"arch":"gfx90a","token":"kernel_len128_double_sbrc"}, "Solutions":[ {"sol_node_type":"SOL_KERNEL_ONLY","kernel_key":{"lengths":[ 128,0 ],"precision":"double","scheme":"CS_KERNEL_STOCKHAM_BLOCK_RC","sbrc_trans":"TILE_ALIGNED","kernelConfig":{"use_3steps":false,"half_lds":false,"dir_reg":true,"buffer_inst":false,"tpb":8,"wgs":128,"tpt":[ 16,0 ],"factors":[ 8,8,2 ],"ebtype":"NONE","direction":-1,"static_dim":0,"placement":"NA","iAryType":"CI","oAryType":"CI"}}} ,{"sol_node_type":"SOL_KERNEL_ONLY","kernel_key":{"lengths":[ 128,0 ],"precision":"double","scheme":"CS_KERNEL_STOCKHAM_BLOCK_RC","sbrc_trans":"TILE_ALIGNED","kernelConfig":{"use_3steps":false,"half_lds":false,"dir_reg":true,"buffer_inst":false,"tpb":16,"wgs":128,"tpt":[ 8,0 ],"factors":[ 8,2,8 ],"ebtype":"NONE","direction":-1,"static_dim":0,"placement":"NA","iAryType":"CI","oAryType":"CI"}}} ,{"sol_node_type":"SOL_KERNEL_ONLY","kernel_key":{"lengths":[ 128,0 ],"precision":"double","scheme":"CS_KERNEL_STOCKHAM_BLOCK_RC","sbrc_trans":"TILE_ALIGNED","kernelConfig":{"use_3steps":false,"half_lds":false,"dir_reg":true,"buffer_inst":false,"tpb":16,"wgs":128,"tpt":[ 8,0 ],"factors":[ 4,2,2,8 ],"ebtype":"NONE","direction":-1,"static_dim":2,"placement":"OP","iAryType":"CI","oAryType":"CI"}}} ,{"sol_node_type":"SOL_KERNEL_ONLY","kernel_key":{"lengths":[ 128,0 ],"precision":"double","scheme":"CS_KERNEL_STOCKHAM_BLOCK_RC","sbrc_trans":"TILE_ALIGNED","kernelConfig":{"use_3steps":false,"half_lds":false,"dir_reg":true,"buffer_inst":false,"tpb":16,"wgs":128,"tpt":[ 8,0 ],"factors":[ 4,16,2 ],"ebtype":"NONE","direction":-1,"static_dim":2,"placement":"OP","iAryType":"CI","oAryType":"CI"}}} ,{"sol_node_type":"SOL_KERNEL_ONLY","kernel_key":{"lengths":[ 128,0 ],"precision":"double","scheme":"CS_KERNEL_STOCKHAM_BLOCK_RC","sbrc_trans":"TILE_ALIGNED","kernelConfig":{"use_3steps":false,"half_lds":false,"dir_reg":true,"buffer_inst":false,"tpb":16,"wgs":128,"tpt":[ 8,0 ],"factors":[ 2,4,4,4 ],"ebtype":"NONE","direction":1,"static_dim":2,"placement":"OP","iAryType":"CI","oAryType":"CI"}}} ]}, {"Problem":{"arch":"gfx90a","token":"kernel_len192_double_sbrc"}, "Solutions":[ {"sol_node_type":"SOL_KERNEL_ONLY","kernel_key":{"lengths":[ 192,0 ],"precision":"double","scheme":"CS_KERNEL_STOCKHAM_BLOCK_RC","sbrc_trans":"TILE_ALIGNED","kernelConfig":{"use_3steps":false,"half_lds":false,"dir_reg":true,"buffer_inst":false,"tpb":4,"wgs":64,"tpt":[ 16,0 ],"factors":[ 2,8,6,2 ],"ebtype":"NONE","direction":-1,"static_dim":0,"placement":"NA","iAryType":"CI","oAryType":"CI"}}} ,{"sol_node_type":"SOL_KERNEL_ONLY","kernel_key":{"lengths":[ 192,0 ],"precision":"double","scheme":"CS_KERNEL_STOCKHAM_BLOCK_RC","sbrc_trans":"TILE_ALIGNED","kernelConfig":{"use_3steps":false,"half_lds":false,"dir_reg":true,"buffer_inst":false,"tpb":8,"wgs":128,"tpt":[ 16,0 ],"factors":[ 8,4,3,2 ],"ebtype":"NONE","direction":-1,"static_dim":0,"placement":"NA","iAryType":"CI","oAryType":"CI"}}} ,{"sol_node_type":"SOL_KERNEL_ONLY","kernel_key":{"lengths":[ 192,0 ],"precision":"double","scheme":"CS_KERNEL_STOCKHAM_BLOCK_RC","sbrc_trans":"TILE_ALIGNED","kernelConfig":{"use_3steps":false,"half_lds":false,"dir_reg":true,"buffer_inst":false,"tpb":8,"wgs":192,"tpt":[ 24,0 ],"factors":[ 8,3,8 ],"ebtype":"NONE","direction":-1,"static_dim":0,"placement":"NA","iAryType":"CI","oAryType":"CI"}}} ]}, {"Problem":{"arch":"gfx90a","token":"kernel_len256_double_sbrc"}, "Solutions":[ {"sol_node_type":"SOL_KERNEL_ONLY","kernel_key":{"lengths":[ 256,0 ],"precision":"double","scheme":"CS_KERNEL_STOCKHAM_BLOCK_RC","sbrc_trans":"TILE_ALIGNED","kernelConfig":{"use_3steps":false,"half_lds":false,"dir_reg":true,"buffer_inst":false,"tpb":8,"wgs":256,"tpt":[ 32,0 ],"factors":[ 8,4,4,2 ],"ebtype":"NONE","direction":-1,"static_dim":0,"placement":"NA","iAryType":"CI","oAryType":"CI"}}} ]}, {"Problem":{"arch":"gfx90a","token":"kernel_len49_double_sbrc"}, "Solutions":[ {"sol_node_type":"SOL_KERNEL_ONLY","kernel_key":{"lengths":[ 49,0 ],"precision":"double","scheme":"CS_KERNEL_STOCKHAM_BLOCK_RC","sbrc_trans":"TILE_UNALIGNED","kernelConfig":{"use_3steps":false,"half_lds":false,"dir_reg":false,"buffer_inst":false,"tpb":28,"wgs":196,"tpt":[ 7,0 ],"factors":[ 7,7 ],"ebtype":"NONE","direction":-1,"static_dim":0,"placement":"NA","iAryType":"CI","oAryType":"CI"}}} ,{"sol_node_type":"SOL_KERNEL_ONLY","kernel_key":{"lengths":[ 49,0 ],"precision":"double","scheme":"CS_KERNEL_STOCKHAM_BLOCK_RC","sbrc_trans":"TILE_UNALIGNED","kernelConfig":{"use_3steps":false,"half_lds":false,"dir_reg":true,"buffer_inst":false,"tpb":28,"wgs":196,"tpt":[ 7,0 ],"factors":[ 7,7 ],"ebtype":"NONE","direction":1,"static_dim":2,"placement":"OP","iAryType":"CI","oAryType":"CI"}}} ]}, {"Problem":{"arch":"gfx90a","token":"kernel_len81_double_sbrc"}, "Solutions":[ {"sol_node_type":"SOL_KERNEL_ONLY","kernel_key":{"lengths":[ 81,0 ],"precision":"double","scheme":"CS_KERNEL_STOCKHAM_BLOCK_RC","sbrc_trans":"TILE_UNALIGNED","kernelConfig":{"use_3steps":false,"half_lds":false,"dir_reg":true,"buffer_inst":false,"tpb":22,"wgs":198,"tpt":[ 9,0 ],"factors":[ 9,3,3 ],"ebtype":"NONE","direction":-1,"static_dim":0,"placement":"NA","iAryType":"CI","oAryType":"CI"}}} ]}, {"Problem":{"arch":"gfx90a","token":"kernel_len81_single_sbrc"}, "Solutions":[ {"sol_node_type":"SOL_KERNEL_ONLY","kernel_key":{"lengths":[ 81,0 ],"precision":"single","scheme":"CS_KERNEL_STOCKHAM_BLOCK_RC","sbrc_trans":"TILE_UNALIGNED","kernelConfig":{"use_3steps":false,"half_lds":false,"dir_reg":false,"buffer_inst":false,"tpb":36,"wgs":324,"tpt":[ 9,0 ],"factors":[ 9,3,3 ],"ebtype":"NONE","direction":-1,"static_dim":0,"placement":"NA","iAryType":"CI","oAryType":"CI"}}} ,{"sol_node_type":"SOL_KERNEL_ONLY","kernel_key":{"lengths":[ 81,0 ],"precision":"single","scheme":"CS_KERNEL_STOCKHAM_BLOCK_RC","sbrc_trans":"TILE_UNALIGNED","kernelConfig":{"use_3steps":false,"half_lds":false,"dir_reg":true,"buffer_inst":false,"tpb":36,"wgs":324,"tpt":[ 9,0 ],"factors":[ 9,3,3 ],"ebtype":"NONE","direction":-1,"static_dim":0,"placement":"NA","iAryType":"CI","oAryType":"CI"}}} ,{"sol_node_type":"SOL_KERNEL_ONLY","kernel_key":{"lengths":[ 81,0 ],"precision":"single","scheme":"CS_KERNEL_STOCKHAM_BLOCK_RC","sbrc_trans":"TILE_UNALIGNED","kernelConfig":{"use_3steps":false,"half_lds":false,"dir_reg":true,"buffer_inst":false,"tpb":36,"wgs":324,"tpt":[ 9,0 ],"factors":[ 3,9,3 ],"ebtype":"NONE","direction":-1,"static_dim":0,"placement":"NA","iAryType":"CI","oAryType":"CI"}}} ]}, {"Problem":{"arch":"gfx90a","token":"kernel_len336_double_sbcr"}, "Solutions":[ {"sol_node_type":"SOL_KERNEL_ONLY","kernel_key":{"lengths":[ 336,0 ],"precision":"double","scheme":"CS_KERNEL_STOCKHAM_BLOCK_CR","sbrc_trans":"NONE","kernelConfig":{"use_3steps":false,"half_lds":false,"dir_reg":true,"buffer_inst":false,"tpb":6,"wgs":252,"tpt":[ 42,0 ],"factors":[ 8,2,3,7 ],"ebtype":"NONE","direction":-1,"static_dim":0,"placement":"NA","iAryType":"CI","oAryType":"CI"}}} ,{"sol_node_type":"SOL_KERNEL_ONLY","kernel_key":{"lengths":[ 336,0 ],"precision":"double","scheme":"CS_KERNEL_STOCKHAM_BLOCK_CR","sbrc_trans":"NONE","kernelConfig":{"use_3steps":false,"half_lds":false,"dir_reg":true,"buffer_inst":true,"tpb":6,"wgs":252,"tpt":[ 42,0 ],"factors":[ 7,8,2,3 ],"ebtype":"NONE","direction":-1,"static_dim":0,"placement":"NA","iAryType":"CI","oAryType":"CI"}}} ,{"sol_node_type":"SOL_KERNEL_ONLY","kernel_key":{"lengths":[ 336,0 ],"precision":"double","scheme":"CS_KERNEL_STOCKHAM_BLOCK_CR","sbrc_trans":"NONE","kernelConfig":{"use_3steps":false,"half_lds":false,"dir_reg":true,"buffer_inst":true,"tpb":6,"wgs":252,"tpt":[ 42,0 ],"factors":[ 7,2,4,6 ],"ebtype":"NONE","direction":-1,"static_dim":0,"placement":"NA","iAryType":"CI","oAryType":"CI"}}} ,{"sol_node_type":"SOL_KERNEL_ONLY","kernel_key":{"lengths":[ 336,0 ],"precision":"double","scheme":"CS_KERNEL_STOCKHAM_BLOCK_CR","sbrc_trans":"NONE","kernelConfig":{"use_3steps":false,"half_lds":false,"dir_reg":true,"buffer_inst":true,"tpb":6,"wgs":252,"tpt":[ 42,0 ],"factors":[ 8,2,3,7 ],"ebtype":"NONE","direction":-1,"static_dim":0,"placement":"NA","iAryType":"CI","oAryType":"CI"}}} ]}, {"Problem":{"arch":"gfx90a","token":"kernel_len56_double_sbcr"}, "Solutions":[ {"sol_node_type":"SOL_KERNEL_ONLY","kernel_key":{"lengths":[ 56,0 ],"precision":"double","scheme":"CS_KERNEL_STOCKHAM_BLOCK_CR","sbrc_trans":"NONE","kernelConfig":{"use_3steps":false,"half_lds":false,"dir_reg":true,"buffer_inst":true,"tpb":16,"wgs":128,"tpt":[ 8,0 ],"factors":[ 2,7,4 ],"ebtype":"NONE","direction":-1,"static_dim":0,"placement":"NA","iAryType":"CI","oAryType":"CI"}}} ,{"sol_node_type":"SOL_KERNEL_ONLY","kernel_key":{"lengths":[ 56,0 ],"precision":"double","scheme":"CS_KERNEL_STOCKHAM_BLOCK_CR","sbrc_trans":"NONE","kernelConfig":{"use_3steps":false,"half_lds":false,"dir_reg":true,"buffer_inst":false,"tpb":16,"wgs":128,"tpt":[ 8,0 ],"factors":[ 2,7,4 ],"ebtype":"NONE","direction":-1,"static_dim":0,"placement":"NA","iAryType":"CI","oAryType":"CI"}}} ]}, {"Problem":{"arch":"gfx90a","token":"kernel_len256_single_sbrc_xy_z"}, "Solutions":[ {"sol_node_type":"SOL_KERNEL_ONLY","kernel_key":{"lengths":[ 256,0 ],"precision":"single","scheme":"CS_KERNEL_STOCKHAM_TRANSPOSE_XY_Z","sbrc_trans":"TILE_ALIGNED","kernelConfig":{"use_3steps":false,"half_lds":false,"dir_reg":true,"buffer_inst":false,"tpb":16,"wgs":256,"tpt":[ 16,0 ],"factors":[ 4,2,2,16 ],"ebtype":"NONE","direction":-1,"static_dim":0,"placement":"NA","iAryType":"CI","oAryType":"CI"}}} ,{"sol_node_type":"SOL_KERNEL_ONLY","kernel_key":{"lengths":[ 256,0 ],"precision":"single","scheme":"CS_KERNEL_STOCKHAM_TRANSPOSE_XY_Z","sbrc_trans":"TILE_ALIGNED","kernelConfig":{"use_3steps":false,"half_lds":false,"dir_reg":true,"buffer_inst":false,"tpb":16,"wgs":256,"tpt":[ 16,0 ],"factors":[ 8,2,16 ],"ebtype":"NONE","direction":-1,"static_dim":0,"placement":"NA","iAryType":"CI","oAryType":"CI"}}} ]}, {"Problem":{"arch":"gfx90a","token":"kernel_len10x20_single_2d_single"}, "Solutions":[ {"sol_node_type":"SOL_KERNEL_ONLY","kernel_key":{"lengths":[ 10,20 ],"precision":"single","scheme":"CS_KERNEL_2D_SINGLE","sbrc_trans":"NONE","kernelConfig":{"use_3steps":false,"half_lds":false,"dir_reg":false,"buffer_inst":false,"tpb":1,"wgs":100,"tpt":[ 1,10 ],"factors":[ 10,2,10 ],"ebtype":"R2C_POST","direction":-1,"static_dim":3,"placement":"OP","iAryType":"CI","oAryType":"CI"}}} ]}, {"Problem":{"arch":"gfx90a","token":"kernel_len20x10_single_2d_single"}, "Solutions":[ {"sol_node_type":"SOL_KERNEL_ONLY","kernel_key":{"lengths":[ 20,10 ],"precision":"single","scheme":"CS_KERNEL_2D_SINGLE","sbrc_trans":"NONE","kernelConfig":{"use_3steps":false,"half_lds":false,"dir_reg":false,"buffer_inst":false,"tpb":1,"wgs":100,"tpt":[ 5,5 ],"factors":[ 5,4,5,2 ],"ebtype":"C2R_PRE","direction":1,"static_dim":3,"placement":"OP","iAryType":"CI","oAryType":"CI"}}} ]}, {"Problem":{"arch":"gfx90a","token":"kernel_len26x64_single_2d_single"}, "Solutions":[ {"sol_node_type":"SOL_KERNEL_ONLY","kernel_key":{"lengths":[ 26,64 ],"precision":"single","scheme":"CS_KERNEL_2D_SINGLE","sbrc_trans":"NONE","kernelConfig":{"use_3steps":false,"half_lds":false,"dir_reg":false,"buffer_inst":false,"tpb":1,"wgs":208,"tpt":[ 2,8 ],"factors":[ 13,2,8,8 ],"ebtype":"R2C_POST","direction":-1,"static_dim":3,"placement":"OP","iAryType":"CI","oAryType":"CI"}}} ]}, {"Problem":{"arch":"gfx90a","token":"kernel_len26x72_single_2d_single"}, "Solutions":[ {"sol_node_type":"SOL_KERNEL_ONLY","kernel_key":{"lengths":[ 26,72 ],"precision":"single","scheme":"CS_KERNEL_2D_SINGLE","sbrc_trans":"NONE","kernelConfig":{"use_3steps":false,"half_lds":false,"dir_reg":false,"buffer_inst":false,"tpb":1,"wgs":234,"tpt":[ 2,9 ],"factors":[ 13,2,9,8 ],"ebtype":"R2C_POST","direction":-1,"static_dim":3,"placement":"OP","iAryType":"CI","oAryType":"CI"}}} ]}, {"Problem":{"arch":"gfx90a","token":"kernel_len30x60_single_2d_single"}, "Solutions":[ {"sol_node_type":"SOL_KERNEL_ONLY","kernel_key":{"lengths":[ 30,60 ],"precision":"single","scheme":"CS_KERNEL_2D_SINGLE","sbrc_trans":"NONE","kernelConfig":{"use_3steps":false,"half_lds":false,"dir_reg":false,"buffer_inst":false,"tpb":1,"wgs":180,"tpt":[ 3,6 ],"factors":[ 10,3,6,10 ],"ebtype":"R2C_POST","direction":-1,"static_dim":3,"placement":"OP","iAryType":"CI","oAryType":"CI"}}} ]}, {"Problem":{"arch":"gfx90a","token":"kernel_len32x16_single_2d_single"}, "Solutions":[ {"sol_node_type":"SOL_KERNEL_ONLY","kernel_key":{"lengths":[ 32,16 ],"precision":"single","scheme":"CS_KERNEL_2D_SINGLE","sbrc_trans":"NONE","kernelConfig":{"use_3steps":false,"half_lds":false,"dir_reg":false,"buffer_inst":false,"tpb":1,"wgs":256,"tpt":[ 16,8 ],"factors":[ 2,2,2,4,2,2,4 ],"ebtype":"C2R_PRE","direction":1,"static_dim":3,"placement":"OP","iAryType":"CI","oAryType":"CI"}}} ]}, {"Problem":{"arch":"gfx90a","token":"kernel_len32x64_single_2d_single"}, "Solutions":[ {"sol_node_type":"SOL_KERNEL_ONLY","kernel_key":{"lengths":[ 32,64 ],"precision":"single","scheme":"CS_KERNEL_2D_SINGLE","sbrc_trans":"NONE","kernelConfig":{"use_3steps":false,"half_lds":false,"dir_reg":false,"buffer_inst":false,"tpb":1,"wgs":256,"tpt":[ 2,8 ],"factors":[ 2,16,8,8 ],"ebtype":"R2C_POST","direction":-1,"static_dim":3,"placement":"OP","iAryType":"CI","oAryType":"CI"}}} ]}, {"Problem":{"arch":"gfx90a","token":"kernel_len36x72_single_2d_single"}, "Solutions":[ {"sol_node_type":"SOL_KERNEL_ONLY","kernel_key":{"lengths":[ 36,72 ],"precision":"single","scheme":"CS_KERNEL_2D_SINGLE","sbrc_trans":"NONE","kernelConfig":{"use_3steps":false,"half_lds":false,"dir_reg":false,"buffer_inst":false,"tpb":1,"wgs":432,"tpt":[ 6,12 ],"factors":[ 6,6,2,6,6 ],"ebtype":"R2C_POST","direction":-1,"static_dim":3,"placement":"OP","iAryType":"CI","oAryType":"CI"}}} ]}, {"Problem":{"arch":"gfx90a","token":"kernel_len36x80_single_2d_single"}, "Solutions":[ {"sol_node_type":"SOL_KERNEL_ONLY","kernel_key":{"lengths":[ 36,80 ],"precision":"single","scheme":"CS_KERNEL_2D_SINGLE","sbrc_trans":"NONE","kernelConfig":{"use_3steps":false,"half_lds":false,"dir_reg":false,"buffer_inst":false,"tpb":1,"wgs":480,"tpt":[ 6,10 ],"factors":[ 6,6,8,10 ],"ebtype":"R2C_POST","direction":-1,"static_dim":3,"placement":"OP","iAryType":"CI","oAryType":"CI"}}} ]}, {"Problem":{"arch":"gfx90a","token":"kernel_len36x84_single_2d_single"}, "Solutions":[ {"sol_node_type":"SOL_KERNEL_ONLY","kernel_key":{"lengths":[ 36,84 ],"precision":"single","scheme":"CS_KERNEL_2D_SINGLE","sbrc_trans":"NONE","kernelConfig":{"use_3steps":false,"half_lds":false,"dir_reg":false,"buffer_inst":false,"tpb":1,"wgs":504,"tpt":[ 6,12 ],"factors":[ 6,6,3,4,7 ],"ebtype":"R2C_POST","direction":-1,"static_dim":3,"placement":"OP","iAryType":"CI","oAryType":"CI"}}} ]}, {"Problem":{"arch":"gfx90a","token":"kernel_len40x80_single_2d_single"}, "Solutions":[ {"sol_node_type":"SOL_KERNEL_ONLY","kernel_key":{"lengths":[ 40,80 ],"precision":"single","scheme":"CS_KERNEL_2D_SINGLE","sbrc_trans":"NONE","kernelConfig":{"use_3steps":false,"half_lds":false,"dir_reg":false,"buffer_inst":false,"tpb":1,"wgs":400,"tpt":[ 5,10 ],"factors":[ 5,8,10,8 ],"ebtype":"R2C_POST","direction":-1,"static_dim":3,"placement":"OP","iAryType":"CI","oAryType":"CI"}}} ]}, {"Problem":{"arch":"gfx90a","token":"kernel_len42x84_single_2d_single"}, "Solutions":[ {"sol_node_type":"SOL_KERNEL_ONLY","kernel_key":{"lengths":[ 42,84 ],"precision":"single","scheme":"CS_KERNEL_2D_SINGLE","sbrc_trans":"NONE","kernelConfig":{"use_3steps":false,"half_lds":false,"dir_reg":false,"buffer_inst":false,"tpb":1,"wgs":504,"tpt":[ 6,12 ],"factors":[ 6,7,3,4,7 ],"ebtype":"R2C_POST","direction":-1,"static_dim":3,"placement":"OP","iAryType":"CI","oAryType":"CI"}}} ]}, {"Problem":{"arch":"gfx90a","token":"kernel_len42x96_single_2d_single"}, "Solutions":[ {"sol_node_type":"SOL_KERNEL_ONLY","kernel_key":{"lengths":[ 42,96 ],"precision":"single","scheme":"CS_KERNEL_2D_SINGLE","sbrc_trans":"NONE","kernelConfig":{"use_3steps":false,"half_lds":false,"dir_reg":false,"buffer_inst":false,"tpb":1,"wgs":672,"tpt":[ 7,16 ],"factors":[ 7,6,4,4,6 ],"ebtype":"R2C_POST","direction":-1,"static_dim":3,"placement":"OP","iAryType":"CI","oAryType":"CI"}}} ]}, {"Problem":{"arch":"gfx90a","token":"kernel_len60x30_single_2d_single"}, "Solutions":[ {"sol_node_type":"SOL_KERNEL_ONLY","kernel_key":{"lengths":[ 60,30 ],"precision":"single","scheme":"CS_KERNEL_2D_SINGLE","sbrc_trans":"NONE","kernelConfig":{"use_3steps":false,"half_lds":false,"dir_reg":false,"buffer_inst":false,"tpb":1,"wgs":180,"tpt":[ 6,3 ],"factors":[ 10,6,10,3 ],"ebtype":"C2R_PRE","direction":1,"static_dim":3,"placement":"OP","iAryType":"CI","oAryType":"CI"}}} ]}, {"Problem":{"arch":"gfx90a","token":"kernel_len64x26_single_2d_single"}, "Solutions":[ {"sol_node_type":"SOL_KERNEL_ONLY","kernel_key":{"lengths":[ 64,26 ],"precision":"single","scheme":"CS_KERNEL_2D_SINGLE","sbrc_trans":"NONE","kernelConfig":{"use_3steps":false,"half_lds":false,"dir_reg":false,"buffer_inst":false,"tpb":1,"wgs":128,"tpt":[ 4,2 ],"factors":[ 4,16,2,13 ],"ebtype":"C2R_PRE","direction":1,"static_dim":3,"placement":"OP","iAryType":"CI","oAryType":"CI"}}} ]}, {"Problem":{"arch":"gfx90a","token":"kernel_len64x32_single_2d_single"}, "Solutions":[ {"sol_node_type":"SOL_KERNEL_ONLY","kernel_key":{"lengths":[ 64,32 ],"precision":"single","scheme":"CS_KERNEL_2D_SINGLE","sbrc_trans":"NONE","kernelConfig":{"use_3steps":false,"half_lds":false,"dir_reg":false,"buffer_inst":false,"tpb":1,"wgs":256,"tpt":[ 8,4 ],"factors":[ 8,8,2,2,8 ],"ebtype":"C2R_PRE","direction":1,"static_dim":3,"placement":"OP","iAryType":"CI","oAryType":"CI"}}} ]}, {"Problem":{"arch":"gfx90a","token":"kernel_len72x26_single_2d_single"}, "Solutions":[ {"sol_node_type":"SOL_KERNEL_ONLY","kernel_key":{"lengths":[ 72,26 ],"precision":"single","scheme":"CS_KERNEL_2D_SINGLE","sbrc_trans":"NONE","kernelConfig":{"use_3steps":false,"half_lds":false,"dir_reg":false,"buffer_inst":false,"tpb":1,"wgs":234,"tpt":[ 9,2 ],"factors":[ 9,8,13,2 ],"ebtype":"C2R_PRE","direction":1,"static_dim":3,"placement":"OP","iAryType":"CI","oAryType":"CI"}}} ]}, {"Problem":{"arch":"gfx90a","token":"kernel_len72x36_single_2d_single"}, "Solutions":[ {"sol_node_type":"SOL_KERNEL_ONLY","kernel_key":{"lengths":[ 72,36 ],"precision":"single","scheme":"CS_KERNEL_2D_SINGLE","sbrc_trans":"NONE","kernelConfig":{"use_3steps":false,"half_lds":false,"dir_reg":false,"buffer_inst":false,"tpb":1,"wgs":432,"tpt":[ 12,6 ],"factors":[ 2,6,6,6,6 ],"ebtype":"C2R_PRE","direction":1,"static_dim":3,"placement":"OP","iAryType":"CI","oAryType":"CI"}}} ]}, {"Problem":{"arch":"gfx90a","token":"kernel_len80x36_single_2d_single"}, "Solutions":[ {"sol_node_type":"SOL_KERNEL_ONLY","kernel_key":{"lengths":[ 80,36 ],"precision":"single","scheme":"CS_KERNEL_2D_SINGLE","sbrc_trans":"NONE","kernelConfig":{"use_3steps":false,"half_lds":false,"dir_reg":false,"buffer_inst":false,"tpb":1,"wgs":480,"tpt":[ 10,6 ],"factors":[ 10,8,6,6 ],"ebtype":"C2R_PRE","direction":1,"static_dim":3,"placement":"OP","iAryType":"CI","oAryType":"CI"}}} ]}, {"Problem":{"arch":"gfx90a","token":"kernel_len80x40_single_2d_single"}, "Solutions":[ {"sol_node_type":"SOL_KERNEL_ONLY","kernel_key":{"lengths":[ 80,40 ],"precision":"single","scheme":"CS_KERNEL_2D_SINGLE","sbrc_trans":"NONE","kernelConfig":{"use_3steps":false,"half_lds":false,"dir_reg":false,"buffer_inst":false,"tpb":1,"wgs":400,"tpt":[ 10,5 ],"factors":[ 8,10,5,8 ],"ebtype":"C2R_PRE","direction":1,"static_dim":3,"placement":"OP","iAryType":"CI","oAryType":"CI"}}} ]}, {"Problem":{"arch":"gfx90a","token":"kernel_len84x36_single_2d_single"}, "Solutions":[ {"sol_node_type":"SOL_KERNEL_ONLY","kernel_key":{"lengths":[ 84,36 ],"precision":"single","scheme":"CS_KERNEL_2D_SINGLE","sbrc_trans":"NONE","kernelConfig":{"use_3steps":false,"half_lds":false,"dir_reg":false,"buffer_inst":false,"tpb":1,"wgs":504,"tpt":[ 12,6 ],"factors":[ 4,3,7,6,6 ],"ebtype":"C2R_PRE","direction":1,"static_dim":3,"placement":"OP","iAryType":"CI","oAryType":"CI"}}} ]}, {"Problem":{"arch":"gfx90a","token":"kernel_len84x42_single_2d_single"}, "Solutions":[ {"sol_node_type":"SOL_KERNEL_ONLY","kernel_key":{"lengths":[ 84,42 ],"precision":"single","scheme":"CS_KERNEL_2D_SINGLE","sbrc_trans":"NONE","kernelConfig":{"use_3steps":false,"half_lds":false,"dir_reg":false,"buffer_inst":false,"tpb":1,"wgs":504,"tpt":[ 12,6 ],"factors":[ 4,3,7,6,7 ],"ebtype":"C2R_PRE","direction":1,"static_dim":3,"placement":"OP","iAryType":"CI","oAryType":"CI"}}} ]}, {"Problem":{"arch":"gfx90a","token":"kernel_len84x7_single_2d_single"}, "Solutions":[ {"sol_node_type":"SOL_KERNEL_ONLY","kernel_key":{"lengths":[ 84,7 ],"precision":"single","scheme":"CS_KERNEL_2D_SINGLE","sbrc_trans":"NONE","kernelConfig":{"use_3steps":false,"half_lds":false,"dir_reg":false,"buffer_inst":false,"tpb":1,"wgs":84,"tpt":[ 12,1 ],"factors":[ 3,7,4,7 ],"ebtype":"C2R_PRE","direction":1,"static_dim":3,"placement":"OP","iAryType":"CI","oAryType":"CI"}}} ]}, {"Problem":{"arch":"gfx90a","token":"kernel_len96x42_single_2d_single"}, "Solutions":[ {"sol_node_type":"SOL_KERNEL_ONLY","kernel_key":{"lengths":[ 96,42 ],"precision":"single","scheme":"CS_KERNEL_2D_SINGLE","sbrc_trans":"NONE","kernelConfig":{"use_3steps":false,"half_lds":false,"dir_reg":false,"buffer_inst":false,"tpb":1,"wgs":672,"tpt":[ 16,7 ],"factors":[ 4,4,6,6,7 ],"ebtype":"C2R_PRE","direction":1,"static_dim":3,"placement":"OP","iAryType":"CI","oAryType":"CI"}}} ]}, {"Problem":{"arch":"gfx90a","token":"100_dp_ip_complex"}, "Solutions":[ {"sol_node_type":"SOL_DUMMY","using_scheme":"CS_NONE","solution_childnodes":[ ]} ,{"sol_node_type":"SOL_INTERNAL_NODE","using_scheme":"CS_REAL_TRANSFORM_EVEN","solution_childnodes":[ {"child_token":"50_dp_ip_complex","child_option":1} ]} ,{"sol_node_type":"SOL_LEAF_NODE","using_scheme":"CS_KERNEL_STOCKHAM","solution_childnodes":[ {"child_token":"kernel_len100_double_sbrr","child_option":0} ]} ]}, {"Problem":{"arch":"gfx90a","token":"108_sp_ip_complex"}, "Solutions":[ {"sol_node_type":"SOL_DUMMY","using_scheme":"CS_NONE","solution_childnodes":[ ]} ,{"sol_node_type":"SOL_INTERNAL_NODE","using_scheme":"CS_REAL_TRANSFORM_EVEN","solution_childnodes":[ {"child_token":"54_sp_ip_complex","child_option":1} ]} ,{"sol_node_type":"SOL_INTERNAL_NODE","using_scheme":"CS_REAL_TRANSFORM_EVEN","solution_childnodes":[ {"child_token":"54_sp_ip_complex","child_option":3} ]} ,{"sol_node_type":"SOL_LEAF_NODE","using_scheme":"CS_KERNEL_STOCKHAM","solution_childnodes":[ {"child_token":"kernel_len108_single_sbrr","child_option":0} ]} ,{"sol_node_type":"SOL_LEAF_NODE","using_scheme":"CS_KERNEL_STOCKHAM","solution_childnodes":[ {"child_token":"kernel_len108_single_sbrr","child_option":1} ]} ]}, {"Problem":{"arch":"gfx90a","token":"112_sp_ip_complex"}, "Solutions":[ {"sol_node_type":"SOL_DUMMY","using_scheme":"CS_NONE","solution_childnodes":[ ]} ,{"sol_node_type":"SOL_INTERNAL_NODE","using_scheme":"CS_REAL_TRANSFORM_EVEN","solution_childnodes":[ {"child_token":"56_sp_ip_complex","child_option":1} ]} ,{"sol_node_type":"SOL_LEAF_NODE","using_scheme":"CS_KERNEL_STOCKHAM","solution_childnodes":[ {"child_token":"kernel_len112_single_sbrr","child_option":0} ]} ,{"sol_node_type":"SOL_LEAF_NODE","using_scheme":"CS_KERNEL_STOCKHAM","solution_childnodes":[ {"child_token":"kernel_len112_single_sbrr","child_option":1} ]} ]}, {"Problem":{"arch":"gfx90a","token":"125_sp_ip_complex"}, "Solutions":[ {"sol_node_type":"SOL_DUMMY","using_scheme":"CS_NONE","solution_childnodes":[ ]} ,{"sol_node_type":"SOL_LEAF_NODE","using_scheme":"CS_KERNEL_STOCKHAM","solution_childnodes":[ {"child_token":"kernel_len125_single_sbrr","child_option":0} ]} ,{"sol_node_type":"SOL_LEAF_NODE","using_scheme":"CS_KERNEL_STOCKHAM","solution_childnodes":[ {"child_token":"kernel_len125_single_sbrr","child_option":1} ]} ]}, {"Problem":{"arch":"gfx90a","token":"128_sp_ip_complex"}, "Solutions":[ {"sol_node_type":"SOL_DUMMY","using_scheme":"CS_NONE","solution_childnodes":[ ]} ,{"sol_node_type":"SOL_LEAF_NODE","using_scheme":"CS_KERNEL_STOCKHAM","solution_childnodes":[ {"child_token":"kernel_len128_single_sbrr","child_option":1} ]} ,{"sol_node_type":"SOL_INTERNAL_NODE","using_scheme":"CS_REAL_TRANSFORM_EVEN","solution_childnodes":[ {"child_token":"64_sp_ip_complex","child_option":1} ]} ,{"sol_node_type":"SOL_LEAF_NODE","using_scheme":"CS_KERNEL_STOCKHAM","solution_childnodes":[ {"child_token":"kernel_len128_single_sbrr","child_option":2} ]} ]}, {"Problem":{"arch":"gfx90a","token":"192_sp_ip_complex"}, "Solutions":[ {"sol_node_type":"SOL_DUMMY","using_scheme":"CS_NONE","solution_childnodes":[ ]} ,{"sol_node_type":"SOL_INTERNAL_NODE","using_scheme":"CS_REAL_TRANSFORM_EVEN","solution_childnodes":[ {"child_token":"96_sp_ip_complex","child_option":1} ]} ,{"sol_node_type":"SOL_INTERNAL_NODE","using_scheme":"CS_REAL_TRANSFORM_EVEN","solution_childnodes":[ {"child_token":"96_sp_ip_complex","child_option":3} ]} ,{"sol_node_type":"SOL_INTERNAL_NODE","using_scheme":"CS_REAL_TRANSFORM_EVEN","solution_childnodes":[ {"child_token":"96_sp_ip_complex","child_option":5} ]} ,{"sol_node_type":"SOL_LEAF_NODE","using_scheme":"CS_KERNEL_STOCKHAM","solution_childnodes":[ {"child_token":"kernel_len192_single_sbrr","child_option":0} ]} ,{"sol_node_type":"SOL_INTERNAL_NODE","using_scheme":"CS_REAL_TRANSFORM_EVEN","solution_childnodes":[ {"child_token":"96_sp_ip_complex","child_option":7} ]} ,{"sol_node_type":"SOL_LEAF_NODE","using_scheme":"CS_KERNEL_STOCKHAM","solution_childnodes":[ {"child_token":"kernel_len192_single_sbrr","child_option":1} ]} ,{"sol_node_type":"SOL_LEAF_NODE","using_scheme":"CS_KERNEL_STOCKHAM","solution_childnodes":[ {"child_token":"kernel_len192_single_sbrr","child_option":2} ]} ,{"sol_node_type":"SOL_LEAF_NODE","using_scheme":"CS_KERNEL_STOCKHAM","solution_childnodes":[ {"child_token":"kernel_len192_single_sbrr","child_option":3} ]} ,{"sol_node_type":"SOL_LEAF_NODE","using_scheme":"CS_KERNEL_STOCKHAM","solution_childnodes":[ {"child_token":"kernel_len192_single_sbrr","child_option":4} ]} ]}, {"Problem":{"arch":"gfx90a","token":"2187_sp_ip_complex"}, "Solutions":[ {"sol_node_type":"SOL_DUMMY","using_scheme":"CS_NONE","solution_childnodes":[ ]} ,{"sol_node_type":"SOL_LEAF_NODE","using_scheme":"CS_KERNEL_STOCKHAM","solution_childnodes":[ {"child_token":"kernel_len2187_single_sbrr","child_option":0} ]} ,{"sol_node_type":"SOL_LEAF_NODE","using_scheme":"CS_KERNEL_STOCKHAM","solution_childnodes":[ {"child_token":"kernel_len2187_single_sbrr","child_option":1} ]} ]}, {"Problem":{"arch":"gfx90a","token":"243_dp_ip_complex"}, "Solutions":[ {"sol_node_type":"SOL_DUMMY","using_scheme":"CS_NONE","solution_childnodes":[ ]} ,{"sol_node_type":"SOL_LEAF_NODE","using_scheme":"CS_KERNEL_STOCKHAM","solution_childnodes":[ {"child_token":"kernel_len243_double_sbrr","child_option":0} ]} ,{"sol_node_type":"SOL_LEAF_NODE","using_scheme":"CS_KERNEL_STOCKHAM","solution_childnodes":[ {"child_token":"kernel_len243_double_sbrr","child_option":1} ]} ,{"sol_node_type":"SOL_LEAF_NODE","using_scheme":"CS_KERNEL_STOCKHAM","solution_childnodes":[ {"child_token":"kernel_len243_double_sbrr","child_option":2} ]} ,{"sol_node_type":"SOL_LEAF_NODE","using_scheme":"CS_KERNEL_STOCKHAM","solution_childnodes":[ {"child_token":"kernel_len243_double_sbrr","child_option":3} ]} ,{"sol_node_type":"SOL_LEAF_NODE","using_scheme":"CS_KERNEL_STOCKHAM","solution_childnodes":[ {"child_token":"kernel_len243_double_sbrr","child_option":4} ]} ,{"sol_node_type":"SOL_LEAF_NODE","using_scheme":"CS_KERNEL_STOCKHAM","solution_childnodes":[ {"child_token":"kernel_len243_double_sbrr","child_option":5} ]} ]}, {"Problem":{"arch":"gfx90a","token":"243_sp_ip_complex"}, "Solutions":[ {"sol_node_type":"SOL_DUMMY","using_scheme":"CS_NONE","solution_childnodes":[ ]} ,{"sol_node_type":"SOL_LEAF_NODE","using_scheme":"CS_KERNEL_STOCKHAM","solution_childnodes":[ {"child_token":"kernel_len243_single_sbrr","child_option":0} ]} ,{"sol_node_type":"SOL_LEAF_NODE","using_scheme":"CS_KERNEL_STOCKHAM","solution_childnodes":[ {"child_token":"kernel_len243_single_sbrr","child_option":1} ]} ]}, {"Problem":{"arch":"gfx90a","token":"25_sp_ip_complex"}, "Solutions":[ {"sol_node_type":"SOL_DUMMY","using_scheme":"CS_NONE","solution_childnodes":[ ]} ,{"sol_node_type":"SOL_LEAF_NODE","using_scheme":"CS_KERNEL_STOCKHAM","solution_childnodes":[ {"child_token":"kernel_len25_single_sbrr","child_option":0} ]} ,{"sol_node_type":"SOL_LEAF_NODE","using_scheme":"CS_KERNEL_STOCKHAM","solution_childnodes":[ {"child_token":"kernel_len25_single_sbrr","child_option":1} ]} ]}, {"Problem":{"arch":"gfx90a","token":"26_dp_ip_complex"}, "Solutions":[ {"sol_node_type":"SOL_DUMMY","using_scheme":"CS_NONE","solution_childnodes":[ ]} ,{"sol_node_type":"SOL_LEAF_NODE","using_scheme":"CS_KERNEL_STOCKHAM","solution_childnodes":[ {"child_token":"kernel_len26_double_sbrr","child_option":0} ]} ,{"sol_node_type":"SOL_LEAF_NODE","using_scheme":"CS_KERNEL_STOCKHAM","solution_childnodes":[ {"child_token":"kernel_len26_double_sbrr","child_option":1} ]} ]}, {"Problem":{"arch":"gfx90a","token":"4096_sp_ip_complex"}, "Solutions":[ {"sol_node_type":"SOL_DUMMY","using_scheme":"CS_NONE","solution_childnodes":[ ]} ,{"sol_node_type":"SOL_LEAF_NODE","using_scheme":"CS_KERNEL_STOCKHAM","solution_childnodes":[ {"child_token":"kernel_len4096_single_sbrr","child_option":0} ]} ]}, {"Problem":{"arch":"gfx90a","token":"40_sp_ip_complex"}, "Solutions":[ {"sol_node_type":"SOL_DUMMY","using_scheme":"CS_NONE","solution_childnodes":[ ]} ,{"sol_node_type":"SOL_LEAF_NODE","using_scheme":"CS_KERNEL_STOCKHAM","solution_childnodes":[ {"child_token":"kernel_len40_single_sbrr","child_option":0} ]} ]}, {"Problem":{"arch":"gfx90a","token":"42_sp_ip_complex"}, "Solutions":[ {"sol_node_type":"SOL_DUMMY","using_scheme":"CS_NONE","solution_childnodes":[ ]} ,{"sol_node_type":"SOL_LEAF_NODE","using_scheme":"CS_KERNEL_STOCKHAM","solution_childnodes":[ {"child_token":"kernel_len42_single_sbrr","child_option":0} ]} ]}, {"Problem":{"arch":"gfx90a","token":"48_sp_ip_complex"}, "Solutions":[ {"sol_node_type":"SOL_DUMMY","using_scheme":"CS_NONE","solution_childnodes":[ ]} ,{"sol_node_type":"SOL_LEAF_NODE","using_scheme":"CS_KERNEL_STOCKHAM","solution_childnodes":[ {"child_token":"kernel_len48_single_sbrr","child_option":0} ]} ,{"sol_node_type":"SOL_LEAF_NODE","using_scheme":"CS_KERNEL_STOCKHAM","solution_childnodes":[ {"child_token":"kernel_len48_single_sbrr","child_option":1} ]} ,{"sol_node_type":"SOL_LEAF_NODE","using_scheme":"CS_KERNEL_STOCKHAM","solution_childnodes":[ {"child_token":"kernel_len48_single_sbrr","child_option":2} ]} ,{"sol_node_type":"SOL_LEAF_NODE","using_scheme":"CS_KERNEL_STOCKHAM","solution_childnodes":[ {"child_token":"kernel_len48_single_sbrr","child_option":3} ]} ]}, {"Problem":{"arch":"gfx90a","token":"50_dp_ip_complex"}, "Solutions":[ {"sol_node_type":"SOL_DUMMY","using_scheme":"CS_NONE","solution_childnodes":[ ]} ,{"sol_node_type":"SOL_LEAF_NODE","using_scheme":"CS_KERNEL_STOCKHAM","solution_childnodes":[ {"child_token":"kernel_len50_double_sbrr","child_option":0} ]} ,{"sol_node_type":"SOL_LEAF_NODE","using_scheme":"CS_KERNEL_STOCKHAM","solution_childnodes":[ {"child_token":"kernel_len50_double_sbrr","child_option":1} ]} ]}, {"Problem":{"arch":"gfx90a","token":"50_sp_ip_complex"}, "Solutions":[ {"sol_node_type":"SOL_DUMMY","using_scheme":"CS_NONE","solution_childnodes":[ ]} ,{"sol_node_type":"SOL_LEAF_NODE","using_scheme":"CS_KERNEL_STOCKHAM","solution_childnodes":[ {"child_token":"kernel_len50_single_sbrr","child_option":0} ]} ,{"sol_node_type":"SOL_LEAF_NODE","using_scheme":"CS_KERNEL_STOCKHAM","solution_childnodes":[ {"child_token":"kernel_len50_single_sbrr","child_option":1} ]} ,{"sol_node_type":"SOL_LEAF_NODE","using_scheme":"CS_KERNEL_STOCKHAM","solution_childnodes":[ {"child_token":"kernel_len50_single_sbrr","child_option":2} ]} ,{"sol_node_type":"SOL_LEAF_NODE","using_scheme":"CS_KERNEL_STOCKHAM","solution_childnodes":[ {"child_token":"kernel_len50_single_sbrr","child_option":3} ]} ,{"sol_node_type":"SOL_LEAF_NODE","using_scheme":"CS_KERNEL_STOCKHAM","solution_childnodes":[ {"child_token":"kernel_len50_single_sbrr","child_option":4} ]} ,{"sol_node_type":"SOL_LEAF_NODE","using_scheme":"CS_KERNEL_STOCKHAM","solution_childnodes":[ {"child_token":"kernel_len50_single_sbrr","child_option":5} ]} ]}, {"Problem":{"arch":"gfx90a","token":"52_dp_ip_complex"}, "Solutions":[ {"sol_node_type":"SOL_DUMMY","using_scheme":"CS_NONE","solution_childnodes":[ ]} ,{"sol_node_type":"SOL_INTERNAL_NODE","using_scheme":"CS_REAL_TRANSFORM_EVEN","solution_childnodes":[ {"child_token":"26_dp_ip_complex","child_option":1} ]} ,{"sol_node_type":"SOL_LEAF_NODE","using_scheme":"CS_KERNEL_STOCKHAM","solution_childnodes":[ {"child_token":"kernel_len52_double_sbrr","child_option":0} ]} ,{"sol_node_type":"SOL_LEAF_NODE","using_scheme":"CS_KERNEL_STOCKHAM","solution_childnodes":[ {"child_token":"kernel_len52_double_sbrr","child_option":1} ]} ]}, {"Problem":{"arch":"gfx90a","token":"52_sp_ip_complex"}, "Solutions":[ {"sol_node_type":"SOL_DUMMY","using_scheme":"CS_NONE","solution_childnodes":[ ]} ,{"sol_node_type":"SOL_LEAF_NODE","using_scheme":"CS_KERNEL_STOCKHAM","solution_childnodes":[ {"child_token":"kernel_len52_single_sbrr","child_option":0} ]} ]}, {"Problem":{"arch":"gfx90a","token":"54_sp_ip_complex"}, "Solutions":[ {"sol_node_type":"SOL_DUMMY","using_scheme":"CS_NONE","solution_childnodes":[ ]} ,{"sol_node_type":"SOL_LEAF_NODE","using_scheme":"CS_KERNEL_STOCKHAM","solution_childnodes":[ {"child_token":"kernel_len54_single_sbrr","child_option":0} ]} ,{"sol_node_type":"SOL_LEAF_NODE","using_scheme":"CS_KERNEL_STOCKHAM","solution_childnodes":[ {"child_token":"kernel_len54_single_sbrr","child_option":1} ]} ,{"sol_node_type":"SOL_LEAF_NODE","using_scheme":"CS_KERNEL_STOCKHAM","solution_childnodes":[ {"child_token":"kernel_len54_single_sbrr","child_option":2} ]} ,{"sol_node_type":"SOL_LEAF_NODE","using_scheme":"CS_KERNEL_STOCKHAM","solution_childnodes":[ {"child_token":"kernel_len54_single_sbrr","child_option":3} ]} ]}, {"Problem":{"arch":"gfx90a","token":"55_dp_ip_complex"}, "Solutions":[ {"sol_node_type":"SOL_DUMMY","using_scheme":"CS_NONE","solution_childnodes":[ ]} ,{"sol_node_type":"SOL_LEAF_NODE","using_scheme":"CS_KERNEL_STOCKHAM","solution_childnodes":[ {"child_token":"kernel_len55_double_sbrr","child_option":0} ]} ,{"sol_node_type":"SOL_LEAF_NODE","using_scheme":"CS_KERNEL_STOCKHAM","solution_childnodes":[ {"child_token":"kernel_len55_double_sbrr","child_option":1} ]} ,{"sol_node_type":"SOL_LEAF_NODE","using_scheme":"CS_KERNEL_STOCKHAM","solution_childnodes":[ {"child_token":"kernel_len55_double_sbrr","child_option":2} ]} ]}, {"Problem":{"arch":"gfx90a","token":"55_sp_ip_complex"}, "Solutions":[ {"sol_node_type":"SOL_DUMMY","using_scheme":"CS_NONE","solution_childnodes":[ ]} ,{"sol_node_type":"SOL_INTERNAL_NODE","using_scheme":"CS_REAL_TRANSFORM_EVEN","solution_childnodes":[ {"child_token":"54_sp_ip_complex","child_option":2} ]} ,{"sol_node_type":"SOL_INTERNAL_NODE","using_scheme":"CS_REAL_TRANSFORM_EVEN","solution_childnodes":[ {"child_token":"54_sp_ip_complex","child_option":4} ]} ,{"sol_node_type":"SOL_LEAF_NODE","using_scheme":"CS_KERNEL_STOCKHAM","solution_childnodes":[ {"child_token":"kernel_len55_single_sbrr","child_option":0} ]} ,{"sol_node_type":"SOL_LEAF_NODE","using_scheme":"CS_KERNEL_STOCKHAM","solution_childnodes":[ {"child_token":"kernel_len55_single_sbrr","child_option":1} ]} ,{"sol_node_type":"SOL_LEAF_NODE","using_scheme":"CS_KERNEL_STOCKHAM","solution_childnodes":[ {"child_token":"kernel_len55_single_sbrr","child_option":2} ]} ,{"sol_node_type":"SOL_LEAF_NODE","using_scheme":"CS_KERNEL_STOCKHAM","solution_childnodes":[ {"child_token":"kernel_len55_single_sbrr","child_option":3} ]} ]}, {"Problem":{"arch":"gfx90a","token":"56_dp_ip_complex"}, "Solutions":[ {"sol_node_type":"SOL_DUMMY","using_scheme":"CS_NONE","solution_childnodes":[ ]} ,{"sol_node_type":"SOL_LEAF_NODE","using_scheme":"CS_KERNEL_STOCKHAM","solution_childnodes":[ {"child_token":"kernel_len56_double_sbrr","child_option":0} ]} ,{"sol_node_type":"SOL_LEAF_NODE","using_scheme":"CS_KERNEL_STOCKHAM","solution_childnodes":[ {"child_token":"kernel_len56_double_sbrr","child_option":1} ]} ,{"sol_node_type":"SOL_LEAF_NODE","using_scheme":"CS_KERNEL_STOCKHAM","solution_childnodes":[ {"child_token":"kernel_len56_double_sbrr","child_option":2} ]} ,{"sol_node_type":"SOL_LEAF_NODE","using_scheme":"CS_KERNEL_STOCKHAM","solution_childnodes":[ {"child_token":"kernel_len56_double_sbrr","child_option":3} ]} ]}, {"Problem":{"arch":"gfx90a","token":"56_sp_ip_complex"}, "Solutions":[ {"sol_node_type":"SOL_DUMMY","using_scheme":"CS_NONE","solution_childnodes":[ ]} ,{"sol_node_type":"SOL_LEAF_NODE","using_scheme":"CS_KERNEL_STOCKHAM","solution_childnodes":[ {"child_token":"kernel_len56_single_sbrr","child_option":0} ]} ,{"sol_node_type":"SOL_LEAF_NODE","using_scheme":"CS_KERNEL_STOCKHAM","solution_childnodes":[ {"child_token":"kernel_len56_single_sbrr","child_option":1} ]} ]}, {"Problem":{"arch":"gfx90a","token":"64_sp_ip_complex"}, "Solutions":[ {"sol_node_type":"SOL_DUMMY","using_scheme":"CS_NONE","solution_childnodes":[ ]} ,{"sol_node_type":"SOL_LEAF_NODE","using_scheme":"CS_KERNEL_STOCKHAM","solution_childnodes":[ {"child_token":"kernel_len64_single_sbrr","child_option":0} ]} ,{"sol_node_type":"SOL_LEAF_NODE","using_scheme":"CS_KERNEL_STOCKHAM","solution_childnodes":[ {"child_token":"kernel_len64_single_sbrr","child_option":1} ]} ]}, {"Problem":{"arch":"gfx90a","token":"72_sp_ip_complex"}, "Solutions":[ {"sol_node_type":"SOL_DUMMY","using_scheme":"CS_NONE","solution_childnodes":[ ]} ,{"sol_node_type":"SOL_LEAF_NODE","using_scheme":"CS_KERNEL_STOCKHAM","solution_childnodes":[ {"child_token":"kernel_len72_single_sbrr","child_option":0} ]} ,{"sol_node_type":"SOL_LEAF_NODE","using_scheme":"CS_KERNEL_STOCKHAM","solution_childnodes":[ {"child_token":"kernel_len72_single_sbrr","child_option":1} ]} ]}, {"Problem":{"arch":"gfx90a","token":"75_dp_ip_complex"}, "Solutions":[ {"sol_node_type":"SOL_DUMMY","using_scheme":"CS_NONE","solution_childnodes":[ ]} ,{"sol_node_type":"SOL_LEAF_NODE","using_scheme":"CS_KERNEL_STOCKHAM","solution_childnodes":[ {"child_token":"kernel_len75_double_sbrr","child_option":0} ]} ]}, {"Problem":{"arch":"gfx90a","token":"75_sp_ip_complex"}, "Solutions":[ {"sol_node_type":"SOL_DUMMY","using_scheme":"CS_NONE","solution_childnodes":[ ]} ,{"sol_node_type":"SOL_LEAF_NODE","using_scheme":"CS_KERNEL_STOCKHAM","solution_childnodes":[ {"child_token":"kernel_len75_single_sbrr","child_option":0} ]} ]}, {"Problem":{"arch":"gfx90a","token":"81_dp_ip_complex"}, "Solutions":[ {"sol_node_type":"SOL_DUMMY","using_scheme":"CS_NONE","solution_childnodes":[ ]} ,{"sol_node_type":"SOL_LEAF_NODE","using_scheme":"CS_KERNEL_STOCKHAM","solution_childnodes":[ {"child_token":"kernel_len81_double_sbrr","child_option":0} ]} ]}, {"Problem":{"arch":"gfx90a","token":"84_sp_ip_complex"}, "Solutions":[ {"sol_node_type":"SOL_DUMMY","using_scheme":"CS_NONE","solution_childnodes":[ ]} ,{"sol_node_type":"SOL_LEAF_NODE","using_scheme":"CS_KERNEL_STOCKHAM","solution_childnodes":[ {"child_token":"kernel_len84_single_sbrr","child_option":0} ]} ,{"sol_node_type":"SOL_LEAF_NODE","using_scheme":"CS_KERNEL_STOCKHAM","solution_childnodes":[ {"child_token":"kernel_len84_single_sbrr","child_option":1} ]} ,{"sol_node_type":"SOL_LEAF_NODE","using_scheme":"CS_KERNEL_STOCKHAM","solution_childnodes":[ {"child_token":"kernel_len84_single_sbrr","child_option":2} ]} ,{"sol_node_type":"SOL_LEAF_NODE","using_scheme":"CS_KERNEL_STOCKHAM","solution_childnodes":[ {"child_token":"kernel_len84_single_sbrr","child_option":3} ]} ]}, {"Problem":{"arch":"gfx90a","token":"sbcc_100_dp_ip_complex"}, "Solutions":[ {"sol_node_type":"SOL_LEAF_NODE","using_scheme":"CS_KERNEL_STOCKHAM_BLOCK_CC","solution_childnodes":[ {"child_token":"kernel_len100_double_sbcc","child_option":0} ]} ,{"sol_node_type":"SOL_LEAF_NODE","using_scheme":"CS_KERNEL_STOCKHAM_BLOCK_CC","solution_childnodes":[ {"child_token":"kernel_len100_double_sbcc","child_option":1} ]} ,{"sol_node_type":"SOL_LEAF_NODE","using_scheme":"CS_KERNEL_STOCKHAM_BLOCK_CC","solution_childnodes":[ {"child_token":"kernel_len100_double_sbcc","child_option":2} ]} ,{"sol_node_type":"SOL_LEAF_NODE","using_scheme":"CS_KERNEL_STOCKHAM_BLOCK_CC","solution_childnodes":[ {"child_token":"kernel_len100_double_sbcc","child_option":3} ]} ,{"sol_node_type":"SOL_LEAF_NODE","using_scheme":"CS_KERNEL_STOCKHAM_BLOCK_CC","solution_childnodes":[ {"child_token":"kernel_len100_double_sbcc","child_option":4} ]} ]}, {"Problem":{"arch":"gfx90a","token":"sbcc_100_sp_ip_complex"}, "Solutions":[ {"sol_node_type":"SOL_LEAF_NODE","using_scheme":"CS_KERNEL_STOCKHAM_BLOCK_CC","solution_childnodes":[ {"child_token":"kernel_len100_single_sbcc","child_option":0} ]} ,{"sol_node_type":"SOL_LEAF_NODE","using_scheme":"CS_KERNEL_STOCKHAM_BLOCK_CC","solution_childnodes":[ {"child_token":"kernel_len100_single_sbcc","child_option":1} ]} ,{"sol_node_type":"SOL_LEAF_NODE","using_scheme":"CS_KERNEL_STOCKHAM_BLOCK_CC","solution_childnodes":[ {"child_token":"kernel_len100_single_sbcc","child_option":2} ]} ,{"sol_node_type":"SOL_LEAF_NODE","using_scheme":"CS_KERNEL_STOCKHAM_BLOCK_CC","solution_childnodes":[ {"child_token":"kernel_len100_single_sbcc","child_option":3} ]} ]}, {"Problem":{"arch":"gfx90a","token":"sbcc_104_dp_ip_complex"}, "Solutions":[ {"sol_node_type":"SOL_LEAF_NODE","using_scheme":"CS_KERNEL_STOCKHAM_BLOCK_CC","solution_childnodes":[ {"child_token":"kernel_len104_double_sbcc","child_option":0} ]} ,{"sol_node_type":"SOL_LEAF_NODE","using_scheme":"CS_KERNEL_STOCKHAM_BLOCK_CC","solution_childnodes":[ {"child_token":"kernel_len104_double_sbcc","child_option":1} ]} ]}, {"Problem":{"arch":"gfx90a","token":"sbcc_104_sp_ip_complex"}, "Solutions":[ {"sol_node_type":"SOL_LEAF_NODE","using_scheme":"CS_KERNEL_STOCKHAM_BLOCK_CC","solution_childnodes":[ {"child_token":"kernel_len104_single_sbcc","child_option":0} ]} ,{"sol_node_type":"SOL_LEAF_NODE","using_scheme":"CS_KERNEL_STOCKHAM_BLOCK_CC","solution_childnodes":[ {"child_token":"kernel_len104_single_sbcc","child_option":1} ]} ]}, {"Problem":{"arch":"gfx90a","token":"sbcc_108_sp_ip_complex"}, "Solutions":[ {"sol_node_type":"SOL_LEAF_NODE","using_scheme":"CS_KERNEL_STOCKHAM_BLOCK_CC","solution_childnodes":[ {"child_token":"kernel_len108_single_sbcc","child_option":0} ]} ,{"sol_node_type":"SOL_LEAF_NODE","using_scheme":"CS_KERNEL_STOCKHAM_BLOCK_CC","solution_childnodes":[ {"child_token":"kernel_len108_single_sbcc","child_option":1} ]} ,{"sol_node_type":"SOL_LEAF_NODE","using_scheme":"CS_KERNEL_STOCKHAM_BLOCK_CC","solution_childnodes":[ {"child_token":"kernel_len108_single_sbcc","child_option":2} ]} ,{"sol_node_type":"SOL_LEAF_NODE","using_scheme":"CS_KERNEL_STOCKHAM_BLOCK_CC","solution_childnodes":[ {"child_token":"kernel_len108_single_sbcc","child_option":3} ]} ,{"sol_node_type":"SOL_LEAF_NODE","using_scheme":"CS_KERNEL_STOCKHAM_BLOCK_CC","solution_childnodes":[ {"child_token":"kernel_len108_single_sbcc","child_option":4} ]} ,{"sol_node_type":"SOL_LEAF_NODE","using_scheme":"CS_KERNEL_STOCKHAM_BLOCK_CC","solution_childnodes":[ {"child_token":"kernel_len108_single_sbcc","child_option":5} ]} ,{"sol_node_type":"SOL_LEAF_NODE","using_scheme":"CS_KERNEL_STOCKHAM_BLOCK_CC","solution_childnodes":[ {"child_token":"kernel_len108_single_sbcc","child_option":6} ]} ]}, {"Problem":{"arch":"gfx90a","token":"sbcc_112_sp_ip_complex"}, "Solutions":[ {"sol_node_type":"SOL_LEAF_NODE","using_scheme":"CS_KERNEL_STOCKHAM_BLOCK_CC","solution_childnodes":[ {"child_token":"kernel_len112_single_sbcc","child_option":0} ]} ,{"sol_node_type":"SOL_LEAF_NODE","using_scheme":"CS_KERNEL_STOCKHAM_BLOCK_CC","solution_childnodes":[ {"child_token":"kernel_len112_single_sbcc","child_option":1} ]} ,{"sol_node_type":"SOL_LEAF_NODE","using_scheme":"CS_KERNEL_STOCKHAM_BLOCK_CC","solution_childnodes":[ {"child_token":"kernel_len112_single_sbcc","child_option":2} ]} ]}, {"Problem":{"arch":"gfx90a","token":"sbcc_125_dp_ip_complex"}, "Solutions":[ {"sol_node_type":"SOL_LEAF_NODE","using_scheme":"CS_KERNEL_STOCKHAM_BLOCK_CC","solution_childnodes":[ {"child_token":"kernel_len125_double_sbcc","child_option":0} ]} ,{"sol_node_type":"SOL_LEAF_NODE","using_scheme":"CS_KERNEL_STOCKHAM_BLOCK_CC","solution_childnodes":[ {"child_token":"kernel_len125_double_sbcc","child_option":1} ]} ]}, {"Problem":{"arch":"gfx90a","token":"sbcc_125_sp_ip_complex"}, "Solutions":[ {"sol_node_type":"SOL_LEAF_NODE","using_scheme":"CS_KERNEL_STOCKHAM_BLOCK_CC","solution_childnodes":[ {"child_token":"kernel_len125_single_sbcc","child_option":0} ]} ,{"sol_node_type":"SOL_LEAF_NODE","using_scheme":"CS_KERNEL_STOCKHAM_BLOCK_CC","solution_childnodes":[ {"child_token":"kernel_len125_single_sbcc","child_option":1} ]} ,{"sol_node_type":"SOL_LEAF_NODE","using_scheme":"CS_KERNEL_STOCKHAM_BLOCK_CC","solution_childnodes":[ {"child_token":"kernel_len125_single_sbcc","child_option":2} ]} ]}, {"Problem":{"arch":"gfx90a","token":"sbcc_128_sp_ip_complex"}, "Solutions":[ {"sol_node_type":"SOL_LEAF_NODE","using_scheme":"CS_KERNEL_STOCKHAM_BLOCK_CC","solution_childnodes":[ {"child_token":"kernel_len128_single_sbcc","child_option":0} ]} ,{"sol_node_type":"SOL_LEAF_NODE","using_scheme":"CS_KERNEL_STOCKHAM_BLOCK_CC","solution_childnodes":[ {"child_token":"kernel_len128_single_sbcc","child_option":1} ]} ,{"sol_node_type":"SOL_LEAF_NODE","using_scheme":"CS_KERNEL_STOCKHAM_BLOCK_CC","solution_childnodes":[ {"child_token":"kernel_len128_single_sbcc","child_option":2} ]} ,{"sol_node_type":"SOL_LEAF_NODE","using_scheme":"CS_KERNEL_STOCKHAM_BLOCK_CC","solution_childnodes":[ {"child_token":"kernel_len128_single_sbcc","child_option":3} ]} ]}, {"Problem":{"arch":"gfx90a","token":"sbcc_160_sp_ip_complex"}, "Solutions":[ {"sol_node_type":"SOL_LEAF_NODE","using_scheme":"CS_KERNEL_STOCKHAM_BLOCK_CC","solution_childnodes":[ {"child_token":"kernel_len160_single_sbcc","child_option":0} ]} ,{"sol_node_type":"SOL_LEAF_NODE","using_scheme":"CS_KERNEL_STOCKHAM_BLOCK_CC","solution_childnodes":[ {"child_token":"kernel_len160_single_sbcc","child_option":1} ]} ,{"sol_node_type":"SOL_LEAF_NODE","using_scheme":"CS_KERNEL_STOCKHAM_BLOCK_CC","solution_childnodes":[ {"child_token":"kernel_len160_single_sbcc","child_option":2} ]} ,{"sol_node_type":"SOL_LEAF_NODE","using_scheme":"CS_KERNEL_STOCKHAM_BLOCK_CC","solution_childnodes":[ {"child_token":"kernel_len160_single_sbcc","child_option":3} ]} ,{"sol_node_type":"SOL_LEAF_NODE","using_scheme":"CS_KERNEL_STOCKHAM_BLOCK_CC","solution_childnodes":[ {"child_token":"kernel_len160_single_sbcc","child_option":4} ]} ,{"sol_node_type":"SOL_LEAF_NODE","using_scheme":"CS_KERNEL_STOCKHAM_BLOCK_CC","solution_childnodes":[ {"child_token":"kernel_len160_single_sbcc","child_option":5} ]} ]}, {"Problem":{"arch":"gfx90a","token":"sbcc_168_dp_ip_complex"}, "Solutions":[ {"sol_node_type":"SOL_LEAF_NODE","using_scheme":"CS_KERNEL_STOCKHAM_BLOCK_CC","solution_childnodes":[ {"child_token":"kernel_len168_double_sbcc","child_option":0} ]} ,{"sol_node_type":"SOL_LEAF_NODE","using_scheme":"CS_KERNEL_STOCKHAM_BLOCK_CC","solution_childnodes":[ {"child_token":"kernel_len168_double_sbcc","child_option":1} ]} ,{"sol_node_type":"SOL_LEAF_NODE","using_scheme":"CS_KERNEL_STOCKHAM_BLOCK_CC","solution_childnodes":[ {"child_token":"kernel_len168_double_sbcc","child_option":2} ]} ]}, {"Problem":{"arch":"gfx90a","token":"sbcc_168_sp_ip_complex"}, "Solutions":[ {"sol_node_type":"SOL_LEAF_NODE","using_scheme":"CS_KERNEL_STOCKHAM_BLOCK_CC","solution_childnodes":[ {"child_token":"kernel_len168_single_sbcc","child_option":0} ]} ,{"sol_node_type":"SOL_LEAF_NODE","using_scheme":"CS_KERNEL_STOCKHAM_BLOCK_CC","solution_childnodes":[ {"child_token":"kernel_len168_single_sbcc","child_option":1} ]} ,{"sol_node_type":"SOL_LEAF_NODE","using_scheme":"CS_KERNEL_STOCKHAM_BLOCK_CC","solution_childnodes":[ {"child_token":"kernel_len168_single_sbcc","child_option":2} ]} ,{"sol_node_type":"SOL_LEAF_NODE","using_scheme":"CS_KERNEL_STOCKHAM_BLOCK_CC","solution_childnodes":[ {"child_token":"kernel_len168_single_sbcc","child_option":3} ]} ,{"sol_node_type":"SOL_LEAF_NODE","using_scheme":"CS_KERNEL_STOCKHAM_BLOCK_CC","solution_childnodes":[ {"child_token":"kernel_len168_single_sbcc","child_option":4} ]} ,{"sol_node_type":"SOL_LEAF_NODE","using_scheme":"CS_KERNEL_STOCKHAM_BLOCK_CC","solution_childnodes":[ {"child_token":"kernel_len168_single_sbcc","child_option":5} ]} ,{"sol_node_type":"SOL_LEAF_NODE","using_scheme":"CS_KERNEL_STOCKHAM_BLOCK_CC","solution_childnodes":[ {"child_token":"kernel_len168_single_sbcc","child_option":6} ]} ,{"sol_node_type":"SOL_LEAF_NODE","using_scheme":"CS_KERNEL_STOCKHAM_BLOCK_CC","solution_childnodes":[ {"child_token":"kernel_len168_single_sbcc","child_option":7} ]} ,{"sol_node_type":"SOL_LEAF_NODE","using_scheme":"CS_KERNEL_STOCKHAM_BLOCK_CC","solution_childnodes":[ {"child_token":"kernel_len168_single_sbcc","child_option":8} ]} ,{"sol_node_type":"SOL_LEAF_NODE","using_scheme":"CS_KERNEL_STOCKHAM_BLOCK_CC","solution_childnodes":[ {"child_token":"kernel_len168_single_sbcc","child_option":9} ]} ]}, {"Problem":{"arch":"gfx90a","token":"sbcc_192_sp_ip_complex"}, "Solutions":[ {"sol_node_type":"SOL_LEAF_NODE","using_scheme":"CS_KERNEL_STOCKHAM_BLOCK_CC","solution_childnodes":[ {"child_token":"kernel_len192_single_sbcc","child_option":0} ]} ,{"sol_node_type":"SOL_LEAF_NODE","using_scheme":"CS_KERNEL_STOCKHAM_BLOCK_CC","solution_childnodes":[ {"child_token":"kernel_len192_single_sbcc","child_option":1} ]} ,{"sol_node_type":"SOL_LEAF_NODE","using_scheme":"CS_KERNEL_STOCKHAM_BLOCK_CC","solution_childnodes":[ {"child_token":"kernel_len192_single_sbcc","child_option":2} ]} ,{"sol_node_type":"SOL_LEAF_NODE","using_scheme":"CS_KERNEL_STOCKHAM_BLOCK_CC","solution_childnodes":[ {"child_token":"kernel_len192_single_sbcc","child_option":3} ]} ,{"sol_node_type":"SOL_LEAF_NODE","using_scheme":"CS_KERNEL_STOCKHAM_BLOCK_CC","solution_childnodes":[ {"child_token":"kernel_len192_single_sbcc","child_option":4} ]} ,{"sol_node_type":"SOL_LEAF_NODE","using_scheme":"CS_KERNEL_STOCKHAM_BLOCK_CC","solution_childnodes":[ {"child_token":"kernel_len192_single_sbcc","child_option":5} ]} ,{"sol_node_type":"SOL_LEAF_NODE","using_scheme":"CS_KERNEL_STOCKHAM_BLOCK_CC","solution_childnodes":[ {"child_token":"kernel_len192_single_sbcc","child_option":6} ]} ,{"sol_node_type":"SOL_LEAF_NODE","using_scheme":"CS_KERNEL_STOCKHAM_BLOCK_CC","solution_childnodes":[ {"child_token":"kernel_len192_single_sbcc","child_option":7} ]} ,{"sol_node_type":"SOL_LEAF_NODE","using_scheme":"CS_KERNEL_STOCKHAM_BLOCK_CC","solution_childnodes":[ {"child_token":"kernel_len192_single_sbcc","child_option":8} ]} ,{"sol_node_type":"SOL_LEAF_NODE","using_scheme":"CS_KERNEL_STOCKHAM_BLOCK_CC","solution_childnodes":[ {"child_token":"kernel_len192_single_sbcc","child_option":9} ]} ,{"sol_node_type":"SOL_LEAF_NODE","using_scheme":"CS_KERNEL_STOCKHAM_BLOCK_CC","solution_childnodes":[ {"child_token":"kernel_len192_single_sbcc","child_option":10} ]} ]}, {"Problem":{"arch":"gfx90a","token":"sbcc_200_dp_ip_complex"}, "Solutions":[ {"sol_node_type":"SOL_LEAF_NODE","using_scheme":"CS_KERNEL_STOCKHAM_BLOCK_CC","solution_childnodes":[ {"child_token":"kernel_len200_double_sbcc","child_option":0} ]} ]}, {"Problem":{"arch":"gfx90a","token":"sbcc_200_sp_ip_complex"}, "Solutions":[ {"sol_node_type":"SOL_LEAF_NODE","using_scheme":"CS_KERNEL_STOCKHAM_BLOCK_CC","solution_childnodes":[ {"child_token":"kernel_len200_single_sbcc","child_option":0} ]} ,{"sol_node_type":"SOL_LEAF_NODE","using_scheme":"CS_KERNEL_STOCKHAM_BLOCK_CC","solution_childnodes":[ {"child_token":"kernel_len200_single_sbcc","child_option":1} ]} ,{"sol_node_type":"SOL_LEAF_NODE","using_scheme":"CS_KERNEL_STOCKHAM_BLOCK_CC","solution_childnodes":[ {"child_token":"kernel_len200_single_sbcc","child_option":2} ]} ,{"sol_node_type":"SOL_LEAF_NODE","using_scheme":"CS_KERNEL_STOCKHAM_BLOCK_CC","solution_childnodes":[ {"child_token":"kernel_len200_single_sbcc","child_option":3} ]} ]}, {"Problem":{"arch":"gfx90a","token":"sbcc_208_dp_ip_complex"}, "Solutions":[ {"sol_node_type":"SOL_LEAF_NODE","using_scheme":"CS_KERNEL_STOCKHAM_BLOCK_CC","solution_childnodes":[ {"child_token":"kernel_len208_double_sbcc","child_option":0} ]} ,{"sol_node_type":"SOL_LEAF_NODE","using_scheme":"CS_KERNEL_STOCKHAM_BLOCK_CC","solution_childnodes":[ {"child_token":"kernel_len208_double_sbcc","child_option":1} ]} ]}, {"Problem":{"arch":"gfx90a","token":"sbcc_208_sp_ip_complex"}, "Solutions":[ {"sol_node_type":"SOL_LEAF_NODE","using_scheme":"CS_KERNEL_STOCKHAM_BLOCK_CC","solution_childnodes":[ {"child_token":"kernel_len208_single_sbcc","child_option":0} ]} ,{"sol_node_type":"SOL_LEAF_NODE","using_scheme":"CS_KERNEL_STOCKHAM_BLOCK_CC","solution_childnodes":[ {"child_token":"kernel_len208_single_sbcc","child_option":1} ]} ,{"sol_node_type":"SOL_LEAF_NODE","using_scheme":"CS_KERNEL_STOCKHAM_BLOCK_CC","solution_childnodes":[ {"child_token":"kernel_len208_single_sbcc","child_option":2} ]} ]}, {"Problem":{"arch":"gfx90a","token":"sbcc_216_sp_ip_complex"}, "Solutions":[ {"sol_node_type":"SOL_LEAF_NODE","using_scheme":"CS_KERNEL_STOCKHAM_BLOCK_CC","solution_childnodes":[ {"child_token":"kernel_len216_single_sbcc","child_option":0} ]} ,{"sol_node_type":"SOL_LEAF_NODE","using_scheme":"CS_KERNEL_STOCKHAM_BLOCK_CC","solution_childnodes":[ {"child_token":"kernel_len216_single_sbcc","child_option":1} ]} ,{"sol_node_type":"SOL_LEAF_NODE","using_scheme":"CS_KERNEL_STOCKHAM_BLOCK_CC","solution_childnodes":[ {"child_token":"kernel_len216_single_sbcc","child_option":2} ]} ,{"sol_node_type":"SOL_LEAF_NODE","using_scheme":"CS_KERNEL_STOCKHAM_BLOCK_CC","solution_childnodes":[ {"child_token":"kernel_len216_single_sbcc","child_option":3} ]} ,{"sol_node_type":"SOL_LEAF_NODE","using_scheme":"CS_KERNEL_STOCKHAM_BLOCK_CC","solution_childnodes":[ {"child_token":"kernel_len216_single_sbcc","child_option":4} ]} ,{"sol_node_type":"SOL_LEAF_NODE","using_scheme":"CS_KERNEL_STOCKHAM_BLOCK_CC","solution_childnodes":[ {"child_token":"kernel_len216_single_sbcc","child_option":5} ]} ]}, {"Problem":{"arch":"gfx90a","token":"sbcc_224_dp_ip_complex"}, "Solutions":[ {"sol_node_type":"SOL_LEAF_NODE","using_scheme":"CS_KERNEL_STOCKHAM_BLOCK_CC","solution_childnodes":[ {"child_token":"kernel_len224_double_sbcc","child_option":0} ]} ]}, {"Problem":{"arch":"gfx90a","token":"sbcc_224_sp_ip_complex"}, "Solutions":[ {"sol_node_type":"SOL_LEAF_NODE","using_scheme":"CS_KERNEL_STOCKHAM_BLOCK_CC","solution_childnodes":[ {"child_token":"kernel_len224_single_sbcc","child_option":0} ]} ,{"sol_node_type":"SOL_LEAF_NODE","using_scheme":"CS_KERNEL_STOCKHAM_BLOCK_CC","solution_childnodes":[ {"child_token":"kernel_len224_single_sbcc","child_option":1} ]} ,{"sol_node_type":"SOL_LEAF_NODE","using_scheme":"CS_KERNEL_STOCKHAM_BLOCK_CC","solution_childnodes":[ {"child_token":"kernel_len224_single_sbcc","child_option":2} ]} ,{"sol_node_type":"SOL_LEAF_NODE","using_scheme":"CS_KERNEL_STOCKHAM_BLOCK_CC","solution_childnodes":[ {"child_token":"kernel_len224_single_sbcc","child_option":3} ]} ,{"sol_node_type":"SOL_LEAF_NODE","using_scheme":"CS_KERNEL_STOCKHAM_BLOCK_CC","solution_childnodes":[ {"child_token":"kernel_len224_single_sbcc","child_option":4} ]} ]}, {"Problem":{"arch":"gfx90a","token":"sbcc_240_sp_ip_complex"}, "Solutions":[ {"sol_node_type":"SOL_LEAF_NODE","using_scheme":"CS_KERNEL_STOCKHAM_BLOCK_CC","solution_childnodes":[ {"child_token":"kernel_len240_single_sbcc","child_option":0} ]} ,{"sol_node_type":"SOL_LEAF_NODE","using_scheme":"CS_KERNEL_STOCKHAM_BLOCK_CC","solution_childnodes":[ {"child_token":"kernel_len240_single_sbcc","child_option":1} ]} ,{"sol_node_type":"SOL_LEAF_NODE","using_scheme":"CS_KERNEL_STOCKHAM_BLOCK_CC","solution_childnodes":[ {"child_token":"kernel_len240_single_sbcc","child_option":2} ]} ,{"sol_node_type":"SOL_LEAF_NODE","using_scheme":"CS_KERNEL_STOCKHAM_BLOCK_CC","solution_childnodes":[ {"child_token":"kernel_len240_single_sbcc","child_option":3} ]} ,{"sol_node_type":"SOL_LEAF_NODE","using_scheme":"CS_KERNEL_STOCKHAM_BLOCK_CC","solution_childnodes":[ {"child_token":"kernel_len240_single_sbcc","child_option":4} ]} ,{"sol_node_type":"SOL_LEAF_NODE","using_scheme":"CS_KERNEL_STOCKHAM_BLOCK_CC","solution_childnodes":[ {"child_token":"kernel_len240_single_sbcc","child_option":5} ]} ,{"sol_node_type":"SOL_LEAF_NODE","using_scheme":"CS_KERNEL_STOCKHAM_BLOCK_CC","solution_childnodes":[ {"child_token":"kernel_len240_single_sbcc","child_option":6} ]} ]}, {"Problem":{"arch":"gfx90a","token":"sbcc_243_dp_ip_complex"}, "Solutions":[ {"sol_node_type":"SOL_LEAF_NODE","using_scheme":"CS_KERNEL_STOCKHAM_BLOCK_CC","solution_childnodes":[ {"child_token":"kernel_len243_double_sbcc","child_option":0} ]} ,{"sol_node_type":"SOL_LEAF_NODE","using_scheme":"CS_KERNEL_STOCKHAM_BLOCK_CC","solution_childnodes":[ {"child_token":"kernel_len243_double_sbcc","child_option":1} ]} ,{"sol_node_type":"SOL_LEAF_NODE","using_scheme":"CS_KERNEL_STOCKHAM_BLOCK_CC","solution_childnodes":[ {"child_token":"kernel_len243_double_sbcc","child_option":2} ]} ]}, {"Problem":{"arch":"gfx90a","token":"sbcc_243_sp_ip_complex"}, "Solutions":[ {"sol_node_type":"SOL_LEAF_NODE","using_scheme":"CS_KERNEL_STOCKHAM_BLOCK_CC","solution_childnodes":[ {"child_token":"kernel_len243_single_sbcc","child_option":0} ]} ]}, {"Problem":{"arch":"gfx90a","token":"sbcc_280_sp_ip_complex"}, "Solutions":[ {"sol_node_type":"SOL_LEAF_NODE","using_scheme":"CS_KERNEL_STOCKHAM_BLOCK_CC","solution_childnodes":[ {"child_token":"kernel_len280_single_sbcc","child_option":0} ]} ,{"sol_node_type":"SOL_LEAF_NODE","using_scheme":"CS_KERNEL_STOCKHAM_BLOCK_CC","solution_childnodes":[ {"child_token":"kernel_len280_single_sbcc","child_option":1} ]} ]}, {"Problem":{"arch":"gfx90a","token":"sbcc_336_dp_ip_complex"}, "Solutions":[ {"sol_node_type":"SOL_LEAF_NODE","using_scheme":"CS_KERNEL_STOCKHAM_BLOCK_CC","solution_childnodes":[ {"child_token":"kernel_len336_double_sbcc","child_option":0} ]} ,{"sol_node_type":"SOL_LEAF_NODE","using_scheme":"CS_KERNEL_STOCKHAM_BLOCK_CC","solution_childnodes":[ {"child_token":"kernel_len336_double_sbcc","child_option":1} ]} ,{"sol_node_type":"SOL_LEAF_NODE","using_scheme":"CS_KERNEL_STOCKHAM_BLOCK_CC","solution_childnodes":[ {"child_token":"kernel_len336_double_sbcc","child_option":2} ]} ,{"sol_node_type":"SOL_LEAF_NODE","using_scheme":"CS_KERNEL_STOCKHAM_BLOCK_CC","solution_childnodes":[ {"child_token":"kernel_len336_double_sbcc","child_option":3} ]} ,{"sol_node_type":"SOL_LEAF_NODE","using_scheme":"CS_KERNEL_STOCKHAM_BLOCK_CC","solution_childnodes":[ {"child_token":"kernel_len336_double_sbcc","child_option":4} ]} ,{"sol_node_type":"SOL_LEAF_NODE","using_scheme":"CS_KERNEL_STOCKHAM_BLOCK_CC","solution_childnodes":[ {"child_token":"kernel_len336_double_sbcc","child_option":5} ]} ,{"sol_node_type":"SOL_LEAF_NODE","using_scheme":"CS_KERNEL_STOCKHAM_BLOCK_CC","solution_childnodes":[ {"child_token":"kernel_len336_double_sbcc","child_option":6} ]} ,{"sol_node_type":"SOL_LEAF_NODE","using_scheme":"CS_KERNEL_STOCKHAM_BLOCK_CC","solution_childnodes":[ {"child_token":"kernel_len336_double_sbcc","child_option":7} ]} ]}, {"Problem":{"arch":"gfx90a","token":"sbcc_343_dp_ip_complex"}, "Solutions":[ {"sol_node_type":"SOL_LEAF_NODE","using_scheme":"CS_KERNEL_STOCKHAM_BLOCK_CC","solution_childnodes":[ {"child_token":"kernel_len343_double_sbcc","child_option":0} ]} ,{"sol_node_type":"SOL_LEAF_NODE","using_scheme":"CS_KERNEL_STOCKHAM_BLOCK_CC","solution_childnodes":[ {"child_token":"kernel_len343_double_sbcc","child_option":1} ]} ]}, {"Problem":{"arch":"gfx90a","token":"sbcc_60_sp_ip_complex"}, "Solutions":[ {"sol_node_type":"SOL_LEAF_NODE","using_scheme":"CS_KERNEL_STOCKHAM_BLOCK_CC","solution_childnodes":[ {"child_token":"kernel_len60_single_sbcc","child_option":0} ]} ,{"sol_node_type":"SOL_LEAF_NODE","using_scheme":"CS_KERNEL_STOCKHAM_BLOCK_CC","solution_childnodes":[ {"child_token":"kernel_len60_single_sbcc","child_option":1} ]} ]}, {"Problem":{"arch":"gfx90a","token":"sbcc_64_dp_ip_complex"}, "Solutions":[ {"sol_node_type":"SOL_LEAF_NODE","using_scheme":"CS_KERNEL_STOCKHAM_BLOCK_CC","solution_childnodes":[ {"child_token":"kernel_len64_double_sbcc","child_option":0} ]} ,{"sol_node_type":"SOL_LEAF_NODE","using_scheme":"CS_KERNEL_STOCKHAM_BLOCK_CC","solution_childnodes":[ {"child_token":"kernel_len64_double_sbcc","child_option":1} ]} ,{"sol_node_type":"SOL_LEAF_NODE","using_scheme":"CS_KERNEL_STOCKHAM_BLOCK_CC","solution_childnodes":[ {"child_token":"kernel_len64_double_sbcc","child_option":2} ]} ,{"sol_node_type":"SOL_LEAF_NODE","using_scheme":"CS_KERNEL_STOCKHAM_BLOCK_CC","solution_childnodes":[ {"child_token":"kernel_len64_double_sbcc","child_option":3} ]} ,{"sol_node_type":"SOL_LEAF_NODE","using_scheme":"CS_KERNEL_STOCKHAM_BLOCK_CC","solution_childnodes":[ {"child_token":"kernel_len64_double_sbcc","child_option":4} ]} ]}, {"Problem":{"arch":"gfx90a","token":"sbcc_64_sp_ip_complex"}, "Solutions":[ {"sol_node_type":"SOL_LEAF_NODE","using_scheme":"CS_KERNEL_STOCKHAM_BLOCK_CC","solution_childnodes":[ {"child_token":"kernel_len64_single_sbcc","child_option":0} ]} ,{"sol_node_type":"SOL_LEAF_NODE","using_scheme":"CS_KERNEL_STOCKHAM_BLOCK_CC","solution_childnodes":[ {"child_token":"kernel_len64_single_sbcc","child_option":1} ]} ,{"sol_node_type":"SOL_LEAF_NODE","using_scheme":"CS_KERNEL_STOCKHAM_BLOCK_CC","solution_childnodes":[ {"child_token":"kernel_len64_single_sbcc","child_option":2} ]} ]}, {"Problem":{"arch":"gfx90a","token":"sbcc_72_sp_ip_complex"}, "Solutions":[ {"sol_node_type":"SOL_LEAF_NODE","using_scheme":"CS_KERNEL_STOCKHAM_BLOCK_CC","solution_childnodes":[ {"child_token":"kernel_len72_single_sbcc","child_option":0} ]} ,{"sol_node_type":"SOL_LEAF_NODE","using_scheme":"CS_KERNEL_STOCKHAM_BLOCK_CC","solution_childnodes":[ {"child_token":"kernel_len72_single_sbcc","child_option":1} ]} ,{"sol_node_type":"SOL_LEAF_NODE","using_scheme":"CS_KERNEL_STOCKHAM_BLOCK_CC","solution_childnodes":[ {"child_token":"kernel_len72_single_sbcc","child_option":2} ]} ,{"sol_node_type":"SOL_LEAF_NODE","using_scheme":"CS_KERNEL_STOCKHAM_BLOCK_CC","solution_childnodes":[ {"child_token":"kernel_len72_single_sbcc","child_option":3} ]} ]}, {"Problem":{"arch":"gfx90a","token":"sbcc_80_sp_ip_complex"}, "Solutions":[ {"sol_node_type":"SOL_LEAF_NODE","using_scheme":"CS_KERNEL_STOCKHAM_BLOCK_CC","solution_childnodes":[ {"child_token":"kernel_len80_single_sbcc","child_option":0} ]} ,{"sol_node_type":"SOL_LEAF_NODE","using_scheme":"CS_KERNEL_STOCKHAM_BLOCK_CC","solution_childnodes":[ {"child_token":"kernel_len80_single_sbcc","child_option":1} ]} ,{"sol_node_type":"SOL_LEAF_NODE","using_scheme":"CS_KERNEL_STOCKHAM_BLOCK_CC","solution_childnodes":[ {"child_token":"kernel_len80_single_sbcc","child_option":2} ]} ,{"sol_node_type":"SOL_LEAF_NODE","using_scheme":"CS_KERNEL_STOCKHAM_BLOCK_CC","solution_childnodes":[ {"child_token":"kernel_len80_single_sbcc","child_option":3} ]} ,{"sol_node_type":"SOL_LEAF_NODE","using_scheme":"CS_KERNEL_STOCKHAM_BLOCK_CC","solution_childnodes":[ {"child_token":"kernel_len80_single_sbcc","child_option":4} ]} ]}, {"Problem":{"arch":"gfx90a","token":"sbcc_81_dp_ip_complex"}, "Solutions":[ {"sol_node_type":"SOL_LEAF_NODE","using_scheme":"CS_KERNEL_STOCKHAM_BLOCK_CC","solution_childnodes":[ {"child_token":"kernel_len81_double_sbcc","child_option":0} ]} ]}, {"Problem":{"arch":"gfx90a","token":"sbcc_81_sp_ip_complex"}, "Solutions":[ {"sol_node_type":"SOL_LEAF_NODE","using_scheme":"CS_KERNEL_STOCKHAM_BLOCK_CC","solution_childnodes":[ {"child_token":"kernel_len81_single_sbcc","child_option":0} ]} ,{"sol_node_type":"SOL_LEAF_NODE","using_scheme":"CS_KERNEL_STOCKHAM_BLOCK_CC","solution_childnodes":[ {"child_token":"kernel_len81_single_sbcc","child_option":1} ]} ]}, {"Problem":{"arch":"gfx90a","token":"sbcc_84_sp_ip_complex"}, "Solutions":[ {"sol_node_type":"SOL_LEAF_NODE","using_scheme":"CS_KERNEL_STOCKHAM_BLOCK_CC","solution_childnodes":[ {"child_token":"kernel_len84_single_sbcc","child_option":0} ]} ,{"sol_node_type":"SOL_LEAF_NODE","using_scheme":"CS_KERNEL_STOCKHAM_BLOCK_CC","solution_childnodes":[ {"child_token":"kernel_len84_single_sbcc","child_option":1} ]} ,{"sol_node_type":"SOL_LEAF_NODE","using_scheme":"CS_KERNEL_STOCKHAM_BLOCK_CC","solution_childnodes":[ {"child_token":"kernel_len84_single_sbcc","child_option":2} ]} ,{"sol_node_type":"SOL_LEAF_NODE","using_scheme":"CS_KERNEL_STOCKHAM_BLOCK_CC","solution_childnodes":[ {"child_token":"kernel_len84_single_sbcc","child_option":3} ]} ]}, {"Problem":{"arch":"gfx90a","token":"sbcc_96_dp_ip_complex"}, "Solutions":[ {"sol_node_type":"SOL_LEAF_NODE","using_scheme":"CS_KERNEL_STOCKHAM_BLOCK_CC","solution_childnodes":[ {"child_token":"kernel_len96_double_sbcc","child_option":0} ]} ,{"sol_node_type":"SOL_LEAF_NODE","using_scheme":"CS_KERNEL_STOCKHAM_BLOCK_CC","solution_childnodes":[ {"child_token":"kernel_len96_double_sbcc","child_option":1} ]} ]}, {"Problem":{"arch":"gfx90a","token":"sbcc_96_sp_ip_complex"}, "Solutions":[ {"sol_node_type":"SOL_LEAF_NODE","using_scheme":"CS_KERNEL_STOCKHAM_BLOCK_CC","solution_childnodes":[ {"child_token":"kernel_len96_single_sbcc","child_option":0} ]} ,{"sol_node_type":"SOL_LEAF_NODE","using_scheme":"CS_KERNEL_STOCKHAM_BLOCK_CC","solution_childnodes":[ {"child_token":"kernel_len96_single_sbcc","child_option":1} ]} ,{"sol_node_type":"SOL_LEAF_NODE","using_scheme":"CS_KERNEL_STOCKHAM_BLOCK_CC","solution_childnodes":[ {"child_token":"kernel_len96_single_sbcc","child_option":2} ]} ,{"sol_node_type":"SOL_LEAF_NODE","using_scheme":"CS_KERNEL_STOCKHAM_BLOCK_CC","solution_childnodes":[ {"child_token":"kernel_len96_single_sbcc","child_option":3} ]} ,{"sol_node_type":"SOL_LEAF_NODE","using_scheme":"CS_KERNEL_STOCKHAM_BLOCK_CC","solution_childnodes":[ {"child_token":"kernel_len96_single_sbcc","child_option":4} ]} ,{"sol_node_type":"SOL_LEAF_NODE","using_scheme":"CS_KERNEL_STOCKHAM_BLOCK_CC","solution_childnodes":[ {"child_token":"kernel_len96_single_sbcc","child_option":5} ]} ]}, {"Problem":{"arch":"gfx90a","token":"sbrc_100_dp_ip_complex"}, "Solutions":[ {"sol_node_type":"SOL_LEAF_NODE","using_scheme":"CS_KERNEL_STOCKHAM_BLOCK_RC","solution_childnodes":[ {"child_token":"kernel_len100_double_sbrc","child_option":0} ]} ]}, {"Problem":{"arch":"gfx90a","token":"sbrc_112_dp_ip_complex"}, "Solutions":[ {"sol_node_type":"SOL_LEAF_NODE","using_scheme":"CS_KERNEL_STOCKHAM_BLOCK_RC","solution_childnodes":[ {"child_token":"kernel_len112_double_sbrc","child_option":0} ]} ,{"sol_node_type":"SOL_LEAF_NODE","using_scheme":"CS_KERNEL_STOCKHAM_BLOCK_RC","solution_childnodes":[ {"child_token":"kernel_len112_double_sbrc","child_option":1} ]} ,{"sol_node_type":"SOL_LEAF_NODE","using_scheme":"CS_KERNEL_STOCKHAM_BLOCK_RC","solution_childnodes":[ {"child_token":"kernel_len112_double_sbrc","child_option":2} ]} ]}, {"Problem":{"arch":"gfx90a","token":"sbrc_125_dp_ip_complex"}, "Solutions":[ {"sol_node_type":"SOL_LEAF_NODE","using_scheme":"CS_KERNEL_STOCKHAM_BLOCK_RC","solution_childnodes":[ {"child_token":"kernel_len125_double_sbrc","child_option":0} ]} ]}, {"Problem":{"arch":"gfx90a","token":"sbrc_128_dp_ip_complex"}, "Solutions":[ {"sol_node_type":"SOL_LEAF_NODE","using_scheme":"CS_KERNEL_STOCKHAM_BLOCK_RC","solution_childnodes":[ {"child_token":"kernel_len128_double_sbrc","child_option":0} ]} ,{"sol_node_type":"SOL_LEAF_NODE","using_scheme":"CS_KERNEL_STOCKHAM_BLOCK_RC","solution_childnodes":[ {"child_token":"kernel_len128_double_sbrc","child_option":1} ]} ,{"sol_node_type":"SOL_LEAF_NODE","using_scheme":"CS_KERNEL_STOCKHAM_BLOCK_RC","solution_childnodes":[ {"child_token":"kernel_len128_double_sbrc","child_option":2} ]} ,{"sol_node_type":"SOL_LEAF_NODE","using_scheme":"CS_KERNEL_STOCKHAM_BLOCK_RC","solution_childnodes":[ {"child_token":"kernel_len128_double_sbrc","child_option":3} ]} ,{"sol_node_type":"SOL_LEAF_NODE","using_scheme":"CS_KERNEL_STOCKHAM_BLOCK_RC","solution_childnodes":[ {"child_token":"kernel_len128_double_sbrc","child_option":4} ]} ]}, {"Problem":{"arch":"gfx90a","token":"sbrc_192_dp_ip_complex"}, "Solutions":[ {"sol_node_type":"SOL_LEAF_NODE","using_scheme":"CS_KERNEL_STOCKHAM_BLOCK_RC","solution_childnodes":[ {"child_token":"kernel_len192_double_sbrc","child_option":0} ]} ,{"sol_node_type":"SOL_LEAF_NODE","using_scheme":"CS_KERNEL_STOCKHAM_BLOCK_RC","solution_childnodes":[ {"child_token":"kernel_len192_double_sbrc","child_option":1} ]} ,{"sol_node_type":"SOL_LEAF_NODE","using_scheme":"CS_KERNEL_STOCKHAM_BLOCK_RC","solution_childnodes":[ {"child_token":"kernel_len192_double_sbrc","child_option":2} ]} ]}, {"Problem":{"arch":"gfx90a","token":"sbrc_256_dp_ip_complex"}, "Solutions":[ {"sol_node_type":"SOL_LEAF_NODE","using_scheme":"CS_KERNEL_STOCKHAM_BLOCK_RC","solution_childnodes":[ {"child_token":"kernel_len256_double_sbrc","child_option":0} ]} ]}, {"Problem":{"arch":"gfx90a","token":"sbrc_49_dp_ip_complex"}, "Solutions":[ {"sol_node_type":"SOL_LEAF_NODE","using_scheme":"CS_KERNEL_STOCKHAM_BLOCK_RC","solution_childnodes":[ {"child_token":"kernel_len49_double_sbrc","child_option":0} ]} ,{"sol_node_type":"SOL_LEAF_NODE","using_scheme":"CS_KERNEL_STOCKHAM_BLOCK_RC","solution_childnodes":[ {"child_token":"kernel_len49_double_sbrc","child_option":1} ]} ]}, {"Problem":{"arch":"gfx90a","token":"sbrc_81_dp_ip_complex"}, "Solutions":[ {"sol_node_type":"SOL_LEAF_NODE","using_scheme":"CS_KERNEL_STOCKHAM_BLOCK_RC","solution_childnodes":[ {"child_token":"kernel_len81_double_sbrc","child_option":0} ]} ]}, {"Problem":{"arch":"gfx90a","token":"sbrc_81_sp_ip_complex"}, "Solutions":[ {"sol_node_type":"SOL_LEAF_NODE","using_scheme":"CS_KERNEL_STOCKHAM_BLOCK_RC","solution_childnodes":[ {"child_token":"kernel_len81_single_sbrc","child_option":0} ]} ,{"sol_node_type":"SOL_LEAF_NODE","using_scheme":"CS_KERNEL_STOCKHAM_BLOCK_RC","solution_childnodes":[ {"child_token":"kernel_len81_single_sbrc","child_option":1} ]} ,{"sol_node_type":"SOL_LEAF_NODE","using_scheme":"CS_KERNEL_STOCKHAM_BLOCK_RC","solution_childnodes":[ {"child_token":"kernel_len81_single_sbrc","child_option":2} ]} ]}, {"Problem":{"arch":"gfx90a","token":"sbcr_336_dp_ip_complex"}, "Solutions":[ {"sol_node_type":"SOL_LEAF_NODE","using_scheme":"CS_KERNEL_STOCKHAM_BLOCK_CR","solution_childnodes":[ {"child_token":"kernel_len336_double_sbcr","child_option":0} ]} ,{"sol_node_type":"SOL_LEAF_NODE","using_scheme":"CS_KERNEL_STOCKHAM_BLOCK_CR","solution_childnodes":[ {"child_token":"kernel_len336_double_sbcr","child_option":1} ]} ,{"sol_node_type":"SOL_LEAF_NODE","using_scheme":"CS_KERNEL_STOCKHAM_BLOCK_CR","solution_childnodes":[ {"child_token":"kernel_len336_double_sbcr","child_option":2} ]} ,{"sol_node_type":"SOL_LEAF_NODE","using_scheme":"CS_KERNEL_STOCKHAM_BLOCK_CR","solution_childnodes":[ {"child_token":"kernel_len336_double_sbcr","child_option":3} ]} ]}, {"Problem":{"arch":"gfx90a","token":"sbcr_56_dp_ip_complex"}, "Solutions":[ {"sol_node_type":"SOL_LEAF_NODE","using_scheme":"CS_KERNEL_STOCKHAM_BLOCK_CR","solution_childnodes":[ {"child_token":"kernel_len56_double_sbcr","child_option":0} ]} ,{"sol_node_type":"SOL_LEAF_NODE","using_scheme":"CS_KERNEL_STOCKHAM_BLOCK_CR","solution_childnodes":[ {"child_token":"kernel_len56_double_sbcr","child_option":1} ]} ]}, {"Problem":{"arch":"gfx90a","token":"sbrc_xy_z_256_sp_ip_complex"}, "Solutions":[ {"sol_node_type":"SOL_LEAF_NODE","using_scheme":"CS_KERNEL_STOCKHAM_TRANSPOSE_XY_Z","solution_childnodes":[ {"child_token":"kernel_len256_single_sbrc_xy_z","child_option":0} ]} ,{"sol_node_type":"SOL_LEAF_NODE","using_scheme":"CS_KERNEL_STOCKHAM_TRANSPOSE_XY_Z","solution_childnodes":[ {"child_token":"kernel_len256_single_sbrc_xy_z","child_option":1} ]} ]}, {"Problem":{"arch":"gfx90a","token":"leafnode_token_builtin_kernel"}, "Solutions":[ {"sol_node_type":"SOL_LEAF_NODE","using_scheme":"CS_KERNEL_TRANSPOSE","solution_childnodes":[ {"child_token":"kernel_token_builtin_kernel","child_option":0} ]} ,{"sol_node_type":"SOL_LEAF_NODE","using_scheme":"CS_KERNEL_TRANSPOSE_XY_Z","solution_childnodes":[ {"child_token":"kernel_token_builtin_kernel","child_option":0} ]} ,{"sol_node_type":"SOL_LEAF_NODE","using_scheme":"CS_KERNEL_TRANSPOSE_Z_XY","solution_childnodes":[ {"child_token":"kernel_token_builtin_kernel","child_option":0} ]} ,{"sol_node_type":"SOL_LEAF_NODE","using_scheme":"CS_KERNEL_COPY_R_TO_CMPLX","solution_childnodes":[ {"child_token":"kernel_token_builtin_kernel","child_option":0} ]} ,{"sol_node_type":"SOL_LEAF_NODE","using_scheme":"CS_KERNEL_COPY_CMPLX_TO_HERM","solution_childnodes":[ {"child_token":"kernel_token_builtin_kernel","child_option":0} ]} ,{"sol_node_type":"SOL_LEAF_NODE","using_scheme":"CS_KERNEL_R_TO_CMPLX","solution_childnodes":[ {"child_token":"kernel_token_builtin_kernel","child_option":0} ]} ,{"sol_node_type":"SOL_LEAF_NODE","using_scheme":"CS_KERNEL_COPY_HERM_TO_CMPLX","solution_childnodes":[ {"child_token":"kernel_token_builtin_kernel","child_option":0} ]} ,{"sol_node_type":"SOL_LEAF_NODE","using_scheme":"CS_KERNEL_COPY_CMPLX_TO_R","solution_childnodes":[ {"child_token":"kernel_token_builtin_kernel","child_option":0} ]} ,{"sol_node_type":"SOL_LEAF_NODE","using_scheme":"CS_KERNEL_CMPLX_TO_R","solution_childnodes":[ {"child_token":"kernel_token_builtin_kernel","child_option":0} ]} ]}, {"Problem":{"arch":"gfx90a","token":"10_20_sp_ip_complex"}, "Solutions":[ {"sol_node_type":"SOL_DUMMY","using_scheme":"CS_NONE","solution_childnodes":[ ]} ,{"sol_node_type":"SOL_LEAF_NODE","using_scheme":"CS_KERNEL_2D_SINGLE","solution_childnodes":[ {"child_token":"kernel_len10x20_single_2d_single","child_option":0} ]} ]}, {"Problem":{"arch":"gfx90a","token":"20_10_sp_ip_complex"}, "Solutions":[ {"sol_node_type":"SOL_DUMMY","using_scheme":"CS_NONE","solution_childnodes":[ ]} ,{"sol_node_type":"SOL_LEAF_NODE","using_scheme":"CS_KERNEL_2D_SINGLE","solution_childnodes":[ {"child_token":"kernel_len20x10_single_2d_single","child_option":0} ]} ]}, {"Problem":{"arch":"gfx90a","token":"26_64_sp_ip_complex"}, "Solutions":[ {"sol_node_type":"SOL_DUMMY","using_scheme":"CS_NONE","solution_childnodes":[ ]} ,{"sol_node_type":"SOL_LEAF_NODE","using_scheme":"CS_KERNEL_2D_SINGLE","solution_childnodes":[ {"child_token":"kernel_len26x64_single_2d_single","child_option":0} ]} ]}, {"Problem":{"arch":"gfx90a","token":"26_72_sp_ip_complex"}, "Solutions":[ {"sol_node_type":"SOL_DUMMY","using_scheme":"CS_NONE","solution_childnodes":[ ]} ,{"sol_node_type":"SOL_LEAF_NODE","using_scheme":"CS_KERNEL_2D_SINGLE","solution_childnodes":[ {"child_token":"kernel_len26x72_single_2d_single","child_option":0} ]} ]}, {"Problem":{"arch":"gfx90a","token":"30_60_sp_ip_complex"}, "Solutions":[ {"sol_node_type":"SOL_DUMMY","using_scheme":"CS_NONE","solution_childnodes":[ ]} ,{"sol_node_type":"SOL_LEAF_NODE","using_scheme":"CS_KERNEL_2D_SINGLE","solution_childnodes":[ {"child_token":"kernel_len30x60_single_2d_single","child_option":0} ]} ]}, {"Problem":{"arch":"gfx90a","token":"32_16_sp_ip_complex"}, "Solutions":[ {"sol_node_type":"SOL_DUMMY","using_scheme":"CS_NONE","solution_childnodes":[ ]} ,{"sol_node_type":"SOL_LEAF_NODE","using_scheme":"CS_KERNEL_2D_SINGLE","solution_childnodes":[ {"child_token":"kernel_len32x16_single_2d_single","child_option":0} ]} ]}, {"Problem":{"arch":"gfx90a","token":"32_64_sp_ip_complex"}, "Solutions":[ {"sol_node_type":"SOL_DUMMY","using_scheme":"CS_NONE","solution_childnodes":[ ]} ,{"sol_node_type":"SOL_LEAF_NODE","using_scheme":"CS_KERNEL_2D_SINGLE","solution_childnodes":[ {"child_token":"kernel_len32x64_single_2d_single","child_option":0} ]} ]}, {"Problem":{"arch":"gfx90a","token":"36_72_sp_ip_complex"}, "Solutions":[ {"sol_node_type":"SOL_DUMMY","using_scheme":"CS_NONE","solution_childnodes":[ ]} ,{"sol_node_type":"SOL_LEAF_NODE","using_scheme":"CS_KERNEL_2D_SINGLE","solution_childnodes":[ {"child_token":"kernel_len36x72_single_2d_single","child_option":0} ]} ]}, {"Problem":{"arch":"gfx90a","token":"36_80_sp_ip_complex"}, "Solutions":[ {"sol_node_type":"SOL_DUMMY","using_scheme":"CS_NONE","solution_childnodes":[ ]} ,{"sol_node_type":"SOL_LEAF_NODE","using_scheme":"CS_KERNEL_2D_SINGLE","solution_childnodes":[ {"child_token":"kernel_len36x80_single_2d_single","child_option":0} ]} ]}, {"Problem":{"arch":"gfx90a","token":"36_84_sp_ip_complex"}, "Solutions":[ {"sol_node_type":"SOL_DUMMY","using_scheme":"CS_NONE","solution_childnodes":[ ]} ,{"sol_node_type":"SOL_LEAF_NODE","using_scheme":"CS_KERNEL_2D_SINGLE","solution_childnodes":[ {"child_token":"kernel_len36x84_single_2d_single","child_option":0} ]} ]}, {"Problem":{"arch":"gfx90a","token":"40_80_sp_ip_complex"}, "Solutions":[ {"sol_node_type":"SOL_DUMMY","using_scheme":"CS_NONE","solution_childnodes":[ ]} ,{"sol_node_type":"SOL_LEAF_NODE","using_scheme":"CS_KERNEL_2D_SINGLE","solution_childnodes":[ {"child_token":"kernel_len40x80_single_2d_single","child_option":0} ]} ]}, {"Problem":{"arch":"gfx90a","token":"42_84_sp_ip_complex"}, "Solutions":[ {"sol_node_type":"SOL_DUMMY","using_scheme":"CS_NONE","solution_childnodes":[ ]} ,{"sol_node_type":"SOL_LEAF_NODE","using_scheme":"CS_KERNEL_2D_SINGLE","solution_childnodes":[ {"child_token":"kernel_len42x84_single_2d_single","child_option":0} ]} ]}, {"Problem":{"arch":"gfx90a","token":"42_96_sp_ip_complex"}, "Solutions":[ {"sol_node_type":"SOL_DUMMY","using_scheme":"CS_NONE","solution_childnodes":[ ]} ,{"sol_node_type":"SOL_LEAF_NODE","using_scheme":"CS_KERNEL_2D_SINGLE","solution_childnodes":[ {"child_token":"kernel_len42x96_single_2d_single","child_option":0} ]} ]}, {"Problem":{"arch":"gfx90a","token":"60_30_sp_ip_complex"}, "Solutions":[ {"sol_node_type":"SOL_DUMMY","using_scheme":"CS_NONE","solution_childnodes":[ ]} ,{"sol_node_type":"SOL_LEAF_NODE","using_scheme":"CS_KERNEL_2D_SINGLE","solution_childnodes":[ {"child_token":"kernel_len60x30_single_2d_single","child_option":0} ]} ]}, {"Problem":{"arch":"gfx90a","token":"64_26_sp_ip_complex"}, "Solutions":[ {"sol_node_type":"SOL_DUMMY","using_scheme":"CS_NONE","solution_childnodes":[ ]} ,{"sol_node_type":"SOL_LEAF_NODE","using_scheme":"CS_KERNEL_2D_SINGLE","solution_childnodes":[ {"child_token":"kernel_len64x26_single_2d_single","child_option":0} ]} ]}, {"Problem":{"arch":"gfx90a","token":"64_32_sp_ip_complex"}, "Solutions":[ {"sol_node_type":"SOL_DUMMY","using_scheme":"CS_NONE","solution_childnodes":[ ]} ,{"sol_node_type":"SOL_LEAF_NODE","using_scheme":"CS_KERNEL_2D_SINGLE","solution_childnodes":[ {"child_token":"kernel_len64x32_single_2d_single","child_option":0} ]} ]}, {"Problem":{"arch":"gfx90a","token":"72_26_sp_ip_complex"}, "Solutions":[ {"sol_node_type":"SOL_DUMMY","using_scheme":"CS_NONE","solution_childnodes":[ ]} ,{"sol_node_type":"SOL_LEAF_NODE","using_scheme":"CS_KERNEL_2D_SINGLE","solution_childnodes":[ {"child_token":"kernel_len72x26_single_2d_single","child_option":0} ]} ]}, {"Problem":{"arch":"gfx90a","token":"72_36_sp_ip_complex"}, "Solutions":[ {"sol_node_type":"SOL_DUMMY","using_scheme":"CS_NONE","solution_childnodes":[ ]} ,{"sol_node_type":"SOL_LEAF_NODE","using_scheme":"CS_KERNEL_2D_SINGLE","solution_childnodes":[ {"child_token":"kernel_len72x36_single_2d_single","child_option":0} ]} ]}, {"Problem":{"arch":"gfx90a","token":"80_36_sp_ip_complex"}, "Solutions":[ {"sol_node_type":"SOL_DUMMY","using_scheme":"CS_NONE","solution_childnodes":[ ]} ,{"sol_node_type":"SOL_LEAF_NODE","using_scheme":"CS_KERNEL_2D_SINGLE","solution_childnodes":[ {"child_token":"kernel_len80x36_single_2d_single","child_option":0} ]} ]}, {"Problem":{"arch":"gfx90a","token":"80_40_sp_ip_complex"}, "Solutions":[ {"sol_node_type":"SOL_DUMMY","using_scheme":"CS_NONE","solution_childnodes":[ ]} ,{"sol_node_type":"SOL_LEAF_NODE","using_scheme":"CS_KERNEL_2D_SINGLE","solution_childnodes":[ {"child_token":"kernel_len80x40_single_2d_single","child_option":0} ]} ]}, {"Problem":{"arch":"gfx90a","token":"84_36_sp_ip_complex"}, "Solutions":[ {"sol_node_type":"SOL_DUMMY","using_scheme":"CS_NONE","solution_childnodes":[ ]} ,{"sol_node_type":"SOL_LEAF_NODE","using_scheme":"CS_KERNEL_2D_SINGLE","solution_childnodes":[ {"child_token":"kernel_len84x36_single_2d_single","child_option":0} ]} ]}, {"Problem":{"arch":"gfx90a","token":"84_42_sp_ip_complex"}, "Solutions":[ {"sol_node_type":"SOL_DUMMY","using_scheme":"CS_NONE","solution_childnodes":[ ]} ,{"sol_node_type":"SOL_LEAF_NODE","using_scheme":"CS_KERNEL_2D_SINGLE","solution_childnodes":[ {"child_token":"kernel_len84x42_single_2d_single","child_option":0} ]} ]}, {"Problem":{"arch":"gfx90a","token":"84_7_sp_ip_complex"}, "Solutions":[ {"sol_node_type":"SOL_DUMMY","using_scheme":"CS_NONE","solution_childnodes":[ ]} ,{"sol_node_type":"SOL_LEAF_NODE","using_scheme":"CS_KERNEL_2D_SINGLE","solution_childnodes":[ {"child_token":"kernel_len84x7_single_2d_single","child_option":0} ]} ]}, {"Problem":{"arch":"gfx90a","token":"96_42_sp_ip_complex"}, "Solutions":[ {"sol_node_type":"SOL_DUMMY","using_scheme":"CS_NONE","solution_childnodes":[ ]} ,{"sol_node_type":"SOL_LEAF_NODE","using_scheme":"CS_KERNEL_2D_SINGLE","solution_childnodes":[ {"child_token":"kernel_len96x42_single_2d_single","child_option":0} ]} ]}, {"Problem":{"arch":"gfx90a","token":"16807_dp_ip_real_bwd"}, "Solutions":[ {"sol_node_type":"SOL_INTERNAL_NODE","using_scheme":"CS_REAL_TRANSFORM_USING_CMPLX","solution_childnodes":[ {"child_token":"leafnode_token_builtin_kernel","child_option":6},{"child_token":"16807_dp_ip_complex","child_option":1},{"child_token":"leafnode_token_builtin_kernel","child_option":7} ]} ]}, {"Problem":{"arch":"gfx90a","token":"16807_dp_op_real_bwd"}, "Solutions":[ {"sol_node_type":"SOL_INTERNAL_NODE","using_scheme":"CS_REAL_TRANSFORM_USING_CMPLX","solution_childnodes":[ {"child_token":"leafnode_token_builtin_kernel","child_option":6},{"child_token":"16807_dp_ip_complex","child_option":1},{"child_token":"leafnode_token_builtin_kernel","child_option":7} ]} ]}, {"Problem":{"arch":"gfx90a","token":"243_dp_ip_real_bwd"}, "Solutions":[ {"sol_node_type":"SOL_INTERNAL_NODE","using_scheme":"CS_REAL_TRANSFORM_USING_CMPLX","solution_childnodes":[ {"child_token":"leafnode_token_builtin_kernel","child_option":6},{"child_token":"243_dp_ip_complex","child_option":4},{"child_token":"leafnode_token_builtin_kernel","child_option":7} ]} ]}, {"Problem":{"arch":"gfx90a","token":"243_dp_ip_real_fwd"}, "Solutions":[ {"sol_node_type":"SOL_INTERNAL_NODE","using_scheme":"CS_REAL_TRANSFORM_USING_CMPLX","solution_childnodes":[ {"child_token":"leafnode_token_builtin_kernel","child_option":3},{"child_token":"243_dp_ip_complex","child_option":3},{"child_token":"leafnode_token_builtin_kernel","child_option":4} ]} ]}, {"Problem":{"arch":"gfx90a","token":"243_dp_op_real_fwd"}, "Solutions":[ {"sol_node_type":"SOL_INTERNAL_NODE","using_scheme":"CS_REAL_TRANSFORM_USING_CMPLX","solution_childnodes":[ {"child_token":"leafnode_token_builtin_kernel","child_option":3},{"child_token":"243_dp_ip_complex","child_option":1},{"child_token":"leafnode_token_builtin_kernel","child_option":4} ]} ]}, {"Problem":{"arch":"gfx90a","token":"55_55_75_dp_op_real_bwd"}, "Solutions":[ {"sol_node_type":"SOL_INTERNAL_NODE","using_scheme":"CS_REAL_TRANSFORM_USING_CMPLX","solution_childnodes":[ {"child_token":"leafnode_token_builtin_kernel","child_option":6},{"child_token":"55_55_75_dp_ip_complex","child_option":1},{"child_token":"leafnode_token_builtin_kernel","child_option":7} ]} ]}, {"Problem":{"arch":"gfx90a","token":"55_55_75_sp_op_real_bwd"}, "Solutions":[ {"sol_node_type":"SOL_INTERNAL_NODE","using_scheme":"CS_REAL_TRANSFORM_USING_CMPLX","solution_childnodes":[ {"child_token":"leafnode_token_builtin_kernel","child_option":6},{"child_token":"55_55_75_sp_ip_complex","child_option":2},{"child_token":"leafnode_token_builtin_kernel","child_option":7} ]} ]}, {"Problem":{"arch":"gfx90a","token":"55_55_75_sp_op_real_fwd"}, "Solutions":[ {"sol_node_type":"SOL_INTERNAL_NODE","using_scheme":"CS_REAL_TRANSFORM_USING_CMPLX","solution_childnodes":[ {"child_token":"leafnode_token_builtin_kernel","child_option":3},{"child_token":"55_55_75_sp_ip_complex","child_option":1},{"child_token":"leafnode_token_builtin_kernel","child_option":4} ]} ]}, {"Problem":{"arch":"gfx90a","token":"55_55_dp_op_real_bwd"}, "Solutions":[ {"sol_node_type":"SOL_INTERNAL_NODE","using_scheme":"CS_REAL_TRANSFORM_USING_CMPLX","solution_childnodes":[ {"child_token":"leafnode_token_builtin_kernel","child_option":6},{"child_token":"55_55_dp_ip_complex","child_option":1},{"child_token":"leafnode_token_builtin_kernel","child_option":7} ]} ]}, {"Problem":{"arch":"gfx90a","token":"100_sp_ip_complex"}, "Solutions":[ {"sol_node_type":"SOL_DUMMY","using_scheme":"CS_NONE","solution_childnodes":[ ]} ,{"sol_node_type":"SOL_INTERNAL_NODE","using_scheme":"CS_REAL_TRANSFORM_EVEN","solution_childnodes":[ {"child_token":"50_sp_ip_complex","child_option":1} ]} ,{"sol_node_type":"SOL_INTERNAL_NODE","using_scheme":"CS_REAL_TRANSFORM_EVEN","solution_childnodes":[ {"child_token":"50_sp_ip_complex","child_option":3} ]} ,{"sol_node_type":"SOL_LEAF_NODE","using_scheme":"CS_KERNEL_STOCKHAM","solution_childnodes":[ {"child_token":"kernel_len100_single_sbrr","child_option":0} ]} ,{"sol_node_type":"SOL_LEAF_NODE","using_scheme":"CS_KERNEL_STOCKHAM","solution_childnodes":[ {"child_token":"kernel_len100_single_sbrr","child_option":1} ]} ,{"sol_node_type":"SOL_LEAF_NODE","using_scheme":"CS_KERNEL_STOCKHAM","solution_childnodes":[ {"child_token":"kernel_len100_single_sbrr","child_option":2} ]} ,{"sol_node_type":"SOL_INTERNAL_NODE","using_scheme":"CS_REAL_TRANSFORM_EVEN","solution_childnodes":[ {"child_token":"50_sp_ip_complex","child_option":6} ]} ]}, {"Problem":{"arch":"gfx90a","token":"101_sp_ip_complex"}, "Solutions":[ {"sol_node_type":"SOL_DUMMY","using_scheme":"CS_NONE","solution_childnodes":[ ]} ,{"sol_node_type":"SOL_INTERNAL_NODE","using_scheme":"CS_REAL_TRANSFORM_EVEN","solution_childnodes":[ {"child_token":"100_sp_ip_complex","child_option":4} ]} ]}, {"Problem":{"arch":"gfx90a","token":"104_dp_ip_complex"}, "Solutions":[ {"sol_node_type":"SOL_DUMMY","using_scheme":"CS_NONE","solution_childnodes":[ ]} ,{"sol_node_type":"SOL_INTERNAL_NODE","using_scheme":"CS_REAL_TRANSFORM_EVEN","solution_childnodes":[ {"child_token":"52_dp_ip_complex","child_option":2} ]} ]}, {"Problem":{"arch":"gfx90a","token":"104_sp_ip_complex"}, "Solutions":[ {"sol_node_type":"SOL_DUMMY","using_scheme":"CS_NONE","solution_childnodes":[ ]} ,{"sol_node_type":"SOL_INTERNAL_NODE","using_scheme":"CS_REAL_TRANSFORM_EVEN","solution_childnodes":[ {"child_token":"52_sp_ip_complex","child_option":1} ]} ]}, {"Problem":{"arch":"gfx90a","token":"109_sp_ip_complex"}, "Solutions":[ {"sol_node_type":"SOL_DUMMY","using_scheme":"CS_NONE","solution_childnodes":[ ]} ,{"sol_node_type":"SOL_INTERNAL_NODE","using_scheme":"CS_REAL_TRANSFORM_EVEN","solution_childnodes":[ {"child_token":"108_sp_ip_complex","child_option":4} ]} ]}, {"Problem":{"arch":"gfx90a","token":"113_sp_ip_complex"}, "Solutions":[ {"sol_node_type":"SOL_DUMMY","using_scheme":"CS_NONE","solution_childnodes":[ ]} ,{"sol_node_type":"SOL_INTERNAL_NODE","using_scheme":"CS_REAL_TRANSFORM_EVEN","solution_childnodes":[ {"child_token":"112_sp_ip_complex","child_option":3} ]} ]}, {"Problem":{"arch":"gfx90a","token":"129_sp_ip_complex"}, "Solutions":[ {"sol_node_type":"SOL_DUMMY","using_scheme":"CS_NONE","solution_childnodes":[ ]} ,{"sol_node_type":"SOL_INTERNAL_NODE","using_scheme":"CS_REAL_TRANSFORM_EVEN","solution_childnodes":[ {"child_token":"128_sp_ip_complex","child_option":1} ]} ]}, {"Problem":{"arch":"gfx90a","token":"144_sp_ip_complex"}, "Solutions":[ {"sol_node_type":"SOL_DUMMY","using_scheme":"CS_NONE","solution_childnodes":[ ]} ,{"sol_node_type":"SOL_INTERNAL_NODE","using_scheme":"CS_REAL_TRANSFORM_EVEN","solution_childnodes":[ {"child_token":"72_sp_ip_complex","child_option":1} ]} ]}, {"Problem":{"arch":"gfx90a","token":"16384_dp_ip_real_bwd"}, "Solutions":[ {"sol_node_type":"SOL_INTERNAL_NODE","using_scheme":"CS_REAL_TRANSFORM_EVEN","solution_childnodes":[ {"child_token":"leafnode_token_builtin_kernel","child_option":8},{"child_token":"8192_dp_ip_complex","child_option":3} ]} ]}, {"Problem":{"arch":"gfx90a","token":"16384_dp_ip_real_fwd"}, "Solutions":[ {"sol_node_type":"SOL_INTERNAL_NODE","using_scheme":"CS_REAL_TRANSFORM_EVEN","solution_childnodes":[ {"child_token":"8192_dp_ip_complex","child_option":1},{"child_token":"leafnode_token_builtin_kernel","child_option":5} ]} ]}, {"Problem":{"arch":"gfx90a","token":"16384_dp_op_real_fwd"}, "Solutions":[ {"sol_node_type":"SOL_INTERNAL_NODE","using_scheme":"CS_REAL_TRANSFORM_EVEN","solution_childnodes":[ {"child_token":"8192_dp_ip_complex","child_option":2},{"child_token":"leafnode_token_builtin_kernel","child_option":5} ]} ]}, {"Problem":{"arch":"gfx90a","token":"168_sp_ip_complex"}, "Solutions":[ {"sol_node_type":"SOL_DUMMY","using_scheme":"CS_NONE","solution_childnodes":[ ]} ,{"sol_node_type":"SOL_INTERNAL_NODE","using_scheme":"CS_REAL_TRANSFORM_EVEN","solution_childnodes":[ {"child_token":"84_sp_ip_complex","child_option":1} ]} ,{"sol_node_type":"SOL_INTERNAL_NODE","using_scheme":"CS_REAL_TRANSFORM_EVEN","solution_childnodes":[ {"child_token":"84_sp_ip_complex","child_option":3} ]} ]}, {"Problem":{"arch":"gfx90a","token":"200_dp_ip_complex"}, "Solutions":[ {"sol_node_type":"SOL_DUMMY","using_scheme":"CS_NONE","solution_childnodes":[ ]} ,{"sol_node_type":"SOL_INTERNAL_NODE","using_scheme":"CS_REAL_TRANSFORM_EVEN","solution_childnodes":[ {"child_token":"100_dp_ip_complex","child_option":2} ]} ]}, {"Problem":{"arch":"gfx90a","token":"200_sp_ip_complex"}, "Solutions":[ {"sol_node_type":"SOL_DUMMY","using_scheme":"CS_NONE","solution_childnodes":[ ]} ,{"sol_node_type":"SOL_INTERNAL_NODE","using_scheme":"CS_REAL_TRANSFORM_EVEN","solution_childnodes":[ {"child_token":"100_sp_ip_complex","child_option":3} ]} ,{"sol_node_type":"SOL_INTERNAL_NODE","using_scheme":"CS_REAL_TRANSFORM_EVEN","solution_childnodes":[ {"child_token":"100_sp_ip_complex","child_option":5} ]} ]}, {"Problem":{"arch":"gfx90a","token":"216_sp_ip_complex"}, "Solutions":[ {"sol_node_type":"SOL_DUMMY","using_scheme":"CS_NONE","solution_childnodes":[ ]} ,{"sol_node_type":"SOL_INTERNAL_NODE","using_scheme":"CS_REAL_TRANSFORM_EVEN","solution_childnodes":[ {"child_token":"108_sp_ip_complex","child_option":3} ]} ]}, {"Problem":{"arch":"gfx90a","token":"224_sp_ip_complex"}, "Solutions":[ {"sol_node_type":"SOL_DUMMY","using_scheme":"CS_NONE","solution_childnodes":[ ]} ,{"sol_node_type":"SOL_INTERNAL_NODE","using_scheme":"CS_REAL_TRANSFORM_EVEN","solution_childnodes":[ {"child_token":"112_sp_ip_complex","child_option":2} ]} ]}, {"Problem":{"arch":"gfx90a","token":"256_sp_ip_complex"}, "Solutions":[ {"sol_node_type":"SOL_DUMMY","using_scheme":"CS_NONE","solution_childnodes":[ ]} ,{"sol_node_type":"SOL_LEAF_NODE","using_scheme":"CS_KERNEL_STOCKHAM","solution_childnodes":[ {"child_token":"kernel_len256_single_sbrr","child_option":0} ]} ,{"sol_node_type":"SOL_INTERNAL_NODE","using_scheme":"CS_REAL_TRANSFORM_EVEN","solution_childnodes":[ {"child_token":"128_sp_ip_complex","child_option":1} ]} ,{"sol_node_type":"SOL_INTERNAL_NODE","using_scheme":"CS_REAL_TRANSFORM_EVEN","solution_childnodes":[ {"child_token":"128_sp_ip_complex","child_option":3} ]} ]}, {"Problem":{"arch":"gfx90a","token":"486_dp_ip_real_bwd"}, "Solutions":[ {"sol_node_type":"SOL_INTERNAL_NODE","using_scheme":"CS_REAL_TRANSFORM_EVEN","solution_childnodes":[ {"child_token":"243_dp_ip_complex","child_option":5} ]} ]}, {"Problem":{"arch":"gfx90a","token":"486_dp_ip_real_fwd"}, "Solutions":[ {"sol_node_type":"SOL_INTERNAL_NODE","using_scheme":"CS_REAL_TRANSFORM_EVEN","solution_childnodes":[ {"child_token":"243_dp_ip_complex","child_option":2} ]} ]}, {"Problem":{"arch":"gfx90a","token":"486_dp_op_real_bwd"}, "Solutions":[ {"sol_node_type":"SOL_INTERNAL_NODE","using_scheme":"CS_REAL_TRANSFORM_EVEN","solution_childnodes":[ {"child_token":"243_dp_ip_complex","child_option":6} ]} ]}, {"Problem":{"arch":"gfx90a","token":"486_dp_op_real_fwd"}, "Solutions":[ {"sol_node_type":"SOL_INTERNAL_NODE","using_scheme":"CS_REAL_TRANSFORM_EVEN","solution_childnodes":[ {"child_token":"243_dp_ip_complex","child_option":2} ]} ]}, {"Problem":{"arch":"gfx90a","token":"49_sp_ip_complex"}, "Solutions":[ {"sol_node_type":"SOL_DUMMY","using_scheme":"CS_NONE","solution_childnodes":[ ]} ,{"sol_node_type":"SOL_INTERNAL_NODE","using_scheme":"CS_REAL_TRANSFORM_EVEN","solution_childnodes":[ {"child_token":"48_sp_ip_complex","child_option":4} ]} ,{"sol_node_type":"SOL_INTERNAL_NODE","using_scheme":"CS_REAL_TRANSFORM_EVEN","solution_childnodes":[ {"child_token":"48_sp_ip_complex","child_option":2} ]} ,{"sol_node_type":"SOL_INTERNAL_NODE","using_scheme":"CS_REAL_TRANSFORM_EVEN","solution_childnodes":[ {"child_token":"48_sp_ip_complex","child_option":3} ]} ]}, {"Problem":{"arch":"gfx90a","token":"51_dp_ip_complex"}, "Solutions":[ {"sol_node_type":"SOL_DUMMY","using_scheme":"CS_NONE","solution_childnodes":[ ]} ,{"sol_node_type":"SOL_INTERNAL_NODE","using_scheme":"CS_REAL_TRANSFORM_EVEN","solution_childnodes":[ {"child_token":"50_dp_ip_complex","child_option":2} ]} ]}, {"Problem":{"arch":"gfx90a","token":"51_sp_ip_complex"}, "Solutions":[ {"sol_node_type":"SOL_DUMMY","using_scheme":"CS_NONE","solution_childnodes":[ ]} ,{"sol_node_type":"SOL_INTERNAL_NODE","using_scheme":"CS_REAL_TRANSFORM_EVEN","solution_childnodes":[ {"child_token":"50_sp_ip_complex","child_option":2} ]} ,{"sol_node_type":"SOL_INTERNAL_NODE","using_scheme":"CS_REAL_TRANSFORM_EVEN","solution_childnodes":[ {"child_token":"50_sp_ip_complex","child_option":4} ]} ,{"sol_node_type":"SOL_INTERNAL_NODE","using_scheme":"CS_REAL_TRANSFORM_EVEN","solution_childnodes":[ {"child_token":"50_sp_ip_complex","child_option":5} ]} ]}, {"Problem":{"arch":"gfx90a","token":"53_dp_ip_complex"}, "Solutions":[ {"sol_node_type":"SOL_DUMMY","using_scheme":"CS_NONE","solution_childnodes":[ ]} ,{"sol_node_type":"SOL_INTERNAL_NODE","using_scheme":"CS_REAL_TRANSFORM_EVEN","solution_childnodes":[ {"child_token":"52_dp_ip_complex","child_option":3} ]} ]}, {"Problem":{"arch":"gfx90a","token":"57_sp_ip_complex"}, "Solutions":[ {"sol_node_type":"SOL_DUMMY","using_scheme":"CS_NONE","solution_childnodes":[ ]} ,{"sol_node_type":"SOL_INTERNAL_NODE","using_scheme":"CS_REAL_TRANSFORM_EVEN","solution_childnodes":[ {"child_token":"56_sp_ip_complex","child_option":2} ]} ]}, {"Problem":{"arch":"gfx90a","token":"65_sp_ip_complex"}, "Solutions":[ {"sol_node_type":"SOL_DUMMY","using_scheme":"CS_NONE","solution_childnodes":[ ]} ,{"sol_node_type":"SOL_INTERNAL_NODE","using_scheme":"CS_REAL_TRANSFORM_EVEN","solution_childnodes":[ {"child_token":"64_sp_ip_complex","child_option":2} ]} ]}, {"Problem":{"arch":"gfx90a","token":"73_sp_ip_complex"}, "Solutions":[ {"sol_node_type":"SOL_DUMMY","using_scheme":"CS_NONE","solution_childnodes":[ ]} ,{"sol_node_type":"SOL_INTERNAL_NODE","using_scheme":"CS_REAL_TRANSFORM_EVEN","solution_childnodes":[ {"child_token":"72_sp_ip_complex","child_option":2} ]} ]}, {"Problem":{"arch":"gfx90a","token":"80_sp_ip_complex"}, "Solutions":[ {"sol_node_type":"SOL_DUMMY","using_scheme":"CS_NONE","solution_childnodes":[ ]} ,{"sol_node_type":"SOL_INTERNAL_NODE","using_scheme":"CS_REAL_TRANSFORM_EVEN","solution_childnodes":[ {"child_token":"40_sp_ip_complex","child_option":1} ]} ]}, {"Problem":{"arch":"gfx90a","token":"85_sp_ip_complex"}, "Solutions":[ {"sol_node_type":"SOL_DUMMY","using_scheme":"CS_NONE","solution_childnodes":[ ]} ,{"sol_node_type":"SOL_INTERNAL_NODE","using_scheme":"CS_REAL_TRANSFORM_EVEN","solution_childnodes":[ {"child_token":"84_sp_ip_complex","child_option":2} ]} ,{"sol_node_type":"SOL_INTERNAL_NODE","using_scheme":"CS_REAL_TRANSFORM_EVEN","solution_childnodes":[ {"child_token":"84_sp_ip_complex","child_option":4} ]} ]}, {"Problem":{"arch":"gfx90a","token":"96_sp_ip_complex"}, "Solutions":[ {"sol_node_type":"SOL_DUMMY","using_scheme":"CS_NONE","solution_childnodes":[ ]} ,{"sol_node_type":"SOL_LEAF_NODE","using_scheme":"CS_KERNEL_STOCKHAM","solution_childnodes":[ {"child_token":"kernel_len96_single_sbrr","child_option":0} ]} ,{"sol_node_type":"SOL_LEAF_NODE","using_scheme":"CS_KERNEL_STOCKHAM","solution_childnodes":[ {"child_token":"kernel_len96_single_sbrr","child_option":1} ]} ,{"sol_node_type":"SOL_LEAF_NODE","using_scheme":"CS_KERNEL_STOCKHAM","solution_childnodes":[ {"child_token":"kernel_len96_single_sbrr","child_option":2} ]} ,{"sol_node_type":"SOL_LEAF_NODE","using_scheme":"CS_KERNEL_STOCKHAM","solution_childnodes":[ {"child_token":"kernel_len96_single_sbrr","child_option":3} ]} ,{"sol_node_type":"SOL_LEAF_NODE","using_scheme":"CS_KERNEL_STOCKHAM","solution_childnodes":[ {"child_token":"kernel_len96_single_sbrr","child_option":4} ]} ,{"sol_node_type":"SOL_LEAF_NODE","using_scheme":"CS_KERNEL_STOCKHAM","solution_childnodes":[ {"child_token":"kernel_len96_single_sbrr","child_option":5} ]} ,{"sol_node_type":"SOL_LEAF_NODE","using_scheme":"CS_KERNEL_STOCKHAM","solution_childnodes":[ {"child_token":"kernel_len96_single_sbrr","child_option":6} ]} ,{"sol_node_type":"SOL_LEAF_NODE","using_scheme":"CS_KERNEL_STOCKHAM","solution_childnodes":[ {"child_token":"kernel_len96_single_sbrr","child_option":7} ]} ,{"sol_node_type":"SOL_INTERNAL_NODE","using_scheme":"CS_REAL_TRANSFORM_EVEN","solution_childnodes":[ {"child_token":"48_sp_ip_complex","child_option":1} ]} ,{"sol_node_type":"SOL_INTERNAL_NODE","using_scheme":"CS_REAL_TRANSFORM_EVEN","solution_childnodes":[ {"child_token":"48_sp_ip_complex","child_option":2} ]} ]}, {"Problem":{"arch":"gfx90a","token":"97_sp_ip_complex"}, "Solutions":[ {"sol_node_type":"SOL_DUMMY","using_scheme":"CS_NONE","solution_childnodes":[ ]} ,{"sol_node_type":"SOL_INTERNAL_NODE","using_scheme":"CS_REAL_TRANSFORM_EVEN","solution_childnodes":[ {"child_token":"96_sp_ip_complex","child_option":2} ]} ,{"sol_node_type":"SOL_INTERNAL_NODE","using_scheme":"CS_REAL_TRANSFORM_EVEN","solution_childnodes":[ {"child_token":"96_sp_ip_complex","child_option":4} ]} ,{"sol_node_type":"SOL_INTERNAL_NODE","using_scheme":"CS_REAL_TRANSFORM_EVEN","solution_childnodes":[ {"child_token":"96_sp_ip_complex","child_option":6} ]} ,{"sol_node_type":"SOL_INTERNAL_NODE","using_scheme":"CS_REAL_TRANSFORM_EVEN","solution_childnodes":[ {"child_token":"96_sp_ip_complex","child_option":8} ]} ]}, {"Problem":{"arch":"gfx90a","token":"52_26_dp_op_real_fwd"}, "Solutions":[ {"sol_node_type":"SOL_INTERNAL_NODE","using_scheme":"CS_REAL_2D_EVEN","solution_childnodes":[ {"child_token":"52_dp_ip_complex","child_option":1},{"child_token":"26_dp_ip_complex","child_option":2} ]} ]}, {"Problem":{"arch":"gfx90a","token":"100_100_100_dp_op_real_bwd"}, "Solutions":[ {"sol_node_type":"SOL_INTERNAL_NODE","using_scheme":"CS_REAL_3D_EVEN","solution_childnodes":[ {"child_token":"sbcc_100_dp_ip_complex","child_option":3},{"child_token":"sbcc_100_dp_ip_complex","child_option":4},{"child_token":"51_dp_ip_complex","child_option":1} ]} ]}, {"Problem":{"arch":"gfx90a","token":"100_100_100_dp_op_real_fwd"}, "Solutions":[ {"sol_node_type":"SOL_INTERNAL_NODE","using_scheme":"CS_REAL_3D_EVEN","solution_childnodes":[ {"child_token":"100_dp_ip_complex","child_option":1},{"child_token":"sbcc_100_dp_ip_complex","child_option":1},{"child_token":"sbcc_100_dp_ip_complex","child_option":2} ]} ]}, {"Problem":{"arch":"gfx90a","token":"100_100_100_sp_op_real_bwd"}, "Solutions":[ {"sol_node_type":"SOL_INTERNAL_NODE","using_scheme":"CS_REAL_3D_EVEN","solution_childnodes":[ {"child_token":"sbcc_100_sp_ip_complex","child_option":2},{"child_token":"sbcc_100_sp_ip_complex","child_option":3},{"child_token":"51_sp_ip_complex","child_option":1} ]} ]}, {"Problem":{"arch":"gfx90a","token":"100_100_100_sp_op_real_fwd"}, "Solutions":[ {"sol_node_type":"SOL_INTERNAL_NODE","using_scheme":"CS_REAL_3D_EVEN","solution_childnodes":[ {"child_token":"100_sp_ip_complex","child_option":1},{"child_token":"sbcc_100_sp_ip_complex","child_option":0},{"child_token":"sbcc_100_sp_ip_complex","child_option":1} ]} ]}, {"Problem":{"arch":"gfx90a","token":"100_100_208_sp_op_real_bwd"}, "Solutions":[ {"sol_node_type":"SOL_INTERNAL_NODE","using_scheme":"CS_REAL_3D_EVEN","solution_childnodes":[ {"child_token":"sbcc_208_sp_ip_complex","child_option":2},{"child_token":"sbcc_100_sp_ip_complex","child_option":3},{"child_token":"51_sp_ip_complex","child_option":3} ]} ]}, {"Problem":{"arch":"gfx90a","token":"100_100_208_sp_op_real_fwd"}, "Solutions":[ {"sol_node_type":"SOL_INTERNAL_NODE","using_scheme":"CS_REAL_3D_EVEN","solution_childnodes":[ {"child_token":"100_sp_ip_complex","child_option":2},{"child_token":"sbcc_208_sp_ip_complex","child_option":0},{"child_token":"sbcc_100_sp_ip_complex","child_option":3} ]} ]}, {"Problem":{"arch":"gfx90a","token":"100_104_216_sp_op_real_bwd"}, "Solutions":[ {"sol_node_type":"SOL_INTERNAL_NODE","using_scheme":"CS_REAL_3D_EVEN","solution_childnodes":[ {"child_token":"sbcc_216_sp_ip_complex","child_option":1},{"child_token":"sbcc_104_sp_ip_complex","child_option":1},{"child_token":"51_sp_ip_complex","child_option":3} ]} ]}, {"Problem":{"arch":"gfx90a","token":"100_104_216_sp_op_real_fwd"}, "Solutions":[ {"sol_node_type":"SOL_INTERNAL_NODE","using_scheme":"CS_REAL_3D_EVEN","solution_childnodes":[ {"child_token":"100_sp_ip_complex","child_option":6},{"child_token":"sbcc_216_sp_ip_complex","child_option":0},{"child_token":"sbcc_104_sp_ip_complex","child_option":0} ]} ]}, {"Problem":{"arch":"gfx90a","token":"104_104_208_dp_op_real_bwd"}, "Solutions":[ {"sol_node_type":"SOL_INTERNAL_NODE","using_scheme":"CS_REAL_3D_EVEN","solution_childnodes":[ {"child_token":"sbcc_208_dp_ip_complex","child_option":1},{"child_token":"sbcc_104_dp_ip_complex","child_option":1},{"child_token":"53_dp_ip_complex","child_option":1} ]} ]}, {"Problem":{"arch":"gfx90a","token":"104_104_208_dp_op_real_fwd"}, "Solutions":[ {"sol_node_type":"SOL_INTERNAL_NODE","using_scheme":"CS_REAL_3D_EVEN","solution_childnodes":[ {"child_token":"104_dp_ip_complex","child_option":1},{"child_token":"sbcc_208_dp_ip_complex","child_option":0},{"child_token":"sbcc_104_dp_ip_complex","child_option":0} ]} ]}, {"Problem":{"arch":"gfx90a","token":"104_108_224_sp_op_real_fwd"}, "Solutions":[ {"sol_node_type":"SOL_INTERNAL_NODE","using_scheme":"CS_REAL_3D_EVEN","solution_childnodes":[ {"child_token":"104_sp_ip_complex","child_option":1},{"child_token":"sbcc_224_sp_ip_complex","child_option":0},{"child_token":"sbcc_108_sp_ip_complex","child_option":2} ]} ]}, {"Problem":{"arch":"gfx90a","token":"108_108_224_sp_op_real_bwd"}, "Solutions":[ {"sol_node_type":"SOL_INTERNAL_NODE","using_scheme":"CS_REAL_3D_EVEN","solution_childnodes":[ {"child_token":"sbcc_224_sp_ip_complex","child_option":2},{"child_token":"sbcc_108_sp_ip_complex","child_option":4},{"child_token":"55_sp_ip_complex","child_option":1} ]} ]}, {"Problem":{"arch":"gfx90a","token":"108_108_224_sp_op_real_fwd"}, "Solutions":[ {"sol_node_type":"SOL_INTERNAL_NODE","using_scheme":"CS_REAL_3D_EVEN","solution_childnodes":[ {"child_token":"108_sp_ip_complex","child_option":1},{"child_token":"sbcc_224_sp_ip_complex","child_option":1},{"child_token":"sbcc_108_sp_ip_complex","child_option":3} ]} ]}, {"Problem":{"arch":"gfx90a","token":"108_108_240_sp_op_real_bwd"}, "Solutions":[ {"sol_node_type":"SOL_INTERNAL_NODE","using_scheme":"CS_REAL_3D_EVEN","solution_childnodes":[ {"child_token":"sbcc_240_sp_ip_complex","child_option":1},{"child_token":"sbcc_108_sp_ip_complex","child_option":6},{"child_token":"55_sp_ip_complex","child_option":1} ]} ]}, {"Problem":{"arch":"gfx90a","token":"108_108_240_sp_op_real_fwd"}, "Solutions":[ {"sol_node_type":"SOL_INTERNAL_NODE","using_scheme":"CS_REAL_3D_EVEN","solution_childnodes":[ {"child_token":"108_sp_ip_complex","child_option":1},{"child_token":"sbcc_240_sp_ip_complex","child_option":0},{"child_token":"sbcc_108_sp_ip_complex","child_option":5} ]} ]}, {"Problem":{"arch":"gfx90a","token":"108_112_240_sp_op_real_bwd"}, "Solutions":[ {"sol_node_type":"SOL_INTERNAL_NODE","using_scheme":"CS_REAL_3D_EVEN","solution_childnodes":[ {"child_token":"sbcc_240_sp_ip_complex","child_option":2},{"child_token":"sbcc_112_sp_ip_complex","child_option":1},{"child_token":"55_sp_ip_complex","child_option":2} ]} ]}, {"Problem":{"arch":"gfx90a","token":"108_112_240_sp_op_real_fwd"}, "Solutions":[ {"sol_node_type":"SOL_INTERNAL_NODE","using_scheme":"CS_REAL_3D_EVEN","solution_childnodes":[ {"child_token":"108_sp_ip_complex","child_option":2},{"child_token":"sbcc_240_sp_ip_complex","child_option":2},{"child_token":"sbcc_112_sp_ip_complex","child_option":0} ]} ]}, {"Problem":{"arch":"gfx90a","token":"112_112_240_sp_op_real_bwd"}, "Solutions":[ {"sol_node_type":"SOL_INTERNAL_NODE","using_scheme":"CS_REAL_3D_EVEN","solution_childnodes":[ {"child_token":"sbcc_240_sp_ip_complex","child_option":4},{"child_token":"sbcc_112_sp_ip_complex","child_option":2},{"child_token":"57_sp_ip_complex","child_option":1} ]} ]}, {"Problem":{"arch":"gfx90a","token":"112_112_240_sp_op_real_fwd"}, "Solutions":[ {"sol_node_type":"SOL_INTERNAL_NODE","using_scheme":"CS_REAL_3D_EVEN","solution_childnodes":[ {"child_token":"112_sp_ip_complex","child_option":1},{"child_token":"sbcc_240_sp_ip_complex","child_option":3},{"child_token":"sbcc_112_sp_ip_complex","child_option":2} ]} ]}, {"Problem":{"arch":"gfx90a","token":"128_128_280_sp_op_real_bwd"}, "Solutions":[ {"sol_node_type":"SOL_INTERNAL_NODE","using_scheme":"CS_REAL_3D_EVEN","solution_childnodes":[ {"child_token":"sbcc_280_sp_ip_complex","child_option":1},{"child_token":"sbcc_128_sp_ip_complex","child_option":3},{"child_token":"65_sp_ip_complex","child_option":1} ]} ]}, {"Problem":{"arch":"gfx90a","token":"128_128_280_sp_op_real_fwd"}, "Solutions":[ {"sol_node_type":"SOL_INTERNAL_NODE","using_scheme":"CS_REAL_3D_EVEN","solution_childnodes":[ {"child_token":"128_sp_ip_complex","child_option":2},{"child_token":"sbcc_280_sp_ip_complex","child_option":0},{"child_token":"sbcc_128_sp_ip_complex","child_option":2} ]} ]}, {"Problem":{"arch":"gfx90a","token":"144_84_80_sp_op_real_bwd"}, "Solutions":[ {"sol_node_type":"SOL_INTERNAL_NODE","using_scheme":"CS_REAL_3D_EVEN","solution_childnodes":[ {"child_token":"sbcc_80_sp_ip_complex","child_option":4},{"child_token":"sbcc_84_sp_ip_complex","child_option":3},{"child_token":"73_sp_ip_complex","child_option":1} ]} ]}, {"Problem":{"arch":"gfx90a","token":"144_84_80_sp_op_real_fwd"}, "Solutions":[ {"sol_node_type":"SOL_INTERNAL_NODE","using_scheme":"CS_REAL_3D_EVEN","solution_childnodes":[ {"child_token":"144_sp_ip_complex","child_option":1},{"child_token":"sbcc_80_sp_ip_complex","child_option":3},{"child_token":"sbcc_84_sp_ip_complex","child_option":2} ]} ]}, {"Problem":{"arch":"gfx90a","token":"14_84_80_sp_op_real_bwd"}, "Solutions":[ {"sol_node_type":"SOL_INTERNAL_NODE","using_scheme":"CS_REAL_3D_EVEN","solution_childnodes":[ {"child_token":"sbcc_80_sp_ip_complex","child_option":2},{"child_token":"84_7_sp_ip_complex","child_option":1} ]} ]}, {"Problem":{"arch":"gfx90a","token":"168_160_160_sp_op_real_bwd"}, "Solutions":[ {"sol_node_type":"SOL_INTERNAL_NODE","using_scheme":"CS_REAL_3D_EVEN","solution_childnodes":[ {"child_token":"sbcc_160_sp_ip_complex","child_option":0},{"child_token":"sbcc_160_sp_ip_complex","child_option":1},{"child_token":"85_sp_ip_complex","child_option":1} ]} ]}, {"Problem":{"arch":"gfx90a","token":"168_160_160_sp_op_real_fwd"}, "Solutions":[ {"sol_node_type":"SOL_INTERNAL_NODE","using_scheme":"CS_REAL_3D_EVEN","solution_childnodes":[ {"child_token":"168_sp_ip_complex","child_option":1},{"child_token":"sbcc_160_sp_ip_complex","child_option":0},{"child_token":"sbcc_160_sp_ip_complex","child_option":1} ]} ]}, {"Problem":{"arch":"gfx90a","token":"168_168_160_sp_op_real_bwd"}, "Solutions":[ {"sol_node_type":"SOL_INTERNAL_NODE","using_scheme":"CS_REAL_3D_EVEN","solution_childnodes":[ {"child_token":"sbcc_160_sp_ip_complex","child_option":1},{"child_token":"sbcc_168_sp_ip_complex","child_option":1},{"child_token":"85_sp_ip_complex","child_option":2} ]} ]}, {"Problem":{"arch":"gfx90a","token":"168_168_160_sp_op_real_fwd"}, "Solutions":[ {"sol_node_type":"SOL_INTERNAL_NODE","using_scheme":"CS_REAL_3D_EVEN","solution_childnodes":[ {"child_token":"168_sp_ip_complex","child_option":2},{"child_token":"sbcc_160_sp_ip_complex","child_option":0},{"child_token":"sbcc_168_sp_ip_complex","child_option":0} ]} ]}, {"Problem":{"arch":"gfx90a","token":"192_168_160_sp_op_real_bwd"}, "Solutions":[ {"sol_node_type":"SOL_INTERNAL_NODE","using_scheme":"CS_REAL_3D_EVEN","solution_childnodes":[ {"child_token":"sbcc_160_sp_ip_complex","child_option":3},{"child_token":"sbcc_168_sp_ip_complex","child_option":3},{"child_token":"97_sp_ip_complex","child_option":1} ]} ]}, {"Problem":{"arch":"gfx90a","token":"192_168_160_sp_op_real_fwd"}, "Solutions":[ {"sol_node_type":"SOL_INTERNAL_NODE","using_scheme":"CS_REAL_3D_EVEN","solution_childnodes":[ {"child_token":"192_sp_ip_complex","child_option":1},{"child_token":"sbcc_160_sp_ip_complex","child_option":2},{"child_token":"sbcc_168_sp_ip_complex","child_option":2} ]} ]}, {"Problem":{"arch":"gfx90a","token":"192_168_168_sp_op_real_bwd"}, "Solutions":[ {"sol_node_type":"SOL_INTERNAL_NODE","using_scheme":"CS_REAL_3D_EVEN","solution_childnodes":[ {"child_token":"sbcc_168_sp_ip_complex","child_option":2},{"child_token":"sbcc_168_sp_ip_complex","child_option":6},{"child_token":"97_sp_ip_complex","child_option":2} ]} ]}, {"Problem":{"arch":"gfx90a","token":"192_168_168_sp_op_real_fwd"}, "Solutions":[ {"sol_node_type":"SOL_INTERNAL_NODE","using_scheme":"CS_REAL_3D_EVEN","solution_childnodes":[ {"child_token":"192_sp_ip_complex","child_option":2},{"child_token":"sbcc_168_sp_ip_complex","child_option":4},{"child_token":"sbcc_168_sp_ip_complex","child_option":5} ]} ]}, {"Problem":{"arch":"gfx90a","token":"192_192_168_sp_op_real_bwd"}, "Solutions":[ {"sol_node_type":"SOL_INTERNAL_NODE","using_scheme":"CS_REAL_3D_EVEN","solution_childnodes":[ {"child_token":"sbcc_168_sp_ip_complex","child_option":7},{"child_token":"192_sp_ip_complex","child_option":4},{"child_token":"97_sp_ip_complex","child_option":3} ]} ]}, {"Problem":{"arch":"gfx90a","token":"192_192_168_sp_op_real_fwd"}, "Solutions":[ {"sol_node_type":"SOL_INTERNAL_NODE","using_scheme":"CS_REAL_3D_EVEN","solution_childnodes":[ {"child_token":"192_sp_ip_complex","child_option":3},{"child_token":"sbcc_168_sp_ip_complex","child_option":5},{"child_token":"192_sp_ip_complex","child_option":4} ]} ]}, {"Problem":{"arch":"gfx90a","token":"192_192_192_sp_op_real_bwd"}, "Solutions":[ {"sol_node_type":"SOL_INTERNAL_NODE","using_scheme":"CS_REAL_3D_EVEN","solution_childnodes":[ {"child_token":"sbcc_192_sp_ip_complex","child_option":2},{"child_token":"sbcc_192_sp_ip_complex","child_option":3},{"child_token":"97_sp_ip_complex","child_option":4} ]} ]}, {"Problem":{"arch":"gfx90a","token":"192_192_192_sp_op_real_fwd"}, "Solutions":[ {"sol_node_type":"SOL_INTERNAL_NODE","using_scheme":"CS_REAL_3D_EVEN","solution_childnodes":[ {"child_token":"192_sp_ip_complex","child_option":5},{"child_token":"sbcc_192_sp_ip_complex","child_option":0},{"child_token":"sbcc_192_sp_ip_complex","child_option":1} ]} ]}, {"Problem":{"arch":"gfx90a","token":"200_192_192_sp_op_real_bwd"}, "Solutions":[ {"sol_node_type":"SOL_INTERNAL_NODE","using_scheme":"CS_REAL_3D_EVEN","solution_childnodes":[ {"child_token":"sbcc_192_sp_ip_complex","child_option":5},{"child_token":"sbcc_192_sp_ip_complex","child_option":6},{"child_token":"101_sp_ip_complex","child_option":1} ]} ]}, {"Problem":{"arch":"gfx90a","token":"200_192_192_sp_op_real_fwd"}, "Solutions":[ {"sol_node_type":"SOL_INTERNAL_NODE","using_scheme":"CS_REAL_3D_EVEN","solution_childnodes":[ {"child_token":"200_sp_ip_complex","child_option":1},{"child_token":"sbcc_192_sp_ip_complex","child_option":0},{"child_token":"sbcc_192_sp_ip_complex","child_option":4} ]} ]}, {"Problem":{"arch":"gfx90a","token":"200_200_192_sp_op_real_bwd"}, "Solutions":[ {"sol_node_type":"SOL_INTERNAL_NODE","using_scheme":"CS_REAL_3D_EVEN","solution_childnodes":[ {"child_token":"192_sp_ip_complex","child_option":7},{"child_token":"sbcc_200_sp_ip_complex","child_option":1},{"child_token":"101_sp_ip_complex","child_option":1} ]} ]}, {"Problem":{"arch":"gfx90a","token":"200_200_192_sp_op_real_fwd"}, "Solutions":[ {"sol_node_type":"SOL_INTERNAL_NODE","using_scheme":"CS_REAL_3D_EVEN","solution_childnodes":[ {"child_token":"200_sp_ip_complex","child_option":2},{"child_token":"192_sp_ip_complex","child_option":6},{"child_token":"sbcc_200_sp_ip_complex","child_option":0} ]} ]}, {"Problem":{"arch":"gfx90a","token":"200_200_200_dp_op_real_fwd"}, "Solutions":[ {"sol_node_type":"SOL_INTERNAL_NODE","using_scheme":"CS_REAL_3D_EVEN","solution_childnodes":[ {"child_token":"200_dp_ip_complex","child_option":1},{"child_token":"sbcc_200_dp_ip_complex","child_option":0},{"child_token":"sbcc_200_dp_ip_complex","child_option":0} ]} ]}, {"Problem":{"arch":"gfx90a","token":"20_20_25_sp_op_real_bwd"}, "Solutions":[ {"sol_node_type":"SOL_INTERNAL_NODE","using_scheme":"CS_REAL_3D_EVEN","solution_childnodes":[ {"child_token":"25_sp_ip_complex","child_option":2},{"child_token":"20_10_sp_ip_complex","child_option":1} ]} ]}, {"Problem":{"arch":"gfx90a","token":"20_20_25_sp_op_real_fwd"}, "Solutions":[ {"sol_node_type":"SOL_INTERNAL_NODE","using_scheme":"CS_REAL_3D_EVEN","solution_childnodes":[ {"child_token":"10_20_sp_ip_complex","child_option":1},{"child_token":"25_sp_ip_complex","child_option":1} ]} ]}, {"Problem":{"arch":"gfx90a","token":"216_216_216_sp_op_real_bwd"}, "Solutions":[ {"sol_node_type":"SOL_INTERNAL_NODE","using_scheme":"CS_REAL_3D_EVEN","solution_childnodes":[ {"child_token":"sbcc_216_sp_ip_complex","child_option":4},{"child_token":"sbcc_216_sp_ip_complex","child_option":5},{"child_token":"109_sp_ip_complex","child_option":1} ]} ]}, {"Problem":{"arch":"gfx90a","token":"216_216_216_sp_op_real_fwd"}, "Solutions":[ {"sol_node_type":"SOL_INTERNAL_NODE","using_scheme":"CS_REAL_3D_EVEN","solution_childnodes":[ {"child_token":"216_sp_ip_complex","child_option":1},{"child_token":"sbcc_216_sp_ip_complex","child_option":2},{"child_token":"sbcc_216_sp_ip_complex","child_option":3} ]} ]}, {"Problem":{"arch":"gfx90a","token":"224_224_240_sp_op_real_bwd"}, "Solutions":[ {"sol_node_type":"SOL_INTERNAL_NODE","using_scheme":"CS_REAL_3D_EVEN","solution_childnodes":[ {"child_token":"sbcc_240_sp_ip_complex","child_option":6},{"child_token":"sbcc_224_sp_ip_complex","child_option":4},{"child_token":"113_sp_ip_complex","child_option":1} ]} ]}, {"Problem":{"arch":"gfx90a","token":"224_224_240_sp_op_real_fwd"}, "Solutions":[ {"sol_node_type":"SOL_INTERNAL_NODE","using_scheme":"CS_REAL_3D_EVEN","solution_childnodes":[ {"child_token":"224_sp_ip_complex","child_option":1},{"child_token":"sbcc_240_sp_ip_complex","child_option":5},{"child_token":"sbcc_224_sp_ip_complex","child_option":3} ]} ]}, {"Problem":{"arch":"gfx90a","token":"256_128_128_sp_op_real_bwd"}, "Solutions":[ {"sol_node_type":"SOL_INTERNAL_NODE","using_scheme":"CS_REAL_3D_EVEN","solution_childnodes":[ {"child_token":"sbcc_128_sp_ip_complex","child_option":0},{"child_token":"sbcc_128_sp_ip_complex","child_option":0},{"child_token":"129_sp_ip_complex","child_option":1} ]} ]}, {"Problem":{"arch":"gfx90a","token":"256_128_128_sp_op_real_fwd"}, "Solutions":[ {"sol_node_type":"SOL_INTERNAL_NODE","using_scheme":"CS_REAL_3D_EVEN","solution_childnodes":[ {"child_token":"256_sp_ip_complex","child_option":3},{"child_token":"sbcc_128_sp_ip_complex","child_option":2},{"child_token":"sbcc_128_sp_ip_complex","child_option":0} ]} ]}, {"Problem":{"arch":"gfx90a","token":"32_32_42_sp_op_real_bwd"}, "Solutions":[ {"sol_node_type":"SOL_INTERNAL_NODE","using_scheme":"CS_REAL_3D_EVEN","solution_childnodes":[ {"child_token":"42_sp_ip_complex","child_option":1},{"child_token":"32_16_sp_ip_complex","child_option":1} ]} ]}, {"Problem":{"arch":"gfx90a","token":"52_64_64_sp_op_real_bwd"}, "Solutions":[ {"sol_node_type":"SOL_INTERNAL_NODE","using_scheme":"CS_REAL_3D_EVEN","solution_childnodes":[ {"child_token":"sbcc_64_sp_ip_complex","child_option":1},{"child_token":"64_26_sp_ip_complex","child_option":1} ]} ]}, {"Problem":{"arch":"gfx90a","token":"52_64_64_sp_op_real_fwd"}, "Solutions":[ {"sol_node_type":"SOL_INTERNAL_NODE","using_scheme":"CS_REAL_3D_EVEN","solution_childnodes":[ {"child_token":"26_64_sp_ip_complex","child_option":1},{"child_token":"sbcc_64_sp_ip_complex","child_option":0} ]} ]}, {"Problem":{"arch":"gfx90a","token":"52_72_72_sp_op_real_bwd"}, "Solutions":[ {"sol_node_type":"SOL_INTERNAL_NODE","using_scheme":"CS_REAL_3D_EVEN","solution_childnodes":[ {"child_token":"sbcc_72_sp_ip_complex","child_option":1},{"child_token":"72_26_sp_ip_complex","child_option":1} ]} ]}, {"Problem":{"arch":"gfx90a","token":"52_72_72_sp_op_real_fwd"}, "Solutions":[ {"sol_node_type":"SOL_INTERNAL_NODE","using_scheme":"CS_REAL_3D_EVEN","solution_childnodes":[ {"child_token":"26_72_sp_ip_complex","child_option":1},{"child_token":"sbcc_72_sp_ip_complex","child_option":0} ]} ]}, {"Problem":{"arch":"gfx90a","token":"60_60_60_sp_op_real_bwd"}, "Solutions":[ {"sol_node_type":"SOL_INTERNAL_NODE","using_scheme":"CS_REAL_3D_EVEN","solution_childnodes":[ {"child_token":"sbcc_60_sp_ip_complex","child_option":1},{"child_token":"60_30_sp_ip_complex","child_option":1} ]} ]}, {"Problem":{"arch":"gfx90a","token":"60_60_60_sp_op_real_fwd"}, "Solutions":[ {"sol_node_type":"SOL_INTERNAL_NODE","using_scheme":"CS_REAL_3D_EVEN","solution_childnodes":[ {"child_token":"30_60_sp_ip_complex","child_option":1},{"child_token":"sbcc_60_sp_ip_complex","child_option":0} ]} ]}, {"Problem":{"arch":"gfx90a","token":"72_72_160_sp_op_real_bwd"}, "Solutions":[ {"sol_node_type":"SOL_INTERNAL_NODE","using_scheme":"CS_REAL_3D_EVEN","solution_childnodes":[ {"child_token":"sbcc_160_sp_ip_complex","child_option":4},{"child_token":"72_36_sp_ip_complex","child_option":1} ]} ]}, {"Problem":{"arch":"gfx90a","token":"72_72_160_sp_op_real_fwd"}, "Solutions":[ {"sol_node_type":"SOL_INTERNAL_NODE","using_scheme":"CS_REAL_3D_EVEN","solution_childnodes":[ {"child_token":"36_72_sp_ip_complex","child_option":1},{"child_token":"sbcc_160_sp_ip_complex","child_option":1} ]} ]}, {"Problem":{"arch":"gfx90a","token":"72_72_72_sp_op_real_bwd"}, "Solutions":[ {"sol_node_type":"SOL_INTERNAL_NODE","using_scheme":"CS_REAL_3D_EVEN","solution_childnodes":[ {"child_token":"sbcc_72_sp_ip_complex","child_option":3},{"child_token":"72_36_sp_ip_complex","child_option":1} ]} ]}, {"Problem":{"arch":"gfx90a","token":"72_72_72_sp_op_real_fwd"}, "Solutions":[ {"sol_node_type":"SOL_INTERNAL_NODE","using_scheme":"CS_REAL_3D_EVEN","solution_childnodes":[ {"child_token":"36_72_sp_ip_complex","child_option":1},{"child_token":"sbcc_72_sp_ip_complex","child_option":2} ]} ]}, {"Problem":{"arch":"gfx90a","token":"72_80_160_sp_op_real_bwd"}, "Solutions":[ {"sol_node_type":"SOL_INTERNAL_NODE","using_scheme":"CS_REAL_3D_EVEN","solution_childnodes":[ {"child_token":"sbcc_160_sp_ip_complex","child_option":4},{"child_token":"80_36_sp_ip_complex","child_option":1} ]} ]}, {"Problem":{"arch":"gfx90a","token":"72_80_160_sp_op_real_fwd"}, "Solutions":[ {"sol_node_type":"SOL_INTERNAL_NODE","using_scheme":"CS_REAL_3D_EVEN","solution_childnodes":[ {"child_token":"36_80_sp_ip_complex","child_option":1},{"child_token":"sbcc_160_sp_ip_complex","child_option":5} ]} ]}, {"Problem":{"arch":"gfx90a","token":"72_84_84_sp_op_real_bwd"}, "Solutions":[ {"sol_node_type":"SOL_INTERNAL_NODE","using_scheme":"CS_REAL_3D_EVEN","solution_childnodes":[ {"child_token":"sbcc_84_sp_ip_complex","child_option":1},{"child_token":"84_36_sp_ip_complex","child_option":1} ]} ]}, {"Problem":{"arch":"gfx90a","token":"72_84_84_sp_op_real_fwd"}, "Solutions":[ {"sol_node_type":"SOL_INTERNAL_NODE","using_scheme":"CS_REAL_3D_EVEN","solution_childnodes":[ {"child_token":"36_84_sp_ip_complex","child_option":1},{"child_token":"sbcc_84_sp_ip_complex","child_option":0} ]} ]}, {"Problem":{"arch":"gfx90a","token":"80_108_108_sp_op_real_fwd"}, "Solutions":[ {"sol_node_type":"SOL_INTERNAL_NODE","using_scheme":"CS_REAL_3D_EVEN","solution_childnodes":[ {"child_token":"80_sp_ip_complex","child_option":1},{"child_token":"sbcc_108_sp_ip_complex","child_option":0},{"child_token":"sbcc_108_sp_ip_complex","child_option":1} ]} ]}, {"Problem":{"arch":"gfx90a","token":"80_80_160_sp_op_real_bwd"}, "Solutions":[ {"sol_node_type":"SOL_INTERNAL_NODE","using_scheme":"CS_REAL_3D_EVEN","solution_childnodes":[ {"child_token":"sbcc_160_sp_ip_complex","child_option":4},{"child_token":"80_40_sp_ip_complex","child_option":1} ]} ]}, {"Problem":{"arch":"gfx90a","token":"80_80_160_sp_op_real_fwd"}, "Solutions":[ {"sol_node_type":"SOL_INTERNAL_NODE","using_scheme":"CS_REAL_3D_EVEN","solution_childnodes":[ {"child_token":"40_80_sp_ip_complex","child_option":1},{"child_token":"sbcc_160_sp_ip_complex","child_option":1} ]} ]}, {"Problem":{"arch":"gfx90a","token":"80_80_168_sp_op_real_bwd"}, "Solutions":[ {"sol_node_type":"SOL_INTERNAL_NODE","using_scheme":"CS_REAL_3D_EVEN","solution_childnodes":[ {"child_token":"sbcc_168_sp_ip_complex","child_option":9},{"child_token":"80_40_sp_ip_complex","child_option":1} ]} ]}, {"Problem":{"arch":"gfx90a","token":"80_80_168_sp_op_real_fwd"}, "Solutions":[ {"sol_node_type":"SOL_INTERNAL_NODE","using_scheme":"CS_REAL_3D_EVEN","solution_childnodes":[ {"child_token":"40_80_sp_ip_complex","child_option":1},{"child_token":"sbcc_168_sp_ip_complex","child_option":8} ]} ]}, {"Problem":{"arch":"gfx90a","token":"80_80_80_sp_op_real_bwd"}, "Solutions":[ {"sol_node_type":"SOL_INTERNAL_NODE","using_scheme":"CS_REAL_3D_EVEN","solution_childnodes":[ {"child_token":"sbcc_80_sp_ip_complex","child_option":1},{"child_token":"80_40_sp_ip_complex","child_option":1} ]} ]}, {"Problem":{"arch":"gfx90a","token":"80_80_80_sp_op_real_fwd"}, "Solutions":[ {"sol_node_type":"SOL_INTERNAL_NODE","using_scheme":"CS_REAL_3D_EVEN","solution_childnodes":[ {"child_token":"40_80_sp_ip_complex","child_option":1},{"child_token":"sbcc_80_sp_ip_complex","child_option":0} ]} ]}, {"Problem":{"arch":"gfx90a","token":"84_84_192_sp_op_real_bwd"}, "Solutions":[ {"sol_node_type":"SOL_INTERNAL_NODE","using_scheme":"CS_REAL_3D_EVEN","solution_childnodes":[ {"child_token":"sbcc_192_sp_ip_complex","child_option":8},{"child_token":"84_42_sp_ip_complex","child_option":1} ]} ]}, {"Problem":{"arch":"gfx90a","token":"84_84_192_sp_op_real_fwd"}, "Solutions":[ {"sol_node_type":"SOL_INTERNAL_NODE","using_scheme":"CS_REAL_3D_EVEN","solution_childnodes":[ {"child_token":"42_84_sp_ip_complex","child_option":1},{"child_token":"sbcc_192_sp_ip_complex","child_option":7} ]} ]}, {"Problem":{"arch":"gfx90a","token":"84_96_192_sp_op_real_bwd"}, "Solutions":[ {"sol_node_type":"SOL_INTERNAL_NODE","using_scheme":"CS_REAL_3D_EVEN","solution_childnodes":[ {"child_token":"sbcc_192_sp_ip_complex","child_option":10},{"child_token":"96_42_sp_ip_complex","child_option":1} ]} ]}, {"Problem":{"arch":"gfx90a","token":"84_96_192_sp_op_real_fwd"}, "Solutions":[ {"sol_node_type":"SOL_INTERNAL_NODE","using_scheme":"CS_REAL_3D_EVEN","solution_childnodes":[ {"child_token":"42_96_sp_ip_complex","child_option":1},{"child_token":"sbcc_192_sp_ip_complex","child_option":9} ]} ]}, {"Problem":{"arch":"gfx90a","token":"96_100_200_sp_op_real_bwd"}, "Solutions":[ {"sol_node_type":"SOL_INTERNAL_NODE","using_scheme":"CS_REAL_3D_EVEN","solution_childnodes":[ {"child_token":"sbcc_200_sp_ip_complex","child_option":3},{"child_token":"sbcc_100_sp_ip_complex","child_option":1},{"child_token":"49_sp_ip_complex","child_option":2} ]} ]}, {"Problem":{"arch":"gfx90a","token":"96_100_200_sp_op_real_fwd"}, "Solutions":[ {"sol_node_type":"SOL_INTERNAL_NODE","using_scheme":"CS_REAL_3D_EVEN","solution_childnodes":[ {"child_token":"96_sp_ip_complex","child_option":9},{"child_token":"sbcc_200_sp_ip_complex","child_option":2},{"child_token":"sbcc_100_sp_ip_complex","child_option":1} ]} ]}, {"Problem":{"arch":"gfx90a","token":"96_96_192_sp_op_real_bwd"}, "Solutions":[ {"sol_node_type":"SOL_INTERNAL_NODE","using_scheme":"CS_REAL_3D_EVEN","solution_childnodes":[ {"child_token":"192_sp_ip_complex","child_option":9},{"child_token":"sbcc_96_sp_ip_complex","child_option":1},{"child_token":"49_sp_ip_complex","child_option":1} ]} ]}, {"Problem":{"arch":"gfx90a","token":"96_96_192_sp_op_real_fwd"}, "Solutions":[ {"sol_node_type":"SOL_INTERNAL_NODE","using_scheme":"CS_REAL_3D_EVEN","solution_childnodes":[ {"child_token":"96_sp_ip_complex","child_option":9},{"child_token":"192_sp_ip_complex","child_option":8},{"child_token":"sbcc_96_sp_ip_complex","child_option":0} ]} ]}, {"Problem":{"arch":"gfx90a","token":"96_96_200_sp_op_real_fwd"}, "Solutions":[ {"sol_node_type":"SOL_INTERNAL_NODE","using_scheme":"CS_REAL_3D_EVEN","solution_childnodes":[ {"child_token":"96_sp_ip_complex","child_option":9},{"child_token":"sbcc_200_sp_ip_complex","child_option":3},{"child_token":"sbcc_96_sp_ip_complex","child_option":2} ]} ]}, {"Problem":{"arch":"gfx90a","token":"96_96_96_sp_op_real_bwd"}, "Solutions":[ {"sol_node_type":"SOL_INTERNAL_NODE","using_scheme":"CS_REAL_3D_EVEN","solution_childnodes":[ {"child_token":"sbcc_96_sp_ip_complex","child_option":4},{"child_token":"sbcc_96_sp_ip_complex","child_option":5},{"child_token":"49_sp_ip_complex","child_option":3} ]} ]}, {"Problem":{"arch":"gfx90a","token":"96_96_96_sp_op_real_fwd"}, "Solutions":[ {"sol_node_type":"SOL_INTERNAL_NODE","using_scheme":"CS_REAL_3D_EVEN","solution_childnodes":[ {"child_token":"96_sp_ip_complex","child_option":10},{"child_token":"sbcc_96_sp_ip_complex","child_option":3},{"child_token":"sbcc_96_sp_ip_complex","child_option":2} ]} ]}, {"Problem":{"arch":"gfx90a","token":"14348907_sp_ip_complex"}, "Solutions":[ {"sol_node_type":"SOL_INTERNAL_NODE","using_scheme":"CS_L1D_TRTRT","solution_childnodes":[ {"child_token":"leafnode_token_builtin_kernel","child_option":0},{"child_token":"6561_sp_ip_complex","child_option":3},{"child_token":"leafnode_token_builtin_kernel","child_option":0},{"child_token":"2187_sp_ip_complex","child_option":2},{"child_token":"leafnode_token_builtin_kernel","child_option":0} ]} ]}, {"Problem":{"arch":"gfx90a","token":"16777216_sp_ip_complex"}, "Solutions":[ {"sol_node_type":"SOL_INTERNAL_NODE","using_scheme":"CS_L1D_TRTRT","solution_childnodes":[ {"child_token":"leafnode_token_builtin_kernel","child_option":0},{"child_token":"4096_sp_ip_complex","child_option":1},{"child_token":"leafnode_token_builtin_kernel","child_option":0},{"child_token":"4096_sp_ip_complex","child_option":1},{"child_token":"leafnode_token_builtin_kernel","child_option":0} ]} ]}, {"Problem":{"arch":"gfx90a","token":"10000_dp_op_complex"}, "Solutions":[ {"sol_node_type":"SOL_INTERNAL_NODE","using_scheme":"CS_L1D_CC","solution_childnodes":[ {"child_token":"sbcc_100_dp_ip_complex","child_option":0},{"child_token":"sbrc_100_dp_ip_complex","child_option":0} ]} ]}, {"Problem":{"arch":"gfx90a","token":"10752_dp_op_complex"}, "Solutions":[ {"sol_node_type":"SOL_INTERNAL_NODE","using_scheme":"CS_L1D_CC","solution_childnodes":[ {"child_token":"sbcc_96_dp_ip_complex","child_option":1},{"child_token":"sbrc_112_dp_ip_complex","child_option":1} ]} ]}, {"Problem":{"arch":"gfx90a","token":"15625_dp_op_complex"}, "Solutions":[ {"sol_node_type":"SOL_INTERNAL_NODE","using_scheme":"CS_L1D_CC","solution_childnodes":[ {"child_token":"sbcc_125_dp_ip_complex","child_option":1},{"child_token":"sbrc_125_dp_ip_complex","child_option":0} ]} ]}, {"Problem":{"arch":"gfx90a","token":"16384_dp_op_complex"}, "Solutions":[ {"sol_node_type":"SOL_INTERNAL_NODE","using_scheme":"CS_L1D_CC","solution_childnodes":[ {"child_token":"sbcc_64_dp_ip_complex","child_option":1},{"child_token":"sbrc_256_dp_ip_complex","child_option":0} ]} ]}, {"Problem":{"arch":"gfx90a","token":"16807_dp_ip_complex"}, "Solutions":[ {"sol_node_type":"SOL_DUMMY","using_scheme":"CS_NONE","solution_childnodes":[ ]} ,{"sol_node_type":"SOL_INTERNAL_NODE","using_scheme":"CS_L1D_CC","solution_childnodes":[ {"child_token":"sbcc_343_dp_ip_complex","child_option":1},{"child_token":"sbrc_49_dp_ip_complex","child_option":1} ]} ]}, {"Problem":{"arch":"gfx90a","token":"16807_dp_op_complex"}, "Solutions":[ {"sol_node_type":"SOL_INTERNAL_NODE","using_scheme":"CS_L1D_CC","solution_childnodes":[ {"child_token":"sbcc_343_dp_ip_complex","child_option":0},{"child_token":"sbrc_49_dp_ip_complex","child_option":0} ]} ]}, {"Problem":{"arch":"gfx90a","token":"18816_dp_op_complex"}, "Solutions":[ {"sol_node_type":"SOL_INTERNAL_NODE","using_scheme":"CS_L1D_CC","solution_childnodes":[ {"child_token":"sbcc_168_dp_ip_complex","child_option":0},{"child_token":"sbrc_112_dp_ip_complex","child_option":2} ]} ]}, {"Problem":{"arch":"gfx90a","token":"21504_dp_op_complex"}, "Solutions":[ {"sol_node_type":"SOL_INTERNAL_NODE","using_scheme":"CS_L1D_CC","solution_childnodes":[ {"child_token":"sbcc_168_dp_ip_complex","child_option":0},{"child_token":"sbrc_128_dp_ip_complex","child_option":0} ]} ]}, {"Problem":{"arch":"gfx90a","token":"32256_dp_op_complex"}, "Solutions":[ {"sol_node_type":"SOL_INTERNAL_NODE","using_scheme":"CS_L1D_CC","solution_childnodes":[ {"child_token":"sbcc_168_dp_ip_complex","child_option":2},{"child_token":"sbrc_192_dp_ip_complex","child_option":0} ]} ]}, {"Problem":{"arch":"gfx90a","token":"43008_dp_op_complex"}, "Solutions":[ {"sol_node_type":"SOL_INTERNAL_NODE","using_scheme":"CS_L1D_CC","solution_childnodes":[ {"child_token":"sbcc_224_dp_ip_complex","child_option":0},{"child_token":"sbrc_192_dp_ip_complex","child_option":2} ]} ]}, {"Problem":{"arch":"gfx90a","token":"6561_sp_ip_complex"}, "Solutions":[ {"sol_node_type":"SOL_DUMMY","using_scheme":"CS_NONE","solution_childnodes":[ ]} ,{"sol_node_type":"SOL_INTERNAL_NODE","using_scheme":"CS_L1D_CC","solution_childnodes":[ {"child_token":"sbcc_81_sp_ip_complex","child_option":0},{"child_token":"sbrc_81_sp_ip_complex","child_option":0} ]} ,{"sol_node_type":"SOL_INTERNAL_NODE","using_scheme":"CS_L1D_CC","solution_childnodes":[ {"child_token":"sbcc_81_sp_ip_complex","child_option":0},{"child_token":"sbrc_81_sp_ip_complex","child_option":1} ]} ,{"sol_node_type":"SOL_INTERNAL_NODE","using_scheme":"CS_L1D_CC","solution_childnodes":[ {"child_token":"sbcc_81_sp_ip_complex","child_option":1},{"child_token":"sbrc_81_sp_ip_complex","child_option":2} ]} ]}, {"Problem":{"arch":"gfx90a","token":"8192_dp_ip_complex"}, "Solutions":[ {"sol_node_type":"SOL_DUMMY","using_scheme":"CS_NONE","solution_childnodes":[ ]} ,{"sol_node_type":"SOL_INTERNAL_NODE","using_scheme":"CS_L1D_CC","solution_childnodes":[ {"child_token":"sbcc_64_dp_ip_complex","child_option":2},{"child_token":"sbrc_128_dp_ip_complex","child_option":2} ]} ,{"sol_node_type":"SOL_INTERNAL_NODE","using_scheme":"CS_L1D_CC","solution_childnodes":[ {"child_token":"sbcc_64_dp_ip_complex","child_option":3},{"child_token":"sbrc_128_dp_ip_complex","child_option":3} ]} ,{"sol_node_type":"SOL_INTERNAL_NODE","using_scheme":"CS_L1D_CC","solution_childnodes":[ {"child_token":"sbcc_64_dp_ip_complex","child_option":4},{"child_token":"sbrc_128_dp_ip_complex","child_option":4} ]} ]}, {"Problem":{"arch":"gfx90a","token":"8192_dp_op_complex"}, "Solutions":[ {"sol_node_type":"SOL_INTERNAL_NODE","using_scheme":"CS_L1D_CC","solution_childnodes":[ {"child_token":"sbcc_64_dp_ip_complex","child_option":0},{"child_token":"sbrc_128_dp_ip_complex","child_option":1} ]} ]}, {"Problem":{"arch":"gfx90a","token":"4096_4096_sp_ip_complex"}, "Solutions":[ {"sol_node_type":"SOL_INTERNAL_NODE","using_scheme":"CS_2D_RTRT","solution_childnodes":[ {"child_token":"4096_sp_ip_complex","child_option":1},{"child_token":"leafnode_token_builtin_kernel","child_option":0},{"child_token":"4096_sp_ip_complex","child_option":1},{"child_token":"leafnode_token_builtin_kernel","child_option":0} ]} ]}, {"Problem":{"arch":"gfx90a","token":"55_55_dp_ip_complex"}, "Solutions":[ {"sol_node_type":"SOL_DUMMY","using_scheme":"CS_NONE","solution_childnodes":[ ]} ,{"sol_node_type":"SOL_INTERNAL_NODE","using_scheme":"CS_2D_RTRT","solution_childnodes":[ {"child_token":"55_dp_ip_complex","child_option":1},{"child_token":"leafnode_token_builtin_kernel","child_option":0},{"child_token":"55_dp_ip_complex","child_option":2},{"child_token":"leafnode_token_builtin_kernel","child_option":0} ]} ]}, {"Problem":{"arch":"gfx90a","token":"6561_6561_sp_ip_complex"}, "Solutions":[ {"sol_node_type":"SOL_INTERNAL_NODE","using_scheme":"CS_2D_RTRT","solution_childnodes":[ {"child_token":"6561_sp_ip_complex","child_option":2},{"child_token":"leafnode_token_builtin_kernel","child_option":0},{"child_token":"6561_sp_ip_complex","child_option":2},{"child_token":"leafnode_token_builtin_kernel","child_option":0} ]} ]}, {"Problem":{"arch":"gfx90a","token":"125_125_sp_ip_complex"}, "Solutions":[ {"sol_node_type":"SOL_DUMMY","using_scheme":"CS_NONE","solution_childnodes":[ ]} ,{"sol_node_type":"SOL_INTERNAL_NODE","using_scheme":"CS_2D_RC","solution_childnodes":[ {"child_token":"125_sp_ip_complex","child_option":1},{"child_token":"sbcc_125_sp_ip_complex","child_option":0} ]} ,{"sol_node_type":"SOL_INTERNAL_NODE","using_scheme":"CS_2D_RC","solution_childnodes":[ {"child_token":"125_sp_ip_complex","child_option":2},{"child_token":"sbcc_125_sp_ip_complex","child_option":0} ]} ]}, {"Problem":{"arch":"gfx90a","token":"243_243_sp_ip_complex"}, "Solutions":[ {"sol_node_type":"SOL_DUMMY","using_scheme":"CS_NONE","solution_childnodes":[ ]} ,{"sol_node_type":"SOL_INTERNAL_NODE","using_scheme":"CS_2D_RC","solution_childnodes":[ {"child_token":"243_sp_ip_complex","child_option":1},{"child_token":"sbcc_243_sp_ip_complex","child_option":0} ]} ]}, {"Problem":{"arch":"gfx90a","token":"56_336_dp_ip_complex"}, "Solutions":[ {"sol_node_type":"SOL_DUMMY","using_scheme":"CS_NONE","solution_childnodes":[ ]} ,{"sol_node_type":"SOL_INTERNAL_NODE","using_scheme":"CS_2D_RC","solution_childnodes":[ {"child_token":"56_dp_ip_complex","child_option":1},{"child_token":"sbcc_336_dp_ip_complex","child_option":0} ]} ,{"sol_node_type":"SOL_INTERNAL_NODE","using_scheme":"CS_2D_RC","solution_childnodes":[ {"child_token":"56_dp_ip_complex","child_option":2},{"child_token":"sbcc_336_dp_ip_complex","child_option":2} ]} ,{"sol_node_type":"SOL_INTERNAL_NODE","using_scheme":"CS_2D_RC","solution_childnodes":[ {"child_token":"56_dp_ip_complex","child_option":3},{"child_token":"sbcc_336_dp_ip_complex","child_option":4} ]} ,{"sol_node_type":"SOL_INTERNAL_NODE","using_scheme":"CS_2D_RC","solution_childnodes":[ {"child_token":"56_dp_ip_complex","child_option":4},{"child_token":"sbcc_336_dp_ip_complex","child_option":5} ]} ,{"sol_node_type":"SOL_INTERNAL_NODE","using_scheme":"CS_2D_RC","solution_childnodes":[ {"child_token":"56_dp_ip_complex","child_option":1},{"child_token":"sbcc_336_dp_ip_complex","child_option":7} ]} ]}, {"Problem":{"arch":"gfx90a","token":"81_81_dp_ip_complex"}, "Solutions":[ {"sol_node_type":"SOL_DUMMY","using_scheme":"CS_NONE","solution_childnodes":[ ]} ,{"sol_node_type":"SOL_INTERNAL_NODE","using_scheme":"CS_2D_RC","solution_childnodes":[ {"child_token":"81_dp_ip_complex","child_option":1},{"child_token":"sbcc_81_dp_ip_complex","child_option":0} ]} ]}, {"Problem":{"arch":"gfx90a","token":"81_81_dp_op_complex"}, "Solutions":[ {"sol_node_type":"SOL_INTERNAL_NODE","using_scheme":"CS_2D_RC","solution_childnodes":[ {"child_token":"81_dp_ip_complex","child_option":1},{"child_token":"sbcc_81_dp_ip_complex","child_option":0} ]} ]}, {"Problem":{"arch":"gfx90a","token":"55_55_75_dp_ip_complex"}, "Solutions":[ {"sol_node_type":"SOL_DUMMY","using_scheme":"CS_NONE","solution_childnodes":[ ]} ,{"sol_node_type":"SOL_INTERNAL_NODE","using_scheme":"CS_3D_TRTRTR","solution_childnodes":[ {"child_token":"leafnode_token_builtin_kernel","child_option":2},{"child_token":"55_dp_ip_complex","child_option":3},{"child_token":"leafnode_token_builtin_kernel","child_option":2},{"child_token":"75_dp_ip_complex","child_option":1},{"child_token":"leafnode_token_builtin_kernel","child_option":2},{"child_token":"55_dp_ip_complex","child_option":3} ]} ]}, {"Problem":{"arch":"gfx90a","token":"55_55_75_sp_ip_complex"}, "Solutions":[ {"sol_node_type":"SOL_DUMMY","using_scheme":"CS_NONE","solution_childnodes":[ ]} ,{"sol_node_type":"SOL_INTERNAL_NODE","using_scheme":"CS_3D_TRTRTR","solution_childnodes":[ {"child_token":"leafnode_token_builtin_kernel","child_option":2},{"child_token":"55_sp_ip_complex","child_option":3},{"child_token":"leafnode_token_builtin_kernel","child_option":2},{"child_token":"75_sp_ip_complex","child_option":1},{"child_token":"leafnode_token_builtin_kernel","child_option":2},{"child_token":"55_sp_ip_complex","child_option":4} ]} ,{"sol_node_type":"SOL_INTERNAL_NODE","using_scheme":"CS_3D_TRTRTR","solution_childnodes":[ {"child_token":"leafnode_token_builtin_kernel","child_option":2},{"child_token":"55_sp_ip_complex","child_option":5},{"child_token":"leafnode_token_builtin_kernel","child_option":2},{"child_token":"75_sp_ip_complex","child_option":1},{"child_token":"leafnode_token_builtin_kernel","child_option":2},{"child_token":"55_sp_ip_complex","child_option":6} ]} ]}, {"Problem":{"arch":"gfx90a","token":"243_243_243_sp_ip_complex"}, "Solutions":[ {"sol_node_type":"SOL_INTERNAL_NODE","using_scheme":"CS_3D_RTRT","solution_childnodes":[ {"child_token":"243_243_sp_ip_complex","child_option":1},{"child_token":"leafnode_token_builtin_kernel","child_option":1},{"child_token":"243_sp_ip_complex","child_option":2},{"child_token":"leafnode_token_builtin_kernel","child_option":2} ]} ]}, {"Problem":{"arch":"gfx90a","token":"256_256_256_sp_ip_complex"}, "Solutions":[ {"sol_node_type":"SOL_INTERNAL_NODE","using_scheme":"CS_3D_BLOCK_RC","solution_childnodes":[ {"child_token":"sbrc_xy_z_256_sp_ip_complex","child_option":0},{"child_token":"sbrc_xy_z_256_sp_ip_complex","child_option":1},{"child_token":"256_sp_ip_complex","child_option":1},{"child_token":"leafnode_token_builtin_kernel","child_option":1} ]} ]}, {"Problem":{"arch":"gfx90a","token":"56_336_336_dp_op_complex"}, "Solutions":[ {"sol_node_type":"SOL_INTERNAL_NODE","using_scheme":"CS_3D_BLOCK_CR","solution_childnodes":[ {"child_token":"sbcr_336_dp_ip_complex","child_option":2},{"child_token":"sbcr_336_dp_ip_complex","child_option":3},{"child_token":"sbcr_56_dp_ip_complex","child_option":1} ]} ]}, {"Problem":{"arch":"gfx90a","token":"56_336_336_dp_op_complex_fwd_batch_1_istride_1_56_18816_ostride_1_56_18816_idist_6322176_odist_6322176_ioffset_0_ooffset_0"}, "Solutions":[ {"sol_node_type":"SOL_INTERNAL_NODE","using_scheme":"CS_3D_BLOCK_CR","solution_childnodes":[ {"child_token":"sbcr_336_dp_ip_complex","child_option":0},{"child_token":"sbcr_336_dp_ip_complex","child_option":1},{"child_token":"sbcr_56_dp_ip_complex","child_option":0} ]} ]}, {"Problem":{"arch":"gfx90a","token":"125_125_125_sp_ip_complex"}, "Solutions":[ {"sol_node_type":"SOL_INTERNAL_NODE","using_scheme":"CS_3D_RC","solution_childnodes":[ {"child_token":"125_125_sp_ip_complex","child_option":2},{"child_token":"sbcc_125_sp_ip_complex","child_option":2} ]} ]}, {"Problem":{"arch":"gfx90a","token":"56_336_336_dp_ip_complex"}, "Solutions":[ {"sol_node_type":"SOL_INTERNAL_NODE","using_scheme":"CS_3D_RC","solution_childnodes":[ {"child_token":"56_336_dp_ip_complex","child_option":5},{"child_token":"sbcc_336_dp_ip_complex","child_option":6} ]} ]}, {"Problem":{"arch":"gfx90a","token":"56_336_336_dp_ip_complex_fwd_batch_1_istride_1_56_18816_ostride_1_56_18816_idist_6322176_odist_6322176_ioffset_0_ooffset_0"}, "Solutions":[ {"sol_node_type":"SOL_INTERNAL_NODE","using_scheme":"CS_3D_RC","solution_childnodes":[ {"child_token":"56_336_dp_ip_complex","child_option":4},{"child_token":"sbcc_336_dp_ip_complex","child_option":6} ]} ]}, {"Problem":{"arch":"gfx90a","token":"81_81_81_dp_op_complex"}, "Solutions":[ {"sol_node_type":"SOL_INTERNAL_NODE","using_scheme":"CS_3D_RC","solution_childnodes":[ {"child_token":"81_81_dp_ip_complex","child_option":1},{"child_token":"sbcc_81_dp_ip_complex","child_option":0} ]} ]} ] }upstream/solution_map/gfx908_rocfft_solution_map.dat0000664000175000017500000012512714637212425021760 0ustar kaolkaol{"Version":3, "Data":[ {"Problem":{"arch":"gfx908","token":"kernel_token_builtin_kernel"}, "Solutions":[ {"sol_node_type":"SOL_BUILTIN_KERNEL"} ]}, {"Problem":{"arch":"gfx908","token":"kernel_len125_single_sbrr"}, "Solutions":[ {"sol_node_type":"SOL_KERNEL_ONLY","kernel_key":{"lengths":[ 125,0 ],"precision":"single","scheme":"CS_KERNEL_STOCKHAM","sbrc_trans":"NONE","kernelConfig":{"use_3steps":false,"half_lds":true,"dir_reg":true,"buffer_inst":false,"tpb":18,"wgs":450,"tpt":[ 25,0 ],"factors":[ 5,5,5 ],"ebtype":"NONE","direction":-1,"static_dim":0,"placement":"NA","iAryType":"CI","oAryType":"CI"}}} ,{"sol_node_type":"SOL_KERNEL_ONLY","kernel_key":{"lengths":[ 125,0 ],"precision":"single","scheme":"CS_KERNEL_STOCKHAM","sbrc_trans":"NONE","kernelConfig":{"use_3steps":false,"half_lds":false,"dir_reg":true,"buffer_inst":false,"tpb":10,"wgs":250,"tpt":[ 25,0 ],"factors":[ 5,5,5 ],"ebtype":"NONE","direction":-1,"static_dim":0,"placement":"NA","iAryType":"CI","oAryType":"CI"}}} ]}, {"Problem":{"arch":"gfx908","token":"kernel_len2187_single_sbrr"}, "Solutions":[ {"sol_node_type":"SOL_KERNEL_ONLY","kernel_key":{"lengths":[ 2187,0 ],"precision":"single","scheme":"CS_KERNEL_STOCKHAM","sbrc_trans":"NONE","kernelConfig":{"use_3steps":false,"half_lds":false,"dir_reg":true,"buffer_inst":false,"tpb":1,"wgs":243,"tpt":[ 243,0 ],"factors":[ 9,9,3,3,3 ],"ebtype":"NONE","direction":-1,"static_dim":0,"placement":"NA","iAryType":"CI","oAryType":"CI"}}} ]}, {"Problem":{"arch":"gfx908","token":"kernel_len243_single_sbrr"}, "Solutions":[ {"sol_node_type":"SOL_KERNEL_ONLY","kernel_key":{"lengths":[ 243,0 ],"precision":"single","scheme":"CS_KERNEL_STOCKHAM","sbrc_trans":"NONE","kernelConfig":{"use_3steps":false,"half_lds":false,"dir_reg":true,"buffer_inst":false,"tpb":8,"wgs":216,"tpt":[ 27,0 ],"factors":[ 9,3,3,3 ],"ebtype":"NONE","direction":-1,"static_dim":0,"placement":"NA","iAryType":"CI","oAryType":"CI"}}} ,{"sol_node_type":"SOL_KERNEL_ONLY","kernel_key":{"lengths":[ 243,0 ],"precision":"single","scheme":"CS_KERNEL_STOCKHAM","sbrc_trans":"NONE","kernelConfig":{"use_3steps":false,"half_lds":false,"dir_reg":true,"buffer_inst":false,"tpb":4,"wgs":108,"tpt":[ 27,0 ],"factors":[ 9,3,3,3 ],"ebtype":"NONE","direction":-1,"static_dim":0,"placement":"NA","iAryType":"CI","oAryType":"CI"}}} ]}, {"Problem":{"arch":"gfx908","token":"kernel_len256_single_sbrr"}, "Solutions":[ {"sol_node_type":"SOL_KERNEL_ONLY","kernel_key":{"lengths":[ 256,0 ],"precision":"single","scheme":"CS_KERNEL_STOCKHAM","sbrc_trans":"NONE","kernelConfig":{"use_3steps":false,"half_lds":false,"dir_reg":true,"buffer_inst":false,"tpb":4,"wgs":128,"tpt":[ 32,0 ],"factors":[ 4,2,8,4 ],"ebtype":"NONE","direction":-1,"static_dim":0,"placement":"NA","iAryType":"CI","oAryType":"CI"}}} ,{"sol_node_type":"SOL_KERNEL_ONLY","kernel_key":{"lengths":[ 256,0 ],"precision":"single","scheme":"CS_KERNEL_STOCKHAM","sbrc_trans":"NONE","kernelConfig":{"use_3steps":false,"half_lds":false,"dir_reg":true,"buffer_inst":false,"tpb":4,"wgs":128,"tpt":[ 32,0 ],"factors":[ 8,2,8,2 ],"ebtype":"NONE","direction":-1,"static_dim":0,"placement":"NA","iAryType":"CI","oAryType":"CI"}}} ]}, {"Problem":{"arch":"gfx908","token":"kernel_len4096_single_sbrr"}, "Solutions":[ {"sol_node_type":"SOL_KERNEL_ONLY","kernel_key":{"lengths":[ 4096,0 ],"precision":"single","scheme":"CS_KERNEL_STOCKHAM","sbrc_trans":"NONE","kernelConfig":{"use_3steps":false,"half_lds":true,"dir_reg":true,"buffer_inst":false,"tpb":2,"wgs":256,"tpt":[ 128,0 ],"factors":[ 8,16,4,8 ],"ebtype":"NONE","direction":-1,"static_dim":0,"placement":"NA","iAryType":"CI","oAryType":"CI"}}} ,{"sol_node_type":"SOL_KERNEL_ONLY","kernel_key":{"lengths":[ 4096,0 ],"precision":"single","scheme":"CS_KERNEL_STOCKHAM","sbrc_trans":"NONE","kernelConfig":{"use_3steps":false,"half_lds":true,"dir_reg":true,"buffer_inst":false,"tpb":2,"wgs":512,"tpt":[ 256,0 ],"factors":[ 8,8,16,4 ],"ebtype":"NONE","direction":-1,"static_dim":0,"placement":"NA","iAryType":"CI","oAryType":"CI"}}} ,{"sol_node_type":"SOL_KERNEL_ONLY","kernel_key":{"lengths":[ 4096,0 ],"precision":"single","scheme":"CS_KERNEL_STOCKHAM","sbrc_trans":"NONE","kernelConfig":{"use_3steps":false,"half_lds":true,"dir_reg":true,"buffer_inst":false,"tpb":2,"wgs":512,"tpt":[ 256,0 ],"factors":[ 4,8,8,4,4 ],"ebtype":"NONE","direction":-1,"static_dim":0,"placement":"NA","iAryType":"CI","oAryType":"CI"}}} ]}, {"Problem":{"arch":"gfx908","token":"kernel_len56_double_sbrr"}, "Solutions":[ {"sol_node_type":"SOL_KERNEL_ONLY","kernel_key":{"lengths":[ 56,0 ],"precision":"double","scheme":"CS_KERNEL_STOCKHAM","sbrc_trans":"NONE","kernelConfig":{"use_3steps":false,"half_lds":false,"dir_reg":true,"buffer_inst":false,"tpb":32,"wgs":256,"tpt":[ 8,0 ],"factors":[ 2,2,7,2 ],"ebtype":"NONE","direction":-1,"static_dim":0,"placement":"NA","iAryType":"CI","oAryType":"CI"}}} ,{"sol_node_type":"SOL_KERNEL_ONLY","kernel_key":{"lengths":[ 56,0 ],"precision":"double","scheme":"CS_KERNEL_STOCKHAM","sbrc_trans":"NONE","kernelConfig":{"use_3steps":false,"half_lds":false,"dir_reg":true,"buffer_inst":false,"tpb":32,"wgs":256,"tpt":[ 8,0 ],"factors":[ 7,4,2 ],"ebtype":"NONE","direction":-1,"static_dim":0,"placement":"NA","iAryType":"CI","oAryType":"CI"}}} ]}, {"Problem":{"arch":"gfx908","token":"kernel_len100_double_sbcc"}, "Solutions":[ {"sol_node_type":"SOL_KERNEL_ONLY","kernel_key":{"lengths":[ 100,0 ],"precision":"double","scheme":"CS_KERNEL_STOCKHAM_BLOCK_CC","sbrc_trans":"NONE","kernelConfig":{"use_3steps":false,"half_lds":true,"dir_reg":true,"buffer_inst":true,"tpb":12,"wgs":120,"tpt":[ 10,0 ],"factors":[ 10,5,2 ],"ebtype":"NONE","direction":-1,"static_dim":0,"placement":"NA","iAryType":"CI","oAryType":"CI"}}} ]}, {"Problem":{"arch":"gfx908","token":"kernel_len125_single_sbcc"}, "Solutions":[ {"sol_node_type":"SOL_KERNEL_ONLY","kernel_key":{"lengths":[ 125,0 ],"precision":"single","scheme":"CS_KERNEL_STOCKHAM_BLOCK_CC","sbrc_trans":"NONE","kernelConfig":{"use_3steps":false,"half_lds":true,"dir_reg":true,"buffer_inst":true,"tpb":26,"wgs":130,"tpt":[ 5,0 ],"factors":[ 5,5,5 ],"ebtype":"NONE","direction":-1,"static_dim":0,"placement":"NA","iAryType":"CI","oAryType":"CI"}}} ,{"sol_node_type":"SOL_KERNEL_ONLY","kernel_key":{"lengths":[ 125,0 ],"precision":"single","scheme":"CS_KERNEL_STOCKHAM_BLOCK_CC","sbrc_trans":"NONE","kernelConfig":{"use_3steps":false,"half_lds":false,"dir_reg":true,"buffer_inst":true,"tpb":25,"wgs":125,"tpt":[ 5,0 ],"factors":[ 5,5,5 ],"ebtype":"NONE","direction":-1,"static_dim":0,"placement":"NA","iAryType":"CI","oAryType":"CI"}}} ]}, {"Problem":{"arch":"gfx908","token":"kernel_len168_double_sbcc"}, "Solutions":[ {"sol_node_type":"SOL_KERNEL_ONLY","kernel_key":{"lengths":[ 168,0 ],"precision":"double","scheme":"CS_KERNEL_STOCKHAM_BLOCK_CC","sbrc_trans":"NONE","kernelConfig":{"use_3steps":true,"half_lds":true,"dir_reg":true,"buffer_inst":true,"tpb":8,"wgs":64,"tpt":[ 8,0 ],"factors":[ 7,3,8 ],"ebtype":"NONE","direction":-1,"static_dim":0,"placement":"NA","iAryType":"CI","oAryType":"CI"}}} ,{"sol_node_type":"SOL_KERNEL_ONLY","kernel_key":{"lengths":[ 168,0 ],"precision":"double","scheme":"CS_KERNEL_STOCKHAM_BLOCK_CC","sbrc_trans":"NONE","kernelConfig":{"use_3steps":false,"half_lds":false,"dir_reg":true,"buffer_inst":true,"tpb":12,"wgs":168,"tpt":[ 14,0 ],"factors":[ 2,6,7,2 ],"ebtype":"NONE","direction":-1,"static_dim":0,"placement":"NA","iAryType":"CI","oAryType":"CI"}}} ,{"sol_node_type":"SOL_KERNEL_ONLY","kernel_key":{"lengths":[ 168,0 ],"precision":"double","scheme":"CS_KERNEL_STOCKHAM_BLOCK_CC","sbrc_trans":"NONE","kernelConfig":{"use_3steps":false,"half_lds":false,"dir_reg":true,"buffer_inst":true,"tpb":12,"wgs":168,"tpt":[ 14,0 ],"factors":[ 6,7,2,2 ],"ebtype":"NONE","direction":-1,"static_dim":0,"placement":"NA","iAryType":"CI","oAryType":"CI"}}} ,{"sol_node_type":"SOL_KERNEL_ONLY","kernel_key":{"lengths":[ 168,0 ],"precision":"double","scheme":"CS_KERNEL_STOCKHAM_BLOCK_CC","sbrc_trans":"NONE","kernelConfig":{"use_3steps":true,"half_lds":false,"dir_reg":true,"buffer_inst":true,"tpb":8,"wgs":168,"tpt":[ 21,0 ],"factors":[ 7,8,3 ],"ebtype":"NONE","direction":-1,"static_dim":0,"placement":"NA","iAryType":"CI","oAryType":"CI"}}} ,{"sol_node_type":"SOL_KERNEL_ONLY","kernel_key":{"lengths":[ 168,0 ],"precision":"double","scheme":"CS_KERNEL_STOCKHAM_BLOCK_CC","sbrc_trans":"NONE","kernelConfig":{"use_3steps":true,"half_lds":true,"dir_reg":true,"buffer_inst":true,"tpb":8,"wgs":112,"tpt":[ 14,0 ],"factors":[ 7,6,2,2 ],"ebtype":"NONE","direction":-1,"static_dim":0,"placement":"NA","iAryType":"CI","oAryType":"CI"}}} ,{"sol_node_type":"SOL_KERNEL_ONLY","kernel_key":{"lengths":[ 168,0 ],"precision":"double","scheme":"CS_KERNEL_STOCKHAM_BLOCK_CC","sbrc_trans":"NONE","kernelConfig":{"use_3steps":true,"half_lds":true,"dir_reg":true,"buffer_inst":true,"tpb":12,"wgs":252,"tpt":[ 21,0 ],"factors":[ 7,8,3 ],"ebtype":"NONE","direction":-1,"static_dim":0,"placement":"NA","iAryType":"CI","oAryType":"CI"}}} ]}, {"Problem":{"arch":"gfx908","token":"kernel_len243_double_sbcc"}, "Solutions":[ {"sol_node_type":"SOL_KERNEL_ONLY","kernel_key":{"lengths":[ 243,0 ],"precision":"double","scheme":"CS_KERNEL_STOCKHAM_BLOCK_CC","sbrc_trans":"NONE","kernelConfig":{"use_3steps":true,"half_lds":true,"dir_reg":true,"buffer_inst":true,"tpb":8,"wgs":216,"tpt":[ 27,0 ],"factors":[ 9,9,3 ],"ebtype":"NONE","direction":-1,"static_dim":0,"placement":"NA","iAryType":"CI","oAryType":"CI"}}} ]}, {"Problem":{"arch":"gfx908","token":"kernel_len243_single_sbcc"}, "Solutions":[ {"sol_node_type":"SOL_KERNEL_ONLY","kernel_key":{"lengths":[ 243,0 ],"precision":"single","scheme":"CS_KERNEL_STOCKHAM_BLOCK_CC","sbrc_trans":"NONE","kernelConfig":{"use_3steps":false,"half_lds":true,"dir_reg":true,"buffer_inst":true,"tpb":15,"wgs":405,"tpt":[ 27,0 ],"factors":[ 3,3,9,3 ],"ebtype":"NONE","direction":-1,"static_dim":0,"placement":"NA","iAryType":"CI","oAryType":"CI"}}} ]}, {"Problem":{"arch":"gfx908","token":"kernel_len336_double_sbcc"}, "Solutions":[ {"sol_node_type":"SOL_KERNEL_ONLY","kernel_key":{"lengths":[ 336,0 ],"precision":"double","scheme":"CS_KERNEL_STOCKHAM_BLOCK_CC","sbrc_trans":"NONE","kernelConfig":{"use_3steps":false,"half_lds":false,"dir_reg":true,"buffer_inst":true,"tpb":4,"wgs":112,"tpt":[ 28,0 ],"factors":[ 2,7,6,4 ],"ebtype":"NONE","direction":-1,"static_dim":0,"placement":"NA","iAryType":"CI","oAryType":"CI"}}} ,{"sol_node_type":"SOL_KERNEL_ONLY","kernel_key":{"lengths":[ 336,0 ],"precision":"double","scheme":"CS_KERNEL_STOCKHAM_BLOCK_CC","sbrc_trans":"NONE","kernelConfig":{"use_3steps":false,"half_lds":true,"dir_reg":true,"buffer_inst":false,"tpb":6,"wgs":126,"tpt":[ 21,0 ],"factors":[ 7,16,3 ],"ebtype":"NONE","direction":-1,"static_dim":0,"placement":"NA","iAryType":"CI","oAryType":"CI"}}} ,{"sol_node_type":"SOL_KERNEL_ONLY","kernel_key":{"lengths":[ 336,0 ],"precision":"double","scheme":"CS_KERNEL_STOCKHAM_BLOCK_CC","sbrc_trans":"NONE","kernelConfig":{"use_3steps":false,"half_lds":false,"dir_reg":true,"buffer_inst":true,"tpb":4,"wgs":112,"tpt":[ 28,0 ],"factors":[ 6,7,2,4 ],"ebtype":"NONE","direction":-1,"static_dim":0,"placement":"NA","iAryType":"CI","oAryType":"CI"}}} ,{"sol_node_type":"SOL_KERNEL_ONLY","kernel_key":{"lengths":[ 336,0 ],"precision":"double","scheme":"CS_KERNEL_STOCKHAM_BLOCK_CC","sbrc_trans":"NONE","kernelConfig":{"use_3steps":false,"half_lds":false,"dir_reg":true,"buffer_inst":false,"tpb":6,"wgs":126,"tpt":[ 21,0 ],"factors":[ 7,16,3 ],"ebtype":"NONE","direction":-1,"static_dim":0,"placement":"NA","iAryType":"CI","oAryType":"CI"}}} ]}, {"Problem":{"arch":"gfx908","token":"kernel_len343_double_sbcc"}, "Solutions":[ {"sol_node_type":"SOL_KERNEL_ONLY","kernel_key":{"lengths":[ 343,0 ],"precision":"double","scheme":"CS_KERNEL_STOCKHAM_BLOCK_CC","sbrc_trans":"NONE","kernelConfig":{"use_3steps":true,"half_lds":true,"dir_reg":true,"buffer_inst":false,"tpb":4,"wgs":196,"tpt":[ 49,0 ],"factors":[ 7,7,7 ],"ebtype":"NONE","direction":-1,"static_dim":0,"placement":"NA","iAryType":"CI","oAryType":"CI"}}} ]}, {"Problem":{"arch":"gfx908","token":"kernel_len64_double_sbcc"}, "Solutions":[ {"sol_node_type":"SOL_KERNEL_ONLY","kernel_key":{"lengths":[ 64,0 ],"precision":"double","scheme":"CS_KERNEL_STOCKHAM_BLOCK_CC","sbrc_trans":"NONE","kernelConfig":{"use_3steps":false,"half_lds":false,"dir_reg":true,"buffer_inst":true,"tpb":16,"wgs":64,"tpt":[ 4,0 ],"factors":[ 4,2,4,2 ],"ebtype":"NONE","direction":-1,"static_dim":0,"placement":"NA","iAryType":"CI","oAryType":"CI"}}} ,{"sol_node_type":"SOL_KERNEL_ONLY","kernel_key":{"lengths":[ 64,0 ],"precision":"double","scheme":"CS_KERNEL_STOCKHAM_BLOCK_CC","sbrc_trans":"NONE","kernelConfig":{"use_3steps":false,"half_lds":true,"dir_reg":true,"buffer_inst":true,"tpb":16,"wgs":64,"tpt":[ 4,0 ],"factors":[ 2,4,4,2 ],"ebtype":"NONE","direction":-1,"static_dim":0,"placement":"NA","iAryType":"CI","oAryType":"CI"}}} ,{"sol_node_type":"SOL_KERNEL_ONLY","kernel_key":{"lengths":[ 64,0 ],"precision":"double","scheme":"CS_KERNEL_STOCKHAM_BLOCK_CC","sbrc_trans":"NONE","kernelConfig":{"use_3steps":false,"half_lds":true,"dir_reg":true,"buffer_inst":true,"tpb":16,"wgs":64,"tpt":[ 4,0 ],"factors":[ 2,8,4 ],"ebtype":"NONE","direction":-1,"static_dim":0,"placement":"NA","iAryType":"CI","oAryType":"CI"}}} ,{"sol_node_type":"SOL_KERNEL_ONLY","kernel_key":{"lengths":[ 64,0 ],"precision":"double","scheme":"CS_KERNEL_STOCKHAM_BLOCK_CC","sbrc_trans":"NONE","kernelConfig":{"use_3steps":false,"half_lds":true,"dir_reg":true,"buffer_inst":true,"tpb":16,"wgs":64,"tpt":[ 4,0 ],"factors":[ 4,4,2,2 ],"ebtype":"NONE","direction":-1,"static_dim":0,"placement":"NA","iAryType":"CI","oAryType":"CI"}}} ]}, {"Problem":{"arch":"gfx908","token":"kernel_len81_single_sbcc"}, "Solutions":[ {"sol_node_type":"SOL_KERNEL_ONLY","kernel_key":{"lengths":[ 81,0 ],"precision":"single","scheme":"CS_KERNEL_STOCKHAM_BLOCK_CC","sbrc_trans":"NONE","kernelConfig":{"use_3steps":false,"half_lds":true,"dir_reg":true,"buffer_inst":false,"tpb":28,"wgs":252,"tpt":[ 9,0 ],"factors":[ 9,3,3 ],"ebtype":"NONE","direction":-1,"static_dim":0,"placement":"NA","iAryType":"CI","oAryType":"CI"}}} ,{"sol_node_type":"SOL_KERNEL_ONLY","kernel_key":{"lengths":[ 81,0 ],"precision":"single","scheme":"CS_KERNEL_STOCKHAM_BLOCK_CC","sbrc_trans":"NONE","kernelConfig":{"use_3steps":false,"half_lds":false,"dir_reg":true,"buffer_inst":true,"tpb":28,"wgs":252,"tpt":[ 9,0 ],"factors":[ 9,3,3 ],"ebtype":"NONE","direction":-1,"static_dim":0,"placement":"NA","iAryType":"CI","oAryType":"CI"}}} ,{"sol_node_type":"SOL_KERNEL_ONLY","kernel_key":{"lengths":[ 81,0 ],"precision":"single","scheme":"CS_KERNEL_STOCKHAM_BLOCK_CC","sbrc_trans":"NONE","kernelConfig":{"use_3steps":false,"half_lds":true,"dir_reg":true,"buffer_inst":true,"tpb":28,"wgs":252,"tpt":[ 9,0 ],"factors":[ 3,9,3 ],"ebtype":"NONE","direction":-1,"static_dim":0,"placement":"NA","iAryType":"CI","oAryType":"CI"}}} ]}, {"Problem":{"arch":"gfx908","token":"kernel_len96_double_sbcc"}, "Solutions":[ {"sol_node_type":"SOL_KERNEL_ONLY","kernel_key":{"lengths":[ 96,0 ],"precision":"double","scheme":"CS_KERNEL_STOCKHAM_BLOCK_CC","sbrc_trans":"NONE","kernelConfig":{"use_3steps":false,"half_lds":false,"dir_reg":true,"buffer_inst":true,"tpb":16,"wgs":192,"tpt":[ 12,0 ],"factors":[ 8,6,2 ],"ebtype":"NONE","direction":-1,"static_dim":0,"placement":"NA","iAryType":"CI","oAryType":"CI"}}} ]}, {"Problem":{"arch":"gfx908","token":"kernel_len100_double_sbrc"}, "Solutions":[ {"sol_node_type":"SOL_KERNEL_ONLY","kernel_key":{"lengths":[ 100,0 ],"precision":"double","scheme":"CS_KERNEL_STOCKHAM_BLOCK_RC","sbrc_trans":"TILE_ALIGNED","kernelConfig":{"use_3steps":false,"half_lds":false,"dir_reg":true,"buffer_inst":false,"tpb":20,"wgs":200,"tpt":[ 10,0 ],"factors":[ 5,10,2 ],"ebtype":"NONE","direction":-1,"static_dim":0,"placement":"NA","iAryType":"CI","oAryType":"CI"}}} ]}, {"Problem":{"arch":"gfx908","token":"kernel_len112_double_sbrc"}, "Solutions":[ {"sol_node_type":"SOL_KERNEL_ONLY","kernel_key":{"lengths":[ 112,0 ],"precision":"double","scheme":"CS_KERNEL_STOCKHAM_BLOCK_RC","sbrc_trans":"TILE_ALIGNED","kernelConfig":{"use_3steps":false,"half_lds":false,"dir_reg":true,"buffer_inst":false,"tpb":8,"wgs":64,"tpt":[ 8,0 ],"factors":[ 4,7,2,2 ],"ebtype":"NONE","direction":-1,"static_dim":0,"placement":"NA","iAryType":"CI","oAryType":"CI"}}} ]}, {"Problem":{"arch":"gfx908","token":"kernel_len128_double_sbrc"}, "Solutions":[ {"sol_node_type":"SOL_KERNEL_ONLY","kernel_key":{"lengths":[ 128,0 ],"precision":"double","scheme":"CS_KERNEL_STOCKHAM_BLOCK_RC","sbrc_trans":"TILE_ALIGNED","kernelConfig":{"use_3steps":false,"half_lds":false,"dir_reg":true,"buffer_inst":false,"tpb":16,"wgs":128,"tpt":[ 8,0 ],"factors":[ 4,4,4,2 ],"ebtype":"NONE","direction":-1,"static_dim":0,"placement":"NA","iAryType":"CI","oAryType":"CI"}}} ,{"sol_node_type":"SOL_KERNEL_ONLY","kernel_key":{"lengths":[ 128,0 ],"precision":"double","scheme":"CS_KERNEL_STOCKHAM_BLOCK_RC","sbrc_trans":"TILE_UNALIGNED","kernelConfig":{"use_3steps":false,"half_lds":false,"dir_reg":true,"buffer_inst":false,"tpb":16,"wgs":128,"tpt":[ 8,0 ],"factors":[ 4,8,2,2 ],"ebtype":"NONE","direction":-1,"static_dim":0,"placement":"NA","iAryType":"CI","oAryType":"CI"}}} ]}, {"Problem":{"arch":"gfx908","token":"kernel_len192_double_sbrc"}, "Solutions":[ {"sol_node_type":"SOL_KERNEL_ONLY","kernel_key":{"lengths":[ 192,0 ],"precision":"double","scheme":"CS_KERNEL_STOCKHAM_BLOCK_RC","sbrc_trans":"TILE_ALIGNED","kernelConfig":{"use_3steps":false,"half_lds":false,"dir_reg":true,"buffer_inst":false,"tpb":8,"wgs":192,"tpt":[ 24,0 ],"factors":[ 4,3,2,8 ],"ebtype":"NONE","direction":-1,"static_dim":0,"placement":"NA","iAryType":"CI","oAryType":"CI"}}} ]}, {"Problem":{"arch":"gfx908","token":"kernel_len256_double_sbrc"}, "Solutions":[ {"sol_node_type":"SOL_KERNEL_ONLY","kernel_key":{"lengths":[ 256,0 ],"precision":"double","scheme":"CS_KERNEL_STOCKHAM_BLOCK_RC","sbrc_trans":"TILE_ALIGNED","kernelConfig":{"use_3steps":false,"half_lds":false,"dir_reg":true,"buffer_inst":false,"tpb":8,"wgs":128,"tpt":[ 16,0 ],"factors":[ 16,4,4 ],"ebtype":"NONE","direction":-1,"static_dim":0,"placement":"NA","iAryType":"CI","oAryType":"CI"}}} ,{"sol_node_type":"SOL_KERNEL_ONLY","kernel_key":{"lengths":[ 256,0 ],"precision":"double","scheme":"CS_KERNEL_STOCKHAM_BLOCK_RC","sbrc_trans":"TILE_ALIGNED","kernelConfig":{"use_3steps":false,"half_lds":false,"dir_reg":true,"buffer_inst":false,"tpb":8,"wgs":128,"tpt":[ 16,0 ],"factors":[ 8,2,4,4 ],"ebtype":"NONE","direction":-1,"static_dim":0,"placement":"NA","iAryType":"CI","oAryType":"CI"}}} ,{"sol_node_type":"SOL_KERNEL_ONLY","kernel_key":{"lengths":[ 256,0 ],"precision":"double","scheme":"CS_KERNEL_STOCKHAM_BLOCK_RC","sbrc_trans":"TILE_ALIGNED","kernelConfig":{"use_3steps":false,"half_lds":false,"dir_reg":true,"buffer_inst":false,"tpb":8,"wgs":128,"tpt":[ 16,0 ],"factors":[ 16,2,8 ],"ebtype":"NONE","direction":-1,"static_dim":0,"placement":"NA","iAryType":"CI","oAryType":"CI"}}} ]}, {"Problem":{"arch":"gfx908","token":"kernel_len49_double_sbrc"}, "Solutions":[ {"sol_node_type":"SOL_KERNEL_ONLY","kernel_key":{"lengths":[ 49,0 ],"precision":"double","scheme":"CS_KERNEL_STOCKHAM_BLOCK_RC","sbrc_trans":"TILE_UNALIGNED","kernelConfig":{"use_3steps":false,"half_lds":false,"dir_reg":true,"buffer_inst":false,"tpb":28,"wgs":196,"tpt":[ 7,0 ],"factors":[ 7,7 ],"ebtype":"NONE","direction":-1,"static_dim":0,"placement":"NA","iAryType":"CI","oAryType":"CI"}}} ]}, {"Problem":{"arch":"gfx908","token":"kernel_len81_double_sbrc"}, "Solutions":[ {"sol_node_type":"SOL_KERNEL_ONLY","kernel_key":{"lengths":[ 81,0 ],"precision":"double","scheme":"CS_KERNEL_STOCKHAM_BLOCK_RC","sbrc_trans":"TILE_UNALIGNED","kernelConfig":{"use_3steps":false,"half_lds":false,"dir_reg":true,"buffer_inst":false,"tpb":15,"wgs":135,"tpt":[ 9,0 ],"factors":[ 3,9,3 ],"ebtype":"NONE","direction":-1,"static_dim":0,"placement":"NA","iAryType":"CI","oAryType":"CI"}}} ]}, {"Problem":{"arch":"gfx908","token":"kernel_len81_single_sbrc"}, "Solutions":[ {"sol_node_type":"SOL_KERNEL_ONLY","kernel_key":{"lengths":[ 81,0 ],"precision":"single","scheme":"CS_KERNEL_STOCKHAM_BLOCK_RC","sbrc_trans":"TILE_UNALIGNED","kernelConfig":{"use_3steps":false,"half_lds":false,"dir_reg":true,"buffer_inst":false,"tpb":36,"wgs":324,"tpt":[ 9,0 ],"factors":[ 3,9,3 ],"ebtype":"NONE","direction":-1,"static_dim":0,"placement":"NA","iAryType":"CI","oAryType":"CI"}}} ]}, {"Problem":{"arch":"gfx908","token":"kernel_len336_double_sbcr"}, "Solutions":[ {"sol_node_type":"SOL_KERNEL_ONLY","kernel_key":{"lengths":[ 336,0 ],"precision":"double","scheme":"CS_KERNEL_STOCKHAM_BLOCK_CR","sbrc_trans":"NONE","kernelConfig":{"use_3steps":false,"half_lds":false,"dir_reg":true,"buffer_inst":true,"tpb":6,"wgs":168,"tpt":[ 28,0 ],"factors":[ 7,3,4,4 ],"ebtype":"NONE","direction":-1,"static_dim":0,"placement":"NA","iAryType":"CI","oAryType":"CI"}}} ]}, {"Problem":{"arch":"gfx908","token":"kernel_len56_double_sbcr"}, "Solutions":[ {"sol_node_type":"SOL_KERNEL_ONLY","kernel_key":{"lengths":[ 56,0 ],"precision":"double","scheme":"CS_KERNEL_STOCKHAM_BLOCK_CR","sbrc_trans":"NONE","kernelConfig":{"use_3steps":false,"half_lds":false,"dir_reg":true,"buffer_inst":true,"tpb":16,"wgs":128,"tpt":[ 8,0 ],"factors":[ 7,4,2 ],"ebtype":"NONE","direction":-1,"static_dim":0,"placement":"NA","iAryType":"CI","oAryType":"CI"}}} ]}, {"Problem":{"arch":"gfx908","token":"kernel_len256_single_sbrc_xy_z"}, "Solutions":[ {"sol_node_type":"SOL_KERNEL_ONLY","kernel_key":{"lengths":[ 256,0 ],"precision":"single","scheme":"CS_KERNEL_STOCKHAM_TRANSPOSE_XY_Z","sbrc_trans":"TILE_ALIGNED","kernelConfig":{"use_3steps":false,"half_lds":false,"dir_reg":true,"buffer_inst":false,"tpb":16,"wgs":256,"tpt":[ 16,0 ],"factors":[ 4,4,8,2 ],"ebtype":"NONE","direction":-1,"static_dim":0,"placement":"NA","iAryType":"CI","oAryType":"CI"}}} ]}, {"Problem":{"arch":"gfx908","token":"125_sp_ip_complex"}, "Solutions":[ {"sol_node_type":"SOL_DUMMY","using_scheme":"CS_NONE","solution_childnodes":[ ]} ,{"sol_node_type":"SOL_LEAF_NODE","using_scheme":"CS_KERNEL_STOCKHAM","solution_childnodes":[ {"child_token":"kernel_len125_single_sbrr","child_option":0} ]} ,{"sol_node_type":"SOL_LEAF_NODE","using_scheme":"CS_KERNEL_STOCKHAM","solution_childnodes":[ {"child_token":"kernel_len125_single_sbrr","child_option":1} ]} ]}, {"Problem":{"arch":"gfx908","token":"2187_sp_ip_complex"}, "Solutions":[ {"sol_node_type":"SOL_DUMMY","using_scheme":"CS_NONE","solution_childnodes":[ ]} ,{"sol_node_type":"SOL_LEAF_NODE","using_scheme":"CS_KERNEL_STOCKHAM","solution_childnodes":[ {"child_token":"kernel_len2187_single_sbrr","child_option":0} ]} ]}, {"Problem":{"arch":"gfx908","token":"243_sp_ip_complex"}, "Solutions":[ {"sol_node_type":"SOL_DUMMY","using_scheme":"CS_NONE","solution_childnodes":[ ]} ,{"sol_node_type":"SOL_LEAF_NODE","using_scheme":"CS_KERNEL_STOCKHAM","solution_childnodes":[ {"child_token":"kernel_len243_single_sbrr","child_option":0} ]} ,{"sol_node_type":"SOL_LEAF_NODE","using_scheme":"CS_KERNEL_STOCKHAM","solution_childnodes":[ {"child_token":"kernel_len243_single_sbrr","child_option":1} ]} ]}, {"Problem":{"arch":"gfx908","token":"256_sp_ip_complex"}, "Solutions":[ {"sol_node_type":"SOL_DUMMY","using_scheme":"CS_NONE","solution_childnodes":[ ]} ,{"sol_node_type":"SOL_LEAF_NODE","using_scheme":"CS_KERNEL_STOCKHAM","solution_childnodes":[ {"child_token":"kernel_len256_single_sbrr","child_option":0} ]} ,{"sol_node_type":"SOL_LEAF_NODE","using_scheme":"CS_KERNEL_STOCKHAM","solution_childnodes":[ {"child_token":"kernel_len256_single_sbrr","child_option":1} ]} ]}, {"Problem":{"arch":"gfx908","token":"4096_sp_ip_complex"}, "Solutions":[ {"sol_node_type":"SOL_DUMMY","using_scheme":"CS_NONE","solution_childnodes":[ ]} ,{"sol_node_type":"SOL_LEAF_NODE","using_scheme":"CS_KERNEL_STOCKHAM","solution_childnodes":[ {"child_token":"kernel_len4096_single_sbrr","child_option":0} ]} ,{"sol_node_type":"SOL_LEAF_NODE","using_scheme":"CS_KERNEL_STOCKHAM","solution_childnodes":[ {"child_token":"kernel_len4096_single_sbrr","child_option":1} ]} ,{"sol_node_type":"SOL_LEAF_NODE","using_scheme":"CS_KERNEL_STOCKHAM","solution_childnodes":[ {"child_token":"kernel_len4096_single_sbrr","child_option":2} ]} ]}, {"Problem":{"arch":"gfx908","token":"56_dp_ip_complex"}, "Solutions":[ {"sol_node_type":"SOL_DUMMY","using_scheme":"CS_NONE","solution_childnodes":[ ]} ,{"sol_node_type":"SOL_LEAF_NODE","using_scheme":"CS_KERNEL_STOCKHAM","solution_childnodes":[ {"child_token":"kernel_len56_double_sbrr","child_option":0} ]} ,{"sol_node_type":"SOL_LEAF_NODE","using_scheme":"CS_KERNEL_STOCKHAM","solution_childnodes":[ {"child_token":"kernel_len56_double_sbrr","child_option":1} ]} ]}, {"Problem":{"arch":"gfx908","token":"sbcc_100_dp_ip_complex"}, "Solutions":[ {"sol_node_type":"SOL_LEAF_NODE","using_scheme":"CS_KERNEL_STOCKHAM_BLOCK_CC","solution_childnodes":[ {"child_token":"kernel_len100_double_sbcc","child_option":0} ]} ]}, {"Problem":{"arch":"gfx908","token":"sbcc_125_sp_ip_complex"}, "Solutions":[ {"sol_node_type":"SOL_LEAF_NODE","using_scheme":"CS_KERNEL_STOCKHAM_BLOCK_CC","solution_childnodes":[ {"child_token":"kernel_len125_single_sbcc","child_option":0} ]} ,{"sol_node_type":"SOL_LEAF_NODE","using_scheme":"CS_KERNEL_STOCKHAM_BLOCK_CC","solution_childnodes":[ {"child_token":"kernel_len125_single_sbcc","child_option":1} ]} ]}, {"Problem":{"arch":"gfx908","token":"sbcc_168_dp_ip_complex"}, "Solutions":[ {"sol_node_type":"SOL_LEAF_NODE","using_scheme":"CS_KERNEL_STOCKHAM_BLOCK_CC","solution_childnodes":[ {"child_token":"kernel_len168_double_sbcc","child_option":0} ]} ,{"sol_node_type":"SOL_LEAF_NODE","using_scheme":"CS_KERNEL_STOCKHAM_BLOCK_CC","solution_childnodes":[ {"child_token":"kernel_len168_double_sbcc","child_option":1} ]} ,{"sol_node_type":"SOL_LEAF_NODE","using_scheme":"CS_KERNEL_STOCKHAM_BLOCK_CC","solution_childnodes":[ {"child_token":"kernel_len168_double_sbcc","child_option":2} ]} ,{"sol_node_type":"SOL_LEAF_NODE","using_scheme":"CS_KERNEL_STOCKHAM_BLOCK_CC","solution_childnodes":[ {"child_token":"kernel_len168_double_sbcc","child_option":3} ]} ,{"sol_node_type":"SOL_LEAF_NODE","using_scheme":"CS_KERNEL_STOCKHAM_BLOCK_CC","solution_childnodes":[ {"child_token":"kernel_len168_double_sbcc","child_option":4} ]} ,{"sol_node_type":"SOL_LEAF_NODE","using_scheme":"CS_KERNEL_STOCKHAM_BLOCK_CC","solution_childnodes":[ {"child_token":"kernel_len168_double_sbcc","child_option":5} ]} ]}, {"Problem":{"arch":"gfx908","token":"sbcc_243_dp_ip_complex"}, "Solutions":[ {"sol_node_type":"SOL_LEAF_NODE","using_scheme":"CS_KERNEL_STOCKHAM_BLOCK_CC","solution_childnodes":[ {"child_token":"kernel_len243_double_sbcc","child_option":0} ]} ]}, {"Problem":{"arch":"gfx908","token":"sbcc_243_sp_ip_complex"}, "Solutions":[ {"sol_node_type":"SOL_LEAF_NODE","using_scheme":"CS_KERNEL_STOCKHAM_BLOCK_CC","solution_childnodes":[ {"child_token":"kernel_len243_single_sbcc","child_option":0} ]} ]}, {"Problem":{"arch":"gfx908","token":"sbcc_336_dp_ip_complex"}, "Solutions":[ {"sol_node_type":"SOL_LEAF_NODE","using_scheme":"CS_KERNEL_STOCKHAM_BLOCK_CC","solution_childnodes":[ {"child_token":"kernel_len336_double_sbcc","child_option":0} ]} ,{"sol_node_type":"SOL_LEAF_NODE","using_scheme":"CS_KERNEL_STOCKHAM_BLOCK_CC","solution_childnodes":[ {"child_token":"kernel_len336_double_sbcc","child_option":1} ]} ,{"sol_node_type":"SOL_LEAF_NODE","using_scheme":"CS_KERNEL_STOCKHAM_BLOCK_CC","solution_childnodes":[ {"child_token":"kernel_len336_double_sbcc","child_option":2} ]} ,{"sol_node_type":"SOL_LEAF_NODE","using_scheme":"CS_KERNEL_STOCKHAM_BLOCK_CC","solution_childnodes":[ {"child_token":"kernel_len336_double_sbcc","child_option":3} ]} ]}, {"Problem":{"arch":"gfx908","token":"sbcc_343_dp_ip_complex"}, "Solutions":[ {"sol_node_type":"SOL_LEAF_NODE","using_scheme":"CS_KERNEL_STOCKHAM_BLOCK_CC","solution_childnodes":[ {"child_token":"kernel_len343_double_sbcc","child_option":0} ]} ]}, {"Problem":{"arch":"gfx908","token":"sbcc_64_dp_ip_complex"}, "Solutions":[ {"sol_node_type":"SOL_LEAF_NODE","using_scheme":"CS_KERNEL_STOCKHAM_BLOCK_CC","solution_childnodes":[ {"child_token":"kernel_len64_double_sbcc","child_option":0} ]} ,{"sol_node_type":"SOL_LEAF_NODE","using_scheme":"CS_KERNEL_STOCKHAM_BLOCK_CC","solution_childnodes":[ {"child_token":"kernel_len64_double_sbcc","child_option":1} ]} ,{"sol_node_type":"SOL_LEAF_NODE","using_scheme":"CS_KERNEL_STOCKHAM_BLOCK_CC","solution_childnodes":[ {"child_token":"kernel_len64_double_sbcc","child_option":2} ]} ,{"sol_node_type":"SOL_LEAF_NODE","using_scheme":"CS_KERNEL_STOCKHAM_BLOCK_CC","solution_childnodes":[ {"child_token":"kernel_len64_double_sbcc","child_option":3} ]} ]}, {"Problem":{"arch":"gfx908","token":"sbcc_81_sp_ip_complex"}, "Solutions":[ {"sol_node_type":"SOL_LEAF_NODE","using_scheme":"CS_KERNEL_STOCKHAM_BLOCK_CC","solution_childnodes":[ {"child_token":"kernel_len81_single_sbcc","child_option":0} ]} ,{"sol_node_type":"SOL_LEAF_NODE","using_scheme":"CS_KERNEL_STOCKHAM_BLOCK_CC","solution_childnodes":[ {"child_token":"kernel_len81_single_sbcc","child_option":1} ]} ,{"sol_node_type":"SOL_LEAF_NODE","using_scheme":"CS_KERNEL_STOCKHAM_BLOCK_CC","solution_childnodes":[ {"child_token":"kernel_len81_single_sbcc","child_option":2} ]} ]}, {"Problem":{"arch":"gfx908","token":"sbcc_96_dp_ip_complex"}, "Solutions":[ {"sol_node_type":"SOL_LEAF_NODE","using_scheme":"CS_KERNEL_STOCKHAM_BLOCK_CC","solution_childnodes":[ {"child_token":"kernel_len96_double_sbcc","child_option":0} ]} ]}, {"Problem":{"arch":"gfx908","token":"sbrc_100_dp_ip_complex"}, "Solutions":[ {"sol_node_type":"SOL_LEAF_NODE","using_scheme":"CS_KERNEL_STOCKHAM_BLOCK_RC","solution_childnodes":[ {"child_token":"kernel_len100_double_sbrc","child_option":0} ]} ]}, {"Problem":{"arch":"gfx908","token":"sbrc_112_dp_ip_complex"}, "Solutions":[ {"sol_node_type":"SOL_LEAF_NODE","using_scheme":"CS_KERNEL_STOCKHAM_BLOCK_RC","solution_childnodes":[ {"child_token":"kernel_len112_double_sbrc","child_option":0} ]} ]}, {"Problem":{"arch":"gfx908","token":"sbrc_128_dp_ip_complex"}, "Solutions":[ {"sol_node_type":"SOL_LEAF_NODE","using_scheme":"CS_KERNEL_STOCKHAM_BLOCK_RC","solution_childnodes":[ {"child_token":"kernel_len128_double_sbrc","child_option":0} ]} ,{"sol_node_type":"SOL_LEAF_NODE","using_scheme":"CS_KERNEL_STOCKHAM_BLOCK_RC","solution_childnodes":[ {"child_token":"kernel_len128_double_sbrc","child_option":1} ]} ]}, {"Problem":{"arch":"gfx908","token":"sbrc_192_dp_ip_complex"}, "Solutions":[ {"sol_node_type":"SOL_LEAF_NODE","using_scheme":"CS_KERNEL_STOCKHAM_BLOCK_RC","solution_childnodes":[ {"child_token":"kernel_len192_double_sbrc","child_option":0} ]} ]}, {"Problem":{"arch":"gfx908","token":"sbrc_256_dp_ip_complex"}, "Solutions":[ {"sol_node_type":"SOL_LEAF_NODE","using_scheme":"CS_KERNEL_STOCKHAM_BLOCK_RC","solution_childnodes":[ {"child_token":"kernel_len256_double_sbrc","child_option":0} ]} ,{"sol_node_type":"SOL_LEAF_NODE","using_scheme":"CS_KERNEL_STOCKHAM_BLOCK_RC","solution_childnodes":[ {"child_token":"kernel_len256_double_sbrc","child_option":1} ]} ,{"sol_node_type":"SOL_LEAF_NODE","using_scheme":"CS_KERNEL_STOCKHAM_BLOCK_RC","solution_childnodes":[ {"child_token":"kernel_len256_double_sbrc","child_option":2} ]} ]}, {"Problem":{"arch":"gfx908","token":"sbrc_49_dp_ip_complex"}, "Solutions":[ {"sol_node_type":"SOL_LEAF_NODE","using_scheme":"CS_KERNEL_STOCKHAM_BLOCK_RC","solution_childnodes":[ {"child_token":"kernel_len49_double_sbrc","child_option":0} ]} ]}, {"Problem":{"arch":"gfx908","token":"sbrc_81_dp_ip_complex"}, "Solutions":[ {"sol_node_type":"SOL_LEAF_NODE","using_scheme":"CS_KERNEL_STOCKHAM_BLOCK_RC","solution_childnodes":[ {"child_token":"kernel_len81_double_sbrc","child_option":0} ]} ]}, {"Problem":{"arch":"gfx908","token":"sbrc_81_sp_ip_complex"}, "Solutions":[ {"sol_node_type":"SOL_LEAF_NODE","using_scheme":"CS_KERNEL_STOCKHAM_BLOCK_RC","solution_childnodes":[ {"child_token":"kernel_len81_single_sbrc","child_option":0} ]} ]}, {"Problem":{"arch":"gfx908","token":"sbcr_336_dp_ip_complex"}, "Solutions":[ {"sol_node_type":"SOL_LEAF_NODE","using_scheme":"CS_KERNEL_STOCKHAM_BLOCK_CR","solution_childnodes":[ {"child_token":"kernel_len336_double_sbcr","child_option":0} ]} ]}, {"Problem":{"arch":"gfx908","token":"sbcr_56_dp_ip_complex"}, "Solutions":[ {"sol_node_type":"SOL_LEAF_NODE","using_scheme":"CS_KERNEL_STOCKHAM_BLOCK_CR","solution_childnodes":[ {"child_token":"kernel_len56_double_sbcr","child_option":0} ]} ]}, {"Problem":{"arch":"gfx908","token":"leafnode_token_builtin_kernel"}, "Solutions":[ {"sol_node_type":"SOL_LEAF_NODE","using_scheme":"CS_KERNEL_TRANSPOSE","solution_childnodes":[ {"child_token":"kernel_token_builtin_kernel","child_option":0} ]} ,{"sol_node_type":"SOL_LEAF_NODE","using_scheme":"CS_KERNEL_TRANSPOSE_XY_Z","solution_childnodes":[ {"child_token":"kernel_token_builtin_kernel","child_option":0} ]} ,{"sol_node_type":"SOL_LEAF_NODE","using_scheme":"CS_KERNEL_TRANSPOSE_Z_XY","solution_childnodes":[ {"child_token":"kernel_token_builtin_kernel","child_option":0} ]} ]}, {"Problem":{"arch":"gfx908","token":"sbrc_xy_z_256_sp_ip_complex"}, "Solutions":[ {"sol_node_type":"SOL_LEAF_NODE","using_scheme":"CS_KERNEL_STOCKHAM_TRANSPOSE_XY_Z","solution_childnodes":[ {"child_token":"kernel_len256_single_sbrc_xy_z","child_option":0} ]} ]}, {"Problem":{"arch":"gfx908","token":"14348907_sp_ip_complex"}, "Solutions":[ {"sol_node_type":"SOL_INTERNAL_NODE","using_scheme":"CS_L1D_TRTRT","solution_childnodes":[ {"child_token":"leafnode_token_builtin_kernel","child_option":0},{"child_token":"6561_sp_ip_complex","child_option":1},{"child_token":"leafnode_token_builtin_kernel","child_option":0},{"child_token":"2187_sp_ip_complex","child_option":1},{"child_token":"leafnode_token_builtin_kernel","child_option":0} ]} ]}, {"Problem":{"arch":"gfx908","token":"16777216_sp_ip_complex"}, "Solutions":[ {"sol_node_type":"SOL_INTERNAL_NODE","using_scheme":"CS_L1D_TRTRT","solution_childnodes":[ {"child_token":"leafnode_token_builtin_kernel","child_option":0},{"child_token":"4096_sp_ip_complex","child_option":1},{"child_token":"leafnode_token_builtin_kernel","child_option":0},{"child_token":"4096_sp_ip_complex","child_option":2},{"child_token":"leafnode_token_builtin_kernel","child_option":0} ]} ]}, {"Problem":{"arch":"gfx908","token":"10000_dp_op_complex"}, "Solutions":[ {"sol_node_type":"SOL_INTERNAL_NODE","using_scheme":"CS_L1D_CC","solution_childnodes":[ {"child_token":"sbcc_100_dp_ip_complex","child_option":0},{"child_token":"sbrc_100_dp_ip_complex","child_option":0} ]} ]}, {"Problem":{"arch":"gfx908","token":"10752_dp_op_complex"}, "Solutions":[ {"sol_node_type":"SOL_INTERNAL_NODE","using_scheme":"CS_L1D_CC","solution_childnodes":[ {"child_token":"sbcc_96_dp_ip_complex","child_option":0},{"child_token":"sbrc_112_dp_ip_complex","child_option":0} ]} ]}, {"Problem":{"arch":"gfx908","token":"16384_dp_op_complex"}, "Solutions":[ {"sol_node_type":"SOL_INTERNAL_NODE","using_scheme":"CS_L1D_CC","solution_childnodes":[ {"child_token":"sbcc_64_dp_ip_complex","child_option":3},{"child_token":"sbrc_256_dp_ip_complex","child_option":1} ]} ]}, {"Problem":{"arch":"gfx908","token":"16807_dp_op_complex"}, "Solutions":[ {"sol_node_type":"SOL_INTERNAL_NODE","using_scheme":"CS_L1D_CC","solution_childnodes":[ {"child_token":"sbcc_343_dp_ip_complex","child_option":0},{"child_token":"sbrc_49_dp_ip_complex","child_option":0} ]} ]}, {"Problem":{"arch":"gfx908","token":"18816_dp_op_complex"}, "Solutions":[ {"sol_node_type":"SOL_INTERNAL_NODE","using_scheme":"CS_L1D_CC","solution_childnodes":[ {"child_token":"sbcc_168_dp_ip_complex","child_option":5},{"child_token":"sbrc_112_dp_ip_complex","child_option":0} ]} ]}, {"Problem":{"arch":"gfx908","token":"19683_dp_op_complex"}, "Solutions":[ {"sol_node_type":"SOL_INTERNAL_NODE","using_scheme":"CS_L1D_CC","solution_childnodes":[ {"child_token":"sbcc_243_dp_ip_complex","child_option":0},{"child_token":"sbrc_81_dp_ip_complex","child_option":0} ]} ]}, {"Problem":{"arch":"gfx908","token":"21504_dp_op_complex"}, "Solutions":[ {"sol_node_type":"SOL_INTERNAL_NODE","using_scheme":"CS_L1D_CC","solution_childnodes":[ {"child_token":"sbcc_168_dp_ip_complex","child_option":2},{"child_token":"sbrc_128_dp_ip_complex","child_option":1} ]} ]}, {"Problem":{"arch":"gfx908","token":"32256_dp_op_complex"}, "Solutions":[ {"sol_node_type":"SOL_INTERNAL_NODE","using_scheme":"CS_L1D_CC","solution_childnodes":[ {"child_token":"sbcc_168_dp_ip_complex","child_option":2},{"child_token":"sbrc_192_dp_ip_complex","child_option":0} ]} ]}, {"Problem":{"arch":"gfx908","token":"43008_dp_op_complex"}, "Solutions":[ {"sol_node_type":"SOL_INTERNAL_NODE","using_scheme":"CS_L1D_CC","solution_childnodes":[ {"child_token":"sbcc_168_dp_ip_complex","child_option":4},{"child_token":"sbrc_256_dp_ip_complex","child_option":2} ]} ]}, {"Problem":{"arch":"gfx908","token":"6561_sp_ip_complex"}, "Solutions":[ {"sol_node_type":"SOL_DUMMY","using_scheme":"CS_NONE","solution_childnodes":[ ]} ,{"sol_node_type":"SOL_INTERNAL_NODE","using_scheme":"CS_L1D_CC","solution_childnodes":[ {"child_token":"sbcc_81_sp_ip_complex","child_option":0},{"child_token":"sbrc_81_sp_ip_complex","child_option":0} ]} ,{"sol_node_type":"SOL_INTERNAL_NODE","using_scheme":"CS_L1D_CC","solution_childnodes":[ {"child_token":"sbcc_81_sp_ip_complex","child_option":1},{"child_token":"sbrc_81_sp_ip_complex","child_option":0} ]} ,{"sol_node_type":"SOL_INTERNAL_NODE","using_scheme":"CS_L1D_CC","solution_childnodes":[ {"child_token":"sbcc_81_sp_ip_complex","child_option":2},{"child_token":"sbrc_81_sp_ip_complex","child_option":0} ]} ]}, {"Problem":{"arch":"gfx908","token":"8192_dp_op_complex"}, "Solutions":[ {"sol_node_type":"SOL_INTERNAL_NODE","using_scheme":"CS_L1D_CC","solution_childnodes":[ {"child_token":"sbcc_64_dp_ip_complex","child_option":3},{"child_token":"sbrc_128_dp_ip_complex","child_option":0} ]} ]}, {"Problem":{"arch":"gfx908","token":"4096_4096_sp_ip_complex"}, "Solutions":[ {"sol_node_type":"SOL_INTERNAL_NODE","using_scheme":"CS_2D_RTRT","solution_childnodes":[ {"child_token":"4096_sp_ip_complex","child_option":3},{"child_token":"leafnode_token_builtin_kernel","child_option":0},{"child_token":"4096_sp_ip_complex","child_option":1},{"child_token":"leafnode_token_builtin_kernel","child_option":0} ]} ]}, {"Problem":{"arch":"gfx908","token":"6561_6561_sp_ip_complex"}, "Solutions":[ {"sol_node_type":"SOL_INTERNAL_NODE","using_scheme":"CS_2D_RTRT","solution_childnodes":[ {"child_token":"6561_sp_ip_complex","child_option":2},{"child_token":"leafnode_token_builtin_kernel","child_option":0},{"child_token":"6561_sp_ip_complex","child_option":3},{"child_token":"leafnode_token_builtin_kernel","child_option":0} ]} ]}, {"Problem":{"arch":"gfx908","token":"125_125_sp_ip_complex"}, "Solutions":[ {"sol_node_type":"SOL_DUMMY","using_scheme":"CS_NONE","solution_childnodes":[ ]} ,{"sol_node_type":"SOL_INTERNAL_NODE","using_scheme":"CS_2D_RC","solution_childnodes":[ {"child_token":"125_sp_ip_complex","child_option":1},{"child_token":"sbcc_125_sp_ip_complex","child_option":0} ]} ,{"sol_node_type":"SOL_INTERNAL_NODE","using_scheme":"CS_2D_RC","solution_childnodes":[ {"child_token":"125_sp_ip_complex","child_option":2},{"child_token":"sbcc_125_sp_ip_complex","child_option":0} ]} ]}, {"Problem":{"arch":"gfx908","token":"243_243_sp_ip_complex"}, "Solutions":[ {"sol_node_type":"SOL_DUMMY","using_scheme":"CS_NONE","solution_childnodes":[ ]} ,{"sol_node_type":"SOL_INTERNAL_NODE","using_scheme":"CS_2D_RC","solution_childnodes":[ {"child_token":"243_sp_ip_complex","child_option":1},{"child_token":"sbcc_243_sp_ip_complex","child_option":0} ]} ]}, {"Problem":{"arch":"gfx908","token":"56_336_dp_ip_complex"}, "Solutions":[ {"sol_node_type":"SOL_DUMMY","using_scheme":"CS_NONE","solution_childnodes":[ ]} ,{"sol_node_type":"SOL_INTERNAL_NODE","using_scheme":"CS_2D_RC","solution_childnodes":[ {"child_token":"56_dp_ip_complex","child_option":1},{"child_token":"sbcc_336_dp_ip_complex","child_option":0} ]} ,{"sol_node_type":"SOL_INTERNAL_NODE","using_scheme":"CS_2D_RC","solution_childnodes":[ {"child_token":"56_dp_ip_complex","child_option":2},{"child_token":"sbcc_336_dp_ip_complex","child_option":2} ]} ]}, {"Problem":{"arch":"gfx908","token":"243_243_243_sp_ip_complex"}, "Solutions":[ {"sol_node_type":"SOL_INTERNAL_NODE","using_scheme":"CS_3D_RTRT","solution_childnodes":[ {"child_token":"243_243_sp_ip_complex","child_option":1},{"child_token":"leafnode_token_builtin_kernel","child_option":1},{"child_token":"243_sp_ip_complex","child_option":2},{"child_token":"leafnode_token_builtin_kernel","child_option":2} ]} ]}, {"Problem":{"arch":"gfx908","token":"56_336_336_dp_op_complex"}, "Solutions":[ {"sol_node_type":"SOL_INTERNAL_NODE","using_scheme":"CS_3D_BLOCK_CR","solution_childnodes":[ {"child_token":"sbcr_336_dp_ip_complex","child_option":0},{"child_token":"sbcr_336_dp_ip_complex","child_option":0},{"child_token":"sbcr_56_dp_ip_complex","child_option":0} ]} ]}, {"Problem":{"arch":"gfx908","token":"125_125_125_sp_ip_complex"}, "Solutions":[ {"sol_node_type":"SOL_INTERNAL_NODE","using_scheme":"CS_3D_RC","solution_childnodes":[ {"child_token":"125_125_sp_ip_complex","child_option":2},{"child_token":"sbcc_125_sp_ip_complex","child_option":1} ]} ]}, {"Problem":{"arch":"gfx908","token":"56_336_336_dp_ip_complex"}, "Solutions":[ {"sol_node_type":"SOL_INTERNAL_NODE","using_scheme":"CS_3D_RC","solution_childnodes":[ {"child_token":"56_336_dp_ip_complex","child_option":2},{"child_token":"sbcc_336_dp_ip_complex","child_option":3} ]} ]} ] }upstream/shared/0000775000175000017500000000000014637212425012632 5ustar kaolkaolupstream/shared/printbuffer.h0000664000175000017500000001024214637212246015331 0ustar kaolkaol// Copyright (C) 2021 - 2023 Advanced Micro Devices, Inc. All rights reserved. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. #ifndef PRINTBUFFER_H #define PRINTBUFFER_H #include "hostbuf.h" #include "increment.h" #include #include // Output a formatted general-dimensional array with given length and stride in batches // separated by dist. template inline void printbuffer(const Toutput* output, const std::vector& length, const std::vector& stride, const Tsize nbatch, const Tsize dist, const size_t offset, Tstream& stream) { auto i_base = 0; for(unsigned int b = 0; b < nbatch; b++, i_base += dist) { std::vector index(length.size()); std::fill(index.begin(), index.end(), 0); do { const int i = std::inner_product(index.begin(), index.end(), stride.begin(), i_base + offset); stream << output[i] << " "; for(int li = index.size(); li-- > 0;) { if(index[li] == (length[li] - 1)) { stream << "\n"; } else { break; } } } while(increment_rowmajor(index, length)); stream << std::endl; } } template class buffer_printer { // The scalar versions might be part of a planar format. public: template static void print_buffer(const std::vector& buf, const std::vector& length, const std::vector& stride, const Tsize nbatch, const Tsize dist, const std::vector& offset, Tstream& stream = std::cout) { for(const auto& vec : buf) { printbuffer(reinterpret_cast(vec.data()), length, stride, nbatch, dist, offset[0], stream); } }; template static void print_buffer_flat(const std::vector& buf, const std::vector& size, const std::vector& offset, Tstream& stream = std::cout) { for(const auto& vec : buf) { auto data = reinterpret_cast(vec.data()); stream << "idx " << 0; for(size_t i = 0; i < size[0]; ++i) stream << " " << data[i]; stream << std::endl; } }; }; #endif upstream/shared/rocfft_params.h0000664000175000017500000005476314637212425015650 0ustar kaolkaol// Copyright (C) 2021 - 2023 Advanced Micro Devices, Inc. All rights reserved. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. #ifndef ROCFFT_PARAMS_H #define ROCFFT_PARAMS_H #include "../shared/fft_params.h" #include "../shared/gpubuf.h" #include "rocfft/rocfft.h" // Return the string of the rocfft_status code static std::string rocfft_status_to_string(const rocfft_status ret) { switch(ret) { case rocfft_status_success: return "rocfft_status_success"; case rocfft_status_failure: return "rocfft_status_failure"; case rocfft_status_invalid_arg_value: return "rocfft_status_invalid_arg_value"; case rocfft_status_invalid_dimensions: return "rocfft_status_invalid_dimensions"; case rocfft_status_invalid_array_type: return "rocfft_status_invalid_array_type"; case rocfft_status_invalid_strides: return "rocfft_status_invalid_strides"; case rocfft_status_invalid_distance: return "rocfft_status_invalid_distance"; case rocfft_status_invalid_offset: return "rocfft_status_invalid_offset"; case rocfft_status_invalid_work_buffer: return "rocfft_status_invalid_work_buffer"; default: throw std::runtime_error("unknown rocfft_status"); } } inline fft_status fft_status_from_rocfftparams(const rocfft_status val) { switch(val) { case rocfft_status_success: return fft_status_success; case rocfft_status_failure: return fft_status_failure; case rocfft_status_invalid_arg_value: return fft_status_invalid_arg_value; case rocfft_status_invalid_dimensions: return fft_status_invalid_dimensions; case rocfft_status_invalid_array_type: return fft_status_invalid_array_type; case rocfft_status_invalid_strides: return fft_status_invalid_strides; case rocfft_status_invalid_distance: return fft_status_invalid_distance; case rocfft_status_invalid_offset: return fft_status_invalid_offset; case rocfft_status_invalid_work_buffer: return fft_status_invalid_work_buffer; default: throw std::runtime_error("Invalid status"); } } inline rocfft_precision rocfft_precision_from_fftparams(const fft_precision val) { switch(val) { case fft_precision_single: return rocfft_precision_single; case fft_precision_double: return rocfft_precision_double; case fft_precision_half: return rocfft_precision_half; default: throw std::runtime_error("Invalid precision"); } } inline rocfft_array_type rocfft_array_type_from_fftparams(const fft_array_type val) { switch(val) { case fft_array_type_complex_interleaved: return rocfft_array_type_complex_interleaved; case fft_array_type_complex_planar: return rocfft_array_type_complex_planar; case fft_array_type_real: return rocfft_array_type_real; case fft_array_type_hermitian_interleaved: return rocfft_array_type_hermitian_interleaved; case fft_array_type_hermitian_planar: return rocfft_array_type_hermitian_planar; case fft_array_type_unset: return rocfft_array_type_unset; } return rocfft_array_type_unset; } inline rocfft_transform_type rocfft_transform_type_from_fftparams(const fft_transform_type val) { switch(val) { case fft_transform_type_complex_forward: return rocfft_transform_type_complex_forward; case fft_transform_type_complex_inverse: return rocfft_transform_type_complex_inverse; case fft_transform_type_real_forward: return rocfft_transform_type_real_forward; case fft_transform_type_real_inverse: return rocfft_transform_type_real_inverse; default: throw std::runtime_error("Invalid transform type"); } } inline rocfft_result_placement rocfft_result_placement_from_fftparams(const fft_result_placement val) { switch(val) { case fft_placement_inplace: return rocfft_placement_inplace; case fft_placement_notinplace: return rocfft_placement_notinplace; default: throw std::runtime_error("Invalid result placement"); } } class rocfft_params : public fft_params { public: rocfft_plan plan = nullptr; rocfft_execution_info info = nullptr; rocfft_plan_description desc = nullptr; gpubuf_t wbuffer; explicit rocfft_params(){}; explicit rocfft_params(const fft_params& p) : fft_params(p){}; rocfft_params(const rocfft_params&) = delete; rocfft_params& operator=(const rocfft_params&) = delete; ~rocfft_params() { free(); }; void free() { if(plan != nullptr) { rocfft_plan_destroy(plan); plan = nullptr; } if(info != nullptr) { rocfft_execution_info_destroy(info); info = nullptr; } if(desc != nullptr) { rocfft_plan_description_destroy(desc); desc = nullptr; } wbuffer.free(); } void validate_fields() const override { // row-major lengths including batch (i.e. batch is at the front) std::vector length_with_batch{nbatch}; std::copy(length.begin(), length.end(), std::back_inserter(length_with_batch)); auto validate_field = [&](const fft_field& f) { for(const auto& b : f.bricks) { // bricks must have same dim as FFT, including batch if(b.lower.size() != length.size() + 1 || b.upper.size() != length.size() + 1 || b.stride.size() != length.size() + 1) throw std::runtime_error( "brick dimension does not match FFT + batch dimension"); // ensure lower < upper, and that both fit in the FFT + batch dims if(!std::lexicographical_compare( b.lower.begin(), b.lower.end(), b.upper.begin(), b.upper.end())) throw std::runtime_error("brick lower index is not less than upper index"); if(!std::lexicographical_compare(b.lower.begin(), b.lower.end(), length_with_batch.begin(), length_with_batch.end())) throw std::runtime_error( "brick lower index is not less than FFT + batch length"); if(!std::lexicographical_compare(b.upper.begin(), b.upper.end(), length_with_batch.begin(), length_with_batch.end()) && b.upper != length_with_batch) throw std::runtime_error("brick upper index is not <= FFT + batch length"); } }; for(const auto& ifield : ifields) validate_field(ifield); for(const auto& ofield : ofields) validate_field(ofield); } rocfft_precision get_rocfft_precision() { return rocfft_precision_from_fftparams(precision); } size_t vram_footprint() override { size_t val = fft_params::vram_footprint(); if(setup_structs() != fft_status_success) { throw std::runtime_error("Struct setup failed"); } val += workbuffersize; return val; } // Convert the generic fft_field structure to a rocfft_field // structure that can be passed to rocFFT. In particular, we need // to convert from row-major to column-major. static rocfft_field fft_field_to_rocfft_field(const fft_field& f) { rocfft_field rfield = nullptr; if(f.bricks.empty()) return rfield; if(rocfft_field_create(&rfield) != rocfft_status_success) throw std::runtime_error("rocfft_field_create failed"); for(const auto& b : f.bricks) { // rocFFT wants column-major bricks and fft_params stores // row-major std::vector lower_cm; std::copy(b.lower.rbegin(), b.lower.rend(), std::back_inserter(lower_cm)); std::vector upper_cm; std::copy(b.upper.rbegin(), b.upper.rend(), std::back_inserter(upper_cm)); std::vector stride_cm; std::copy(b.stride.rbegin(), b.stride.rend(), std::back_inserter(stride_cm)); rocfft_brick rbrick = nullptr; if(rocfft_brick_create(&rbrick, lower_cm.data(), // field_lower upper_cm.data(), // field_upper stride_cm.data(), // brick_stride lower_cm.size(), // dim b.device) // deviceID != rocfft_status_success) throw std::runtime_error("rocfft_brick_create failed"); if(rocfft_field_add_brick(rfield, rbrick) != rocfft_status_success) throw std::runtime_error("rocfft_field_add_brick failed"); rocfft_brick_destroy(rbrick); } return rfield; } fft_status setup_structs() { rocfft_status fft_status = rocfft_status_success; if(desc == nullptr) { rocfft_plan_description_create(&desc); if(fft_status != rocfft_status_success) return fft_status_from_rocfftparams(fft_status); fft_status = rocfft_plan_description_set_data_layout(desc, rocfft_array_type_from_fftparams(itype), rocfft_array_type_from_fftparams(otype), ioffset.data(), ooffset.data(), istride_cm().size(), istride_cm().data(), idist, ostride_cm().size(), ostride_cm().data(), odist); if(fft_status != rocfft_status_success) { throw std::runtime_error("rocfft_plan_description_set_data_layout failed"); } if(scale_factor != 1.0) { fft_status = rocfft_plan_description_set_scale_factor(desc, scale_factor); if(fft_status != rocfft_status_success) { throw std::runtime_error("rocfft_plan_description_set_scale_factor failed"); } } for(const auto& ifield : ifields) { rocfft_field infield = fft_field_to_rocfft_field(ifield); if(rocfft_plan_description_add_infield(desc, infield) != rocfft_status_success) throw std::runtime_error("rocfft_description_add_infield failed"); rocfft_field_destroy(infield); } for(const auto& ofield : ofields) { rocfft_field outfield = fft_field_to_rocfft_field(ofield); if(rocfft_plan_description_add_outfield(desc, outfield) != rocfft_status_success) throw std::runtime_error("rocfft_description_add_outfield failed"); rocfft_field_destroy(outfield); } } if(plan == nullptr) { fft_status = rocfft_plan_create(&plan, rocfft_result_placement_from_fftparams(placement), rocfft_transform_type_from_fftparams(transform_type), get_rocfft_precision(), length_cm().size(), length_cm().data(), nbatch, desc); if(fft_status != rocfft_status_success) { throw std::runtime_error("rocfft_plan_create failed"); } } if(info == nullptr) { fft_status = rocfft_execution_info_create(&info); if(fft_status != rocfft_status_success) { throw std::runtime_error("rocfft_execution_info_create failed"); } } fft_status = rocfft_plan_get_work_buffer_size(plan, &workbuffersize); if(fft_status != rocfft_status_success) { throw std::runtime_error("rocfft_plan_get_work_buffer_size failed"); } return fft_status_from_rocfftparams(fft_status); } fft_status create_plan() override { fft_status ret = setup_structs(); if(ret != fft_status_success) { return ret; } if(workbuffersize > 0) { hipError_t hip_status = hipSuccess; hip_status = wbuffer.alloc(workbuffersize); if(hip_status != hipSuccess) { std::ostringstream oss; oss << "work buffer allocation failed (" << workbuffersize << " requested)"; size_t mem_free = 0; size_t mem_total = 0; hip_status = hipMemGetInfo(&mem_free, &mem_total); if(hip_status == hipSuccess) { oss << "free vram: " << mem_free << " total vram: " << mem_total; } else { oss << "hipMemGetInfo also failed"; } throw work_buffer_alloc_failure(oss.str()); } auto rocret = rocfft_execution_info_set_work_buffer(info, wbuffer.data(), workbuffersize); if(rocret != rocfft_status_success) { throw std::runtime_error("rocfft_execution_info_set_work_buffer failed"); } } return ret; } fft_status set_callbacks(void* load_cb_host, void* load_cb_data, void* store_cb_host, void* store_cb_data) override { if(run_callbacks) { auto roc_status = rocfft_execution_info_set_load_callback(info, &load_cb_host, &load_cb_data, 0); if(roc_status != rocfft_status_success) return fft_status_from_rocfftparams(roc_status); roc_status = rocfft_execution_info_set_store_callback(info, &store_cb_host, &store_cb_data, 0); if(roc_status != rocfft_status_success) return fft_status_from_rocfftparams(roc_status); } return fft_status_success; } fft_status execute(void** in, void** out) override { auto ret = rocfft_execute(plan, in, out, info); return fft_status_from_rocfftparams(ret); } // scatter data to multiple GPUs and adjust I/O buffers to match void multi_gpu_prepare(std::vector& ibuffer, std::vector& pibuffer, std::vector& pobuffer) override { auto alloc_fields = [&](const fft_params::fft_field& field, fft_array_type array_type, std::vector& pbuffer, bool copy_input) { if(field.bricks.empty()) return; // we have a field defined, clear the list of buffers as // we'll be allocating new ones for each brick pbuffer.clear(); for(const auto& b : field.bricks) { // get brick's length - note that this includes batch // dimension const auto brick_len = b.length(); const auto brick_stride = b.stride; const size_t brick_size_elems = product(brick_len.begin(), brick_len.end()); const size_t elem_size_bytes = var_size(precision, array_type); const size_t brick_size_bytes = brick_size_elems * elem_size_bytes; // set device for the alloc, but we want to return to the // default device as the source of a following memcpy { rocfft_scoped_device dev(b.device); multi_gpu_data.emplace_back(); if(multi_gpu_data.back().alloc(brick_size_bytes) != hipSuccess) throw std::runtime_error("device allocation failure"); pbuffer.push_back(multi_gpu_data.back().data()); } if(copy_input) { // For now, assume we're only splitting on highest FFT // dimension, lower-dimensional FFT data is all // contiguous, and batches are contiguous in each brick. // // That means we can express this as a 2D memcpy. const size_t unbatched_elems_per_brick = product(brick_len.begin() + 1, brick_len.end()); const size_t unbatched_elems_per_fft = product(length.begin(), length.end()); // get this brick's starting offset in the field const size_t brick_offset = b.lower_field_offset(istride, idist) * elem_size_bytes; // copy from original input - note that we're // assuming interleaved data so ibuffer has only one // gpubuf if(hipMemcpy2D(pbuffer.back(), unbatched_elems_per_brick * elem_size_bytes, ibuffer.front().data_offset(brick_offset), unbatched_elems_per_fft * elem_size_bytes, unbatched_elems_per_brick * elem_size_bytes, brick_len.front(), hipMemcpyHostToDevice) != hipSuccess) throw std::runtime_error("hipMemcpy failure"); } } // if we copied the input to all the other devices, and // this is an out-of-place transform, we no longer // need the original input if(copy_input && placement == fft_placement_notinplace) ibuffer.clear(); }; // assume one input, one output field for simple cases if(!ifields.empty()) alloc_fields(ifields.front(), itype, pibuffer, true); if(!ofields.empty()) { if(!ifields.empty() && placement == fft_placement_inplace) pobuffer = pibuffer; else alloc_fields(ofields.front(), otype, pobuffer, false); } } // when preparing for multi-GPU transform, we need to allocate data // on each GPU. This vector remembers all of those allocations. std::vector multi_gpu_data; // gather data after multi-GPU FFT for verification void multi_gpu_finalize(std::vector& obuffer, std::vector& pobuffer) override { if(ofields.empty()) return; for(size_t i = 0; i < ofields.front().bricks.size(); ++i) { const auto& b = ofields.front().bricks[i]; const auto& brick_ptr = pobuffer[i]; const auto brick_len = b.length(); const size_t elem_size_bytes = var_size(precision, otype); // get this brick's starting offset in the field const size_t brick_offset = b.lower_field_offset(ostride, odist) * elem_size_bytes; // switch device to where we're copying from rocfft_scoped_device dev(b.device); // For now, assume we're only splitting on highest FFT // dimension, lower-dimensional FFT data is all // contiguous, and batches are contiguous in each brick. // // That means we can express this as a 2D memcpy. const size_t unbatched_elems_per_brick = product(brick_len.begin() + 1, brick_len.end()); const auto output_length = olength(); const size_t unbatched_elems_per_fft = product(output_length.begin(), output_length.end()); // copy to original output buffer - note that // we're assuming interleaved data so obuffer // has only one gpubuf if(hipMemcpy2D(obuffer.front().data_offset(brick_offset), unbatched_elems_per_fft * elem_size_bytes, brick_ptr, unbatched_elems_per_brick * elem_size_bytes, unbatched_elems_per_brick * elem_size_bytes, brick_len.front(), hipMemcpyDeviceToDevice) != hipSuccess) throw std::runtime_error("hipMemcpy failure"); // device-to-device transfers don't synchronize with the // host, add explicit sync (void)hipDeviceSynchronize(); } pobuffer.clear(); pobuffer.push_back(obuffer.front().data()); } }; #endif upstream/shared/arithmetic.h0000664000175000017500000000401714637212425015136 0ustar kaolkaol/****************************************************************************** * Copyright (C) 2021 - 2022 Advanced Micro Devices, Inc. All rights reserved. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. *******************************************************************************/ #pragma once #include #include // arithmetic helper functions static inline bool IsPo2(size_t u) { return (u != 0) && (0 == (u & (u - 1))); } // help function: Find the smallest power of 2 that is >= n; return its // power of 2 factor // e.g., CeilPo2 (7) returns 3 : (2^3 >= 7) static inline size_t CeilPo2(size_t n) { size_t v = 1, t = 0; while(v < n) { v <<= 1; t++; } return t; } template static inline T DivRoundingUp(T a, T b) { return (a + (b - 1)) / b; } template typename Titer::value_type product(Titer begin, Titer end) { return std::accumulate( begin, end, typename Titer::value_type(1), std::multiplies()); } upstream/shared/enum_to_string.h0000664000175000017500000000620214637212246016040 0ustar kaolkaol// Copyright (C) 2023 Advanced Micro Devices, Inc. All rights reserved. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. #ifndef ENUM_TO_STRING_H #define ENUM_TO_STRING_H #include "fft_params.h" // Return the string of the hipError code. static std::string hipError_to_string(const hipError_t ret) { switch(ret) { case hipSuccess: return "hipSuccess"; case hipErrorInvalidContext: return "hipErrorInvalidContext"; case hipErrorInvalidKernelFile: return "hipErrorInvalidKernelFile"; case hipErrorMemoryAllocation: return "hipErrorMemoryAllocation"; case hipErrorInitializationError: return "hipErrorInitializationError"; case hipErrorLaunchFailure: return "hipErrorLaunchFailure"; case hipErrorLaunchOutOfResources: return "hipErrorLaunchOutOfResources"; case hipErrorInvalidDevice: return "hipErrorInvalidDevice"; case hipErrorInvalidValue: return "hipErrorInvalidValue"; case hipErrorInvalidDevicePointer: return "hipErrorInvalidDevicePointer"; case hipErrorInvalidMemcpyDirection: return "hipErrorInvalidMemcpyDirection"; case hipErrorUnknown: return "hipErrorUnknown"; case hipErrorInvalidResourceHandle: return "hipErrorInvalidResourceHandle"; case hipErrorNotReady: return "hipErrorNotReady"; case hipErrorNoDevice: return "hipErrorNoDevice"; case hipErrorPeerAccessAlreadyEnabled: return "hipErrorPeerAccessAlreadyEnabled"; case hipErrorPeerAccessNotEnabled: return "hipErrorPeerAccessNotEnabled"; case hipErrorRuntimeMemory: return "hipErrorRuntimeMemory"; case hipErrorRuntimeOther: return "hipErrorRuntimeOther"; case hipErrorHostMemoryAlreadyRegistered: return "hipErrorHostMemoryAlreadyRegistered"; case hipErrorHostMemoryNotRegistered: return "hipErrorHostMemoryNotRegistered"; case hipErrorMapBufferObjectFailed: return "hipErrorMapBufferObjectFailed"; case hipErrorTbd: return "hipErrorTbd"; default: throw std::runtime_error("unknown hipError"); } } #endif upstream/shared/gpubuf.h0000664000175000017500000000716414637212425014303 0ustar kaolkaol// Copyright (C) 2021 - 2023 Advanced Micro Devices, Inc. All rights reserved. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. #ifndef ROCFFT_GPUBUF_H #define ROCFFT_GPUBUF_H #include "rocfft_hip.h" #include // Simple RAII class for GPU buffers. T is the type of pointer that // data() returns template class gpubuf_t { public: gpubuf_t() {} // buffers are movable but not copyable gpubuf_t(gpubuf_t&& other) { std::swap(buf, other.buf); std::swap(bsize, other.bsize); std::swap(device, other.device); } gpubuf_t& operator=(gpubuf_t&& other) { std::swap(buf, other.buf); std::swap(bsize, other.bsize); std::swap(device, other.device); return *this; } gpubuf_t(const gpubuf_t&) = delete; gpubuf_t& operator=(const gpubuf_t&) = delete; ~gpubuf_t() { free(); } static bool use_alloc_managed() { return std::getenv("ROCFFT_MALLOC_MANAGED"); } hipError_t alloc(const size_t size) { // remember the device that was current as of alloc, so we can // free on the correct device auto ret = hipGetDevice(&device); if(ret != hipSuccess) return ret; bsize = size; static bool alloc_managed = use_alloc_managed(); free(); ret = alloc_managed ? hipMallocManaged(&buf, bsize) : hipMalloc(&buf, bsize); if(ret != hipSuccess) { buf = nullptr; bsize = 0; } return ret; } size_t size() const { return bsize; } void free() { if(buf != nullptr) { // free on the device we allocated on rocfft_scoped_device dev(device); (void)hipFree(buf); buf = nullptr; bsize = 0; } } // return a pointer to the allocated memory, offset by the // specified number of bytes T* data_offset(size_t offset_bytes = 0) const { void* ptr = static_cast(buf) + offset_bytes; return static_cast(ptr); } T* data() const { return static_cast(buf); } // equality/bool tests bool operator==(std::nullptr_t n) const { return buf == n; } bool operator!=(std::nullptr_t n) const { return buf != n; } operator bool() const { return buf; } private: // The GPU buffer void* buf = nullptr; size_t bsize = 0; int device = 0; }; // default gpubuf that gives out void* pointers typedef gpubuf_t<> gpubuf; #endif upstream/shared/work_queue.h0000664000175000017500000000340714637212246015176 0ustar kaolkaol// Copyright (C) 2022 Advanced Micro Devices, Inc. All rights reserved. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. #pragma once #include #include #include template struct WorkQueue { void push(_WorkItem&& i) { std::unique_lock lock(queueMutex); items.emplace(std::move(i)); emptyWait.notify_all(); } _WorkItem pop() { std::unique_lock lock(queueMutex); while(items.empty()) emptyWait.wait(lock); _WorkItem item(items.front()); items.pop(); return item; } private: std::queue<_WorkItem> items; std::mutex queueMutex; std::condition_variable emptyWait; }; upstream/shared/ptrdiff.h0000664000175000017500000000330314637212425014440 0ustar kaolkaol// Copyright (C) 2022 Advanced Micro Devices, Inc. All rights reserved. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. #pragma once // Compute the farthest point from the original pointer. static size_t compute_ptrdiff(const std::vector& length, const std::vector& stride, const size_t nbatch, const size_t dist) { size_t val = 0; if(!length.empty()) { val = 1; for(unsigned int i = 0; i < length.size(); ++i) { val += (length[i] - 1) * stride[i]; } val += (nbatch - 1) * dist; } return val; } upstream/shared/rocfft_complex.h0000664000175000017500000002322714637212246016024 0ustar kaolkaol// Copyright (C) 2021 - 2023 Advanced Micro Devices, Inc. All rights reserved. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. #ifndef ROCFFT_COMPLEX_H #define ROCFFT_COMPLEX_H #include #if !defined(__HIPCC_RTC__) #include #endif #include #include #ifdef __HIP_PLATFORM_NVIDIA__ typedef __half _Float16; #endif template struct rocfft_complex { Treal x; // Real part Treal y; // Imaginary part // Constructors // Do not initialize the members x or y by default, to ensure that it can // be used in __shared__ and that it is a trivial class compatible with C. __device__ __host__ rocfft_complex() = default; __device__ __host__ rocfft_complex(const rocfft_complex&) = default; __device__ __host__ rocfft_complex(rocfft_complex&&) = default; __device__ __host__ rocfft_complex& operator=(const rocfft_complex& rhs) & = default; __device__ __host__ rocfft_complex& operator=(rocfft_complex&& rhs) & = default; __device__ __host__ ~rocfft_complex() = default; // Constructor from real and imaginary parts __device__ __host__ constexpr rocfft_complex(Treal real, Treal imag) : x{real} , y{imag} { } // Conversion from different precision template __device__ __host__ explicit constexpr rocfft_complex(const rocfft_complex& z) : x(z.x) , y(z.y) { } // Accessors __device__ __host__ constexpr Treal real() const { return x; } __device__ __host__ constexpr Treal imag() const { return y; } // Unary operations __forceinline__ __device__ __host__ rocfft_complex operator-() const { return {-x, -y}; } __forceinline__ __device__ __host__ rocfft_complex operator+() const { return *this; } __device__ __host__ Treal asum(const rocfft_complex& z) { return abs(z.x) + abs(z.y); } // Internal real functions static __forceinline__ __device__ __host__ Treal abs(Treal x) { return x < 0 ? -x : x; } static __forceinline__ __device__ __host__ float sqrt(float x) { return ::sqrtf(x); } static __forceinline__ __device__ __host__ double sqrt(double x) { return ::sqrt(x); } // Addition operators __device__ __host__ auto& operator+=(const rocfft_complex& rhs) { return *this = {x + rhs.x, y + rhs.y}; } __device__ __host__ auto operator+(const rocfft_complex& rhs) const { auto lhs = *this; return lhs += rhs; } // Subtraction operators __device__ __host__ auto& operator-=(const rocfft_complex& rhs) { return *this = {x - rhs.x, y - rhs.y}; } __device__ __host__ auto operator-(const rocfft_complex& rhs) const { auto lhs = *this; return lhs -= rhs; } // Multiplication operators __device__ __host__ auto& operator*=(const rocfft_complex& rhs) { return *this = {x * rhs.x - y * rhs.y, y * rhs.x + x * rhs.y}; } __device__ __host__ auto operator*(const rocfft_complex& rhs) const { auto lhs = *this; return lhs *= rhs; } // Division operators __device__ __host__ auto& operator/=(const rocfft_complex& rhs) { // Form of Robert L. Smith's Algorithm 116 if(abs(rhs.x) > abs(rhs.y)) { Treal ratio = rhs.y / rhs.x; Treal scale = 1 / (rhs.x + rhs.y * ratio); *this = {(x + y * ratio) * scale, (y - x * ratio) * scale}; } else { Treal ratio = rhs.x / rhs.y; Treal scale = 1 / (rhs.x * ratio + rhs.y); *this = {(y + x * ratio) * scale, (y * ratio - x) * scale}; } return *this; } __device__ __host__ auto operator/(const rocfft_complex& rhs) const { auto lhs = *this; return lhs /= rhs; } // Comparison operators __device__ __host__ constexpr bool operator==(const rocfft_complex& rhs) const { return x == rhs.x && y == rhs.y; } __device__ __host__ constexpr bool operator!=(const rocfft_complex& rhs) const { return !(*this == rhs); } // Operators for complex-real computations template __device__ __host__ auto& operator+=(const U& rhs) { return (x += Treal(rhs)), *this; } template __device__ __host__ auto& operator-=(const U& rhs) { return (x -= Treal(rhs)), *this; } __device__ __host__ auto operator+(const Treal& rhs) { auto lhs = *this; return lhs += rhs; } __device__ __host__ auto operator-(const Treal& rhs) { auto lhs = *this; return lhs -= rhs; } template __device__ __host__ auto& operator*=(const U& rhs) { return (x *= Treal(rhs)), (y *= Treal(rhs)), *this; } template __device__ __host__ auto operator*(const U& rhs) const { auto lhs = *this; return lhs *= Treal(rhs); } template __device__ __host__ auto& operator/=(const U& rhs) { return (x /= Treal(rhs)), (y /= Treal(rhs)), *this; } template __device__ __host__ auto operator/(const U& rhs) const { auto lhs = *this; return lhs /= Treal(rhs); } template __device__ __host__ constexpr bool operator==(const U& rhs) const { return x == Treal(rhs) && y == 0; } template __device__ __host__ constexpr bool operator!=(const U& rhs) const { return !(*this == rhs); } }; // Stream operators #if !defined(__HIPCC_RTC__) static std::ostream& operator<<(std::ostream& stream, const _Float16& f) { return stream << static_cast(f); } template std::ostream& operator<<(std::ostream& out, const rocfft_complex& z) { return out << '(' << static_cast(z.x) << ',' << static_cast(z.y) << ')'; } #endif // Operators for real-complex computations template __device__ __host__ rocfft_complex operator+(const U& lhs, const rocfft_complex& rhs) { return {Treal(lhs) + rhs.x, rhs.y}; } template __device__ __host__ rocfft_complex operator-(const U& lhs, const rocfft_complex& rhs) { return {Treal(lhs) - rhs.x, -rhs.y}; } template __device__ __host__ rocfft_complex operator*(const U& lhs, const rocfft_complex& rhs) { return {Treal(lhs) * rhs.x, Treal(lhs) * rhs.y}; } template __device__ __host__ rocfft_complex operator/(const U& lhs, const rocfft_complex& rhs) { // Form of Robert L. Smith's Algorithm 116 if(rocfft_complex::abs(rhs.x) > rocfft_complex::abs(rhs.y)) { Treal ratio = rhs.y / rhs.x; Treal scale = Treal(lhs) / (rhs.x + rhs.y * ratio); return {scale, -scale * ratio}; } else { Treal ratio = rhs.x / rhs.y; Treal scale = Treal(lhs) / (rhs.x * ratio + rhs.y); return {ratio * scale, -scale}; } } template __device__ __host__ constexpr bool operator==(const U& lhs, const rocfft_complex& rhs) { return Treal(lhs) == rhs.x && 0 == rhs.y; } template __device__ __host__ constexpr bool operator!=(const U& lhs, const rocfft_complex& rhs) { return !(lhs == rhs); } // Extending std namespace to handle rocfft_complex datatype namespace std { template __device__ __host__ constexpr Treal real(const rocfft_complex& z) { return z.x; } template __device__ __host__ constexpr Treal imag(const rocfft_complex& z) { return z.y; } template __device__ __host__ constexpr rocfft_complex conj(const rocfft_complex& z) { return {z.x, -z.y}; } template __device__ __host__ inline Treal norm(const rocfft_complex& z) { return (z.x * z.x) + (z.y * z.y); } template __device__ __host__ inline Treal abs(const rocfft_complex& z) { Treal tr = rocfft_complex::abs(z.x), ti = rocfft_complex::abs(z.y); return tr > ti ? (ti /= tr, tr * rocfft_complex::sqrt(ti * ti + 1)) : ti ? (tr /= ti, ti * rocfft_complex::sqrt(tr * tr + 1)) : 0; } } #endif // ROCFFT_COMPLEX_H upstream/shared/hostbuf.h0000664000175000017500000001032314637212425014454 0ustar kaolkaol// Copyright (C) 2021 - 2023 Advanced Micro Devices, Inc. All rights reserved. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. #ifndef ROCFFT_HOSTBUF_H #define ROCFFT_HOSTBUF_H #include "arithmetic.h" #include #include #ifndef WIN32 #include #include #endif // Simple RAII class for host buffers. T is the type of pointer that // data() returns template class hostbuf_t { public: hostbuf_t() {} // buffers are movable but not copyable hostbuf_t(hostbuf_t&& other) { std::swap(buf, other.buf); std::swap(bsize, other.bsize); } hostbuf_t& operator=(hostbuf_t&& other) { std::swap(buf, other.buf); std::swap(bsize, other.bsize); return *this; } hostbuf_t(const hostbuf_t&) = delete; hostbuf_t& operator=(const hostbuf_t&) = delete; ~hostbuf_t() { free(); } void alloc(size_t size) { bsize = size; free(); // we're aligning to multiples of 64 bytes, so round the // allocation size up to the nearest 64 to keep ASAN happy if(size % 64) { size += 64 - size % 64; } // FFTW requires aligned allocations to use faster SIMD instructions. // If enabling hugepages, align to 2 MiB. Otherwise, aligning to // 64 bytes is enough for AVX instructions up to AVX512. #ifdef WIN32 buf = _aligned_malloc(size, 64); #else // On Linux, ask for hugepages to reduce TLB pressure and // improve performance. Allocations need to be aligned to // the hugepage size, and rounded up to the next whole // hugepage. static const size_t TWO_MiB = 2 * 1024 * 1024; if(size >= TWO_MiB) { size_t rounded_size = DivRoundingUp(size, TWO_MiB) * TWO_MiB; buf = aligned_alloc(TWO_MiB, rounded_size); madvise(buf, rounded_size, MADV_HUGEPAGE); } else buf = aligned_alloc(64, size); #endif } size_t size() const { return bsize; } void free() { if(buf != nullptr) { #ifdef WIN32 _aligned_free(buf); #else std::free(buf); #endif buf = nullptr; bsize = 0; } } T* data() const { return static_cast(buf); } // Copy method hostbuf_t copy() const { hostbuf_t copy; copy.alloc(bsize); memcpy(copy.buf, buf, bsize); return copy; } // shrink the buffer to fit the new size void shrink(size_t new_size) { if(new_size > bsize) throw std::runtime_error("can't shrink hostbuf to larger size"); // just pretend the buffer is now that size bsize = new_size; } // equality/bool tests bool operator==(std::nullptr_t n) const { return buf == n; } bool operator!=(std::nullptr_t n) const { return buf != n; } operator bool() const { return buf; } private: // The host buffer void* buf = nullptr; size_t bsize = 0; }; // default hostbuf that gives out void* pointers typedef hostbuf_t<> hostbuf; #endif upstream/shared/fft_params.h0000664000175000017500000035723214637212425015141 0ustar kaolkaol// Copyright (C) 2023 Advanced Micro Devices, Inc. All rights reserved. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. #ifndef FFT_PARAMS_H #define FFT_PARAMS_H #include #include #include #include #include #include #ifdef _OPENMP #include #endif #include #include #include #include #include "../shared/arithmetic.h" #include "../shared/array_validator.h" #include "../shared/data_gen_device.h" #include "../shared/data_gen_host.h" #include "../shared/device_properties.h" #include "../shared/printbuffer.h" #include "../shared/ptrdiff.h" enum fft_status { fft_status_success, fft_status_failure, fft_status_invalid_arg_value, fft_status_invalid_dimensions, fft_status_invalid_array_type, fft_status_invalid_strides, fft_status_invalid_distance, fft_status_invalid_offset, fft_status_invalid_work_buffer, }; enum fft_transform_type { fft_transform_type_complex_forward, fft_transform_type_complex_inverse, fft_transform_type_real_forward, fft_transform_type_real_inverse, }; enum fft_precision { fft_precision_half, fft_precision_single, fft_precision_double, }; static std::istream& operator>>(std::istream& str, fft_precision& precision) { std::string word; str >> word; if(word == "half") precision = fft_precision_half; else if(word == "single") precision = fft_precision_single; else if(word == "double") precision = fft_precision_double; else throw std::runtime_error("Invalid precision specified"); return str; } // fft_input_generator: linearly spaced sequence in [-0.5,0.5] // fft_input_random_generator: pseudo-random sequence in [-0.5,0.5] enum fft_input_generator { fft_input_random_generator_device, fft_input_random_generator_host, fft_input_generator_device, fft_input_generator_host, }; static std::istream& operator>>(std::istream& str, fft_input_generator& gen) { std::string word; str >> word; if(word == "0") gen = fft_input_random_generator_device; else if(word == "1") gen = fft_input_random_generator_host; else if(word == "2") gen = fft_input_generator_device; else if(word == "3") gen = fft_input_generator_host; else throw std::runtime_error("Invalid input generator specified"); return str; } enum fft_array_type { fft_array_type_complex_interleaved, fft_array_type_complex_planar, fft_array_type_real, fft_array_type_hermitian_interleaved, fft_array_type_hermitian_planar, fft_array_type_unset, }; enum fft_result_placement { fft_placement_inplace, fft_placement_notinplace, }; // Determine the size of the data type given the precision and type. template inline Tsize var_size(const fft_precision precision, const fft_array_type type) { size_t var_size = 0; switch(precision) { case fft_precision_half: var_size = sizeof(_Float16); break; case fft_precision_single: var_size = sizeof(float); break; case fft_precision_double: var_size = sizeof(double); break; } switch(type) { case fft_array_type_complex_interleaved: case fft_array_type_hermitian_interleaved: var_size *= 2; break; default: break; } return var_size; } // Given an array type and transform length, strides, etc, load random floats in [0,1] // into the input array of floats/doubles or complex floats/doubles gpu buffers. template inline void set_input(std::vector& input, const fft_input_generator igen, const fft_array_type itype, const std::vector& length, const std::vector& ilength, const std::vector& istride, const Tint1& whole_length, const Tint1& whole_stride, const size_t idist, const size_t nbatch, const hipDeviceProp_t& deviceProp) { auto isize = count_iters(whole_length) * nbatch; switch(itype) { case fft_array_type_complex_interleaved: case fft_array_type_hermitian_interleaved: { auto ibuffer = (rocfft_complex*)input[0].data(); if(igen == fft_input_generator_device) generate_interleaved_data( whole_length, idist, isize, whole_stride, nbatch, ibuffer, deviceProp); else if(igen == fft_input_random_generator_device) generate_random_interleaved_data( whole_length, idist, isize, whole_stride, ibuffer, deviceProp); if(itype == fft_array_type_hermitian_interleaved) { auto ibuffer_2 = (rocfft_complex*)input[0].data(); impose_hermitian_symmetry_interleaved( length, ilength, istride, idist, nbatch, ibuffer_2, deviceProp); } break; } case fft_array_type_complex_planar: case fft_array_type_hermitian_planar: { auto ibuffer_real = (Tfloat*)input[0].data(); auto ibuffer_imag = (Tfloat*)input[1].data(); if(igen == fft_input_generator_device) generate_planar_data(whole_length, idist, isize, whole_stride, nbatch, ibuffer_real, ibuffer_imag, deviceProp); else if(igen == fft_input_random_generator_device) generate_random_planar_data( whole_length, idist, isize, whole_stride, ibuffer_real, ibuffer_imag, deviceProp); if(itype == fft_array_type_hermitian_planar) impose_hermitian_symmetry_planar( length, ilength, istride, idist, nbatch, ibuffer_real, ibuffer_imag, deviceProp); break; } case fft_array_type_real: { auto ibuffer = (Tfloat*)input[0].data(); if(igen == fft_input_generator_device) generate_real_data( whole_length, idist, isize, whole_stride, nbatch, ibuffer, deviceProp); else if(igen == fft_input_random_generator_device) generate_random_real_data( whole_length, idist, isize, whole_stride, ibuffer, deviceProp); break; } default: throw std::runtime_error("Input layout format not yet supported"); } } template inline void set_input(std::vector& input, const fft_input_generator igen, const fft_array_type itype, const std::vector& length, const std::vector& ilength, const std::vector& istride, const Tint1& whole_length, const Tint1& whole_stride, const size_t idist, const size_t nbatch, const hipDeviceProp_t& deviceProp) { switch(itype) { case fft_array_type_complex_interleaved: case fft_array_type_hermitian_interleaved: { if(igen == fft_input_generator_host) generate_interleaved_data(input, whole_length, whole_stride, idist, nbatch); else if(igen == fft_input_random_generator_host) generate_random_interleaved_data( input, whole_length, whole_stride, idist, nbatch); if(itype == fft_array_type_hermitian_interleaved) impose_hermitian_symmetry_interleaved(input, length, istride, idist, nbatch); break; } case fft_array_type_complex_planar: case fft_array_type_hermitian_planar: { if(igen == fft_input_generator_host) generate_planar_data(input, whole_length, whole_stride, idist, nbatch); else if(igen == fft_input_random_generator_host) generate_random_planar_data(input, whole_length, whole_stride, idist, nbatch); if(itype == fft_array_type_hermitian_planar) impose_hermitian_symmetry_planar(input, length, istride, idist, nbatch); break; } case fft_array_type_real: { if(igen == fft_input_generator_host) generate_real_data(input, whole_length, whole_stride, idist, nbatch); else if(igen == fft_input_random_generator_host) generate_random_real_data(input, whole_length, whole_stride, idist, nbatch); break; } default: throw std::runtime_error("Input layout format not yet supported"); } } // unroll set_input for dimension 1, 2, 3 template inline void set_input(std::vector& input, const fft_input_generator igen, const fft_array_type itype, const std::vector& length, const std::vector& ilength, const std::vector& istride, const size_t idist, const size_t nbatch, const hipDeviceProp_t& deviceProp) { switch(length.size()) { case 1: set_input(input, igen, itype, length, ilength, istride, ilength[0], istride[0], idist, nbatch, deviceProp); break; case 2: set_input(input, igen, itype, length, ilength, istride, std::make_tuple(ilength[0], ilength[1]), std::make_tuple(istride[0], istride[1]), idist, nbatch, deviceProp); break; case 3: set_input(input, igen, itype, length, ilength, istride, std::make_tuple(ilength[0], ilength[1], ilength[2]), std::make_tuple(istride[0], istride[1], istride[2]), idist, nbatch, deviceProp); break; default: abort(); } } // Container class for test parameters. class fft_params { public: // All parameters are row-major. std::vector length; std::vector istride; std::vector ostride; size_t nbatch = 1; fft_precision precision = fft_precision_single; fft_input_generator igen = fft_input_random_generator_device; fft_transform_type transform_type = fft_transform_type_complex_forward; fft_result_placement placement = fft_placement_inplace; size_t idist = 0; size_t odist = 0; fft_array_type itype = fft_array_type_unset; fft_array_type otype = fft_array_type_unset; std::vector ioffset = {0, 0}; std::vector ooffset = {0, 0}; std::vector isize; std::vector osize; size_t workbuffersize = 0; struct fft_brick { // all vectors here are row-major, with same length as FFT // dimension + 1 (for batch dimension) // inclusive lower bound of brick std::vector lower; // exclusive upper bound of brick std::vector upper; // stride of brick in memory std::vector stride; // compute the length of this brick std::vector length() const { std::vector ret; for(size_t i = 0; i < lower.size(); ++i) ret.push_back(upper[i] - lower[i]); return ret; } // compute offset of lower bound in a field with the given // stride + dist (batch stride is separate) size_t lower_field_offset(std::vector stride, size_t dist) const { // brick strides include batch, so adjust our input accordingly stride.insert(stride.begin(), dist); return std::inner_product(lower.begin(), lower.end(), stride.begin(), 0); } // location of the brick int device = 0; }; struct fft_field { std::vector bricks; }; // optional brick decomposition of inputs/outputs std::vector ifields; std::vector ofields; // run testing load/store callbacks bool run_callbacks = false; static constexpr double load_cb_scalar = 0.457813941; static constexpr double store_cb_scalar = 0.391504938; // Check that data outside of output strides is not overwritten. // This is only set explicitly on some tests where there's space // between dimensions, but the dimensions are still in-order. // We're not trying to generically find holes in arbitrary data // layouts. // // NOTE: this flag is not included in tokens, since it doesn't // affect how the FFT library behaves. bool check_output_strides = false; // scaling factor - we do a pointwise multiplication of outputs by // this factor double scale_factor = 1.0; fft_params(){}; virtual ~fft_params(){}; // Given an array type, return the name as a string. static std::string array_type_name(const fft_array_type type, bool verbose = true) { switch(type) { case fft_array_type_complex_interleaved: return verbose ? "fft_array_type_complex_interleaved" : "CI"; case fft_array_type_complex_planar: return verbose ? "fft_array_type_complex_planar" : "CP"; case fft_array_type_real: return verbose ? "fft_array_type_real" : "R"; case fft_array_type_hermitian_interleaved: return verbose ? "fft_array_type_hermitian_interleaved" : "HI"; case fft_array_type_hermitian_planar: return verbose ? "fft_array_type_hermitian_planar" : "HP"; case fft_array_type_unset: return verbose ? "fft_array_type_unset" : "UN"; } return ""; } std::string transform_type_name() const { switch(transform_type) { case fft_transform_type_complex_forward: return "fft_transform_type_complex_forward"; case fft_transform_type_complex_inverse: return "fft_transform_type_complex_inverse"; case fft_transform_type_real_forward: return "fft_transform_type_real_forward"; case fft_transform_type_real_inverse: return "fft_transform_type_real_inverse"; default: throw std::runtime_error("Invalid transform type"); } } // Convert to string for output. std::string str(const std::string& separator = ", ") const { // top-level stride/dist are not used when fields are specified. const bool have_ifields = !ifields.empty(); const bool have_ofields = !ofields.empty(); std::stringstream ss; auto print_size_vec = [&](const char* description, const std::vector& vec) { ss << description << ":"; for(auto i : vec) ss << " " << i; ss << separator; }; auto print_fields = [&](const char* description, const std::vector& fields) { for(unsigned int fidx = 0; fidx < fields.size(); ++fidx) { const auto& f = fields[fidx]; ss << description << " " << fidx << ":" << separator; for(unsigned int bidx = 0; bidx < f.bricks.size(); ++bidx) { const auto& b = f.bricks[bidx]; ss << " brick " << bidx << ":" << separator; print_size_vec(" lower", b.lower); print_size_vec(" upper", b.upper); print_size_vec(" stride", b.stride); ss << " device: " << b.device << separator; } } }; print_size_vec("length", length); if(have_ifields) { print_fields("ifield", ifields); } else { print_size_vec("istride", istride); ss << "idist: " << idist << separator; } if(have_ofields) { print_fields("ofield", ofields); } else { print_size_vec("ostride", ostride); ss << "odist: " << odist << separator; } ss << "batch: " << nbatch << separator; print_size_vec("isize", isize); print_size_vec("osize", osize); print_size_vec("ioffset", ioffset); print_size_vec("ooffset", ooffset); if(placement == fft_placement_inplace) ss << "in-place"; else ss << "out-of-place"; ss << separator; ss << "transform_type: " << transform_type_name() << separator; ss << array_type_name(itype) << " -> " << array_type_name(otype) << separator; switch(precision) { case fft_precision_half: ss << "half-precision"; break; case fft_precision_single: ss << "single-precision"; break; case fft_precision_double: ss << "double-precision"; break; } ss << separator; print_size_vec("ilength", ilength()); print_size_vec("olength", olength()); print_size_vec("ibuffer_size", ibuffer_sizes()); print_size_vec("obuffer_size", obuffer_sizes()); if(scale_factor != 1.0) ss << "scale factor: " << scale_factor << separator; return ss.str(); } // Produce a stringified token of the test fft params. std::string token() const { std::string ret; switch(transform_type) { case fft_transform_type_complex_forward: ret += "complex_forward_"; break; case fft_transform_type_complex_inverse: ret += "complex_inverse_"; break; case fft_transform_type_real_forward: ret += "real_forward_"; break; case fft_transform_type_real_inverse: ret += "real_inverse_"; break; } auto append_size_vec = [&ret](const std::vector& vec) { for(auto s : vec) { ret += "_"; ret += std::to_string(s); } }; ret += "len"; append_size_vec(length); switch(precision) { case fft_precision_half: ret += "_half_"; break; case fft_precision_single: ret += "_single_"; break; case fft_precision_double: ret += "_double_"; break; } switch(placement) { case fft_placement_inplace: ret += "ip_"; break; case fft_placement_notinplace: ret += "op_"; break; } ret += "batch_"; ret += std::to_string(nbatch); auto append_array_type = [&ret](fft_array_type type) { switch(type) { case fft_array_type_complex_interleaved: ret += "CI"; break; case fft_array_type_complex_planar: ret += "CP"; break; case fft_array_type_real: ret += "R"; break; case fft_array_type_hermitian_interleaved: ret += "HI"; break; case fft_array_type_hermitian_planar: ret += "HP"; break; default: ret += "UN"; break; } }; auto append_brick_info = [&ret, &append_size_vec](const fft_brick& b) { ret += "_brick"; ret += "_lower"; append_size_vec(b.lower); ret += "_upper"; append_size_vec(b.upper); ret += "_stride"; append_size_vec(b.stride); ret += "_dev_"; ret += std::to_string(b.device); }; const bool have_ifields = !ifields.empty(); const bool have_ofields = !ofields.empty(); if(have_ifields) { for(const auto& f : ifields) { ret += "_ifield"; for(const auto& b : f.bricks) append_brick_info(b); } } else { ret += "_istride"; append_size_vec(istride); ret += "_"; append_array_type(itype); } if(have_ofields) { for(const auto& f : ofields) { ret += "_ofield"; for(const auto& b : f.bricks) append_brick_info(b); } } else { ret += "_ostride"; append_size_vec(ostride); ret += "_"; append_array_type(otype); } if(!have_ifields) { ret += "_idist_"; ret += std::to_string(idist); } if(!have_ofields) { ret += "_odist_"; ret += std::to_string(odist); } if(!have_ifields) { ret += "_ioffset"; append_size_vec(ioffset); } if(!have_ofields) { ret += "_ooffset"; append_size_vec(ooffset); } if(run_callbacks) ret += "_CB"; if(scale_factor != 1.0) ret += "_scale"; return ret; } // Set all params from a stringified token. void from_token(std::string token) { std::vector vals; std::string delimiter = "_"; { size_t pos = 0; while((pos = token.find(delimiter)) != std::string::npos) { auto val = token.substr(0, pos); vals.push_back(val); token.erase(0, pos + delimiter.length()); } vals.push_back(token); } auto size_parser = [](const std::vector& vals, const std::string token, size_t& pos) { if(vals[pos++] != token) throw std::runtime_error("Unable to parse token"); return std::stoull(vals[pos++]); }; auto vector_parser = [](const std::vector& vals, const std::string token, size_t& pos) { if(vals[pos++] != token) throw std::runtime_error("Unable to parse token"); std::vector vec; while(pos < vals.size()) { if(std::all_of(vals[pos].begin(), vals[pos].end(), ::isdigit)) { vec.push_back(std::stoull(vals[pos++])); } else { break; } } return vec; }; auto type_parser = [](const std::string& val) { if(val == "CI") return fft_array_type_complex_interleaved; else if(val == "CP") return fft_array_type_complex_planar; else if(val == "R") return fft_array_type_real; else if(val == "HI") return fft_array_type_hermitian_interleaved; else if(val == "HP") return fft_array_type_hermitian_planar; return fft_array_type_unset; }; auto field_parser = [&vector_parser, &size_parser](const std::vector& vals, size_t& pos, std::vector& output) { // skip over ifield/ofield word pos++; fft_field& f = output.emplace_back(); while(pos < vals.size() && vals[pos] == "brick") { fft_brick& b = f.bricks.emplace_back(); pos++; b.lower = vector_parser(vals, "lower", pos); b.upper = vector_parser(vals, "upper", pos); b.stride = vector_parser(vals, "stride", pos); b.device = size_parser(vals, "dev", pos); } }; size_t pos = 0; bool complex = vals[pos++] == "complex"; bool forward = vals[pos++] == "forward"; if(complex && forward) transform_type = fft_transform_type_complex_forward; if(complex && !forward) transform_type = fft_transform_type_complex_inverse; if(!complex && forward) transform_type = fft_transform_type_real_forward; if(!complex && !forward) transform_type = fft_transform_type_real_inverse; length = vector_parser(vals, "len", pos); if(vals[pos] == "half") precision = fft_precision_half; else if(vals[pos] == "single") precision = fft_precision_single; else if(vals[pos] == "double") precision = fft_precision_double; pos++; placement = (vals[pos++] == "ip") ? fft_placement_inplace : fft_placement_notinplace; nbatch = size_parser(vals, "batch", pos); // strides, bricks etc are mixed in from here, so just keep // looking at the next token to decide what to do while(pos < vals.size()) { const auto& next_token = vals[pos]; if(next_token == "istride") { istride = vector_parser(vals, "istride", pos); itype = type_parser(vals[pos]); pos++; } else if(next_token == "ostride") { ostride = vector_parser(vals, "ostride", pos); otype = type_parser(vals[pos]); pos++; } else if(next_token == "idist") idist = size_parser(vals, "idist", pos); else if(next_token == "odist") odist = size_parser(vals, "odist", pos); else if(next_token == "ioffset") ioffset = vector_parser(vals, "ioffset", pos); else if(next_token == "ooffset") ooffset = vector_parser(vals, "ooffset", pos); else if(next_token == "ifield") field_parser(vals, pos, ifields); else if(next_token == "ofield") field_parser(vals, pos, ofields); else break; } if(pos < vals.size() && vals[pos] == "CB") { run_callbacks = true; ++pos; } if(pos < vals.size() && vals[pos] == "scale") { // just pick some factor that's not zero or one scale_factor = 0.1239; ++pos; } } // Stream output operator (for gtest, etc). friend std::ostream& operator<<(std::ostream& stream, const fft_params& params) { stream << params.str(); return stream; } // Dimension of the transform. size_t dim() const { return length.size(); } virtual std::vector ilength() const { auto ilength = length; if(transform_type == fft_transform_type_real_inverse) ilength[dim() - 1] = ilength[dim() - 1] / 2 + 1; return ilength; } virtual std::vector olength() const { auto olength = length; if(transform_type == fft_transform_type_real_forward) olength[dim() - 1] = olength[dim() - 1] / 2 + 1; return olength; } static size_t nbuffer(const fft_array_type type) { switch(type) { case fft_array_type_real: case fft_array_type_complex_interleaved: case fft_array_type_hermitian_interleaved: return 1; case fft_array_type_complex_planar: case fft_array_type_hermitian_planar: return 2; case fft_array_type_unset: return 0; } return 0; } // Number of input buffers size_t nibuffer() const { return nbuffer(itype); } // Number of output buffers size_t nobuffer() const { return nbuffer(otype); } void set_iotypes() { if(itype == fft_array_type_unset) { switch(transform_type) { case fft_transform_type_complex_forward: case fft_transform_type_complex_inverse: itype = fft_array_type_complex_interleaved; break; case fft_transform_type_real_forward: itype = fft_array_type_real; break; case fft_transform_type_real_inverse: itype = fft_array_type_hermitian_interleaved; break; default: throw std::runtime_error("Invalid transform type"); } } if(otype == fft_array_type_unset) { switch(transform_type) { case fft_transform_type_complex_forward: case fft_transform_type_complex_inverse: otype = fft_array_type_complex_interleaved; break; case fft_transform_type_real_forward: otype = fft_array_type_hermitian_interleaved; break; case fft_transform_type_real_inverse: otype = fft_array_type_real; break; default: throw std::runtime_error("Invalid transform type"); } } } // Check that the input and output types are consistent. bool check_iotypes() const { switch(itype) { case fft_array_type_complex_interleaved: case fft_array_type_complex_planar: case fft_array_type_hermitian_interleaved: case fft_array_type_hermitian_planar: case fft_array_type_real: break; default: throw std::runtime_error("Invalid Input array type format"); } switch(otype) { case fft_array_type_complex_interleaved: case fft_array_type_complex_planar: case fft_array_type_hermitian_interleaved: case fft_array_type_hermitian_planar: case fft_array_type_real: break; default: throw std::runtime_error("Invalid Input array type format"); } // Check that format choices are supported if(transform_type != fft_transform_type_real_forward && transform_type != fft_transform_type_real_inverse) { if(placement == fft_placement_inplace && itype != otype) { throw std::runtime_error( "In-place transforms must have identical input and output types"); } } bool okformat = true; switch(itype) { case fft_array_type_complex_interleaved: case fft_array_type_complex_planar: okformat = (otype == fft_array_type_complex_interleaved || otype == fft_array_type_complex_planar); break; case fft_array_type_hermitian_interleaved: case fft_array_type_hermitian_planar: okformat = otype == fft_array_type_real; break; case fft_array_type_real: okformat = (otype == fft_array_type_hermitian_interleaved || otype == fft_array_type_hermitian_planar); break; default: throw std::runtime_error("Invalid Input array type format"); } return okformat; } // Given a length vector, set the rest of the strides. // The optional argument stride0 sets the stride for the contiguous dimension. // The optional rcpadding argument sets the stride correctly for in-place // multi-dimensional real/complex transforms. // Format is row-major. template std::vector compute_stride(const std::vector& length, const std::vector& stride0 = std::vector(), const bool rcpadding = false) const { std::vector stride(dim()); size_t dimoffset = 0; if(stride0.size() == 0) { // Set the contiguous stride: stride[dim() - 1] = 1; dimoffset = 1; } else { // Copy the input values to the end of the stride array: for(size_t i = 0; i < stride0.size(); ++i) { stride[dim() - stride0.size() + i] = stride0[i]; } } if(stride0.size() < dim()) { // Compute any remaining values via recursion. for(size_t i = dim() - dimoffset - stride0.size(); i-- > 0;) { auto lengthip1 = length[i + 1]; if(rcpadding && i == dim() - 2) { lengthip1 = 2 * (lengthip1 / 2 + 1); } stride[i] = stride[i + 1] * lengthip1; } } return stride; } void compute_istride() { istride = compute_stride(ilength(), istride, placement == fft_placement_inplace && transform_type == fft_transform_type_real_forward); } void compute_ostride() { ostride = compute_stride(olength(), ostride, placement == fft_placement_inplace && transform_type == fft_transform_type_real_inverse); } virtual void compute_isize() { auto il = ilength(); size_t val = compute_ptrdiff(il, istride, nbatch, idist); isize.resize(nibuffer()); for(unsigned int i = 0; i < isize.size(); ++i) { isize[i] = val + ioffset[i]; } } virtual void compute_osize() { auto ol = olength(); size_t val = compute_ptrdiff(ol, ostride, nbatch, odist); osize.resize(nobuffer()); for(unsigned int i = 0; i < osize.size(); ++i) { osize[i] = val + ooffset[i]; } } std::vector ibuffer_sizes() const { std::vector ibuffer_sizes; // In-place real-to-complex transforms need to have enough space in the input buffer to // accomadate the output, which is slightly larger. if(placement == fft_placement_inplace && transform_type == fft_transform_type_real_forward) { return obuffer_sizes(); } if(isize.empty()) return ibuffer_sizes; switch(itype) { case fft_array_type_complex_planar: case fft_array_type_hermitian_planar: ibuffer_sizes.resize(2); break; default: ibuffer_sizes.resize(1); } for(unsigned i = 0; i < ibuffer_sizes.size(); i++) { ibuffer_sizes[i] = isize[i] * var_size(precision, itype); } return ibuffer_sizes; } virtual std::vector obuffer_sizes() const { std::vector obuffer_sizes; if(osize.empty()) return obuffer_sizes; switch(otype) { case fft_array_type_complex_planar: case fft_array_type_hermitian_planar: obuffer_sizes.resize(2); break; default: obuffer_sizes.resize(1); } for(unsigned i = 0; i < obuffer_sizes.size(); i++) { obuffer_sizes[i] = osize[i] * var_size(precision, otype); } return obuffer_sizes; } // Compute the idist for a given transform based on the placeness, transform type, and data // layout. size_t compute_idist() const { size_t dist = 0; // In-place 1D transforms need extra dist. if(transform_type == fft_transform_type_real_forward && dim() == 1 && placement == fft_placement_inplace) { dist = 2 * (length[0] / 2 + 1) * istride[0]; return dist; } if(transform_type == fft_transform_type_real_inverse && dim() == 1) { dist = (length[0] / 2 + 1) * istride[0]; return dist; } dist = (transform_type == fft_transform_type_real_inverse) ? (length[dim() - 1] / 2 + 1) * istride[dim() - 1] : length[dim() - 1] * istride[dim() - 1]; for(unsigned int i = 0; i < dim() - 1; ++i) { dist = std::max(length[i] * istride[i], dist); } return dist; } void set_idist() { if(idist != 0) return; idist = compute_idist(); } // Compute the odist for a given transform based on the placeness, transform type, and data // layout. Row-major. size_t compute_odist() const { size_t dist = 0; // In-place 1D transforms need extra dist. if(transform_type == fft_transform_type_real_inverse && dim() == 1 && placement == fft_placement_inplace) { dist = 2 * (length[0] / 2 + 1) * ostride[0]; return dist; } if(transform_type == fft_transform_type_real_forward && dim() == 1) { dist = (length[0] / 2 + 1) * ostride[0]; return dist; } dist = (transform_type == fft_transform_type_real_forward) ? (length[dim() - 1] / 2 + 1) * ostride[dim() - 1] : length[dim() - 1] * ostride[dim() - 1]; for(unsigned int i = 0; i < dim() - 1; ++i) { dist = std::max(length[i] * ostride[i], dist); } return dist; } void set_odist() { if(odist != 0) return; odist = compute_odist(); } // Put the length, stride, batch, and dist into a single length/stride array and pass off to the // validity checker. bool valid_length_stride_batch_dist(const std::vector& l0, const std::vector& s0, const size_t n, const size_t dist, const int verbose = 0) const { if(l0.size() != s0.size()) return false; // Length and stride vectors, including bathes: std::vector l{}, s{}; for(unsigned int i = 0; i < l0.size(); ++i) { if(l0[i] > 1) { if(s0[i] == 0) return false; l.push_back(l0[i]); s.push_back(s0[i]); } } if(n > 1) { if(dist == 0) return false; l.push_back(n); s.push_back(dist); } return array_valid(l, s, verbose); } // Return true if the given GPU parameters would produce a valid transform. bool valid(const int verbose) const { if(ioffset.size() < nibuffer() || ooffset.size() < nobuffer()) return false; // Check that in-place transforms have the same input and output stride: if(placement == fft_placement_inplace) { const auto stridesize = std::min(istride.size(), ostride.size()); bool samestride = true; for(unsigned int i = 0; i < stridesize; ++i) { if(istride[i] != ostride[i]) samestride = false; } if((transform_type == fft_transform_type_complex_forward || transform_type == fft_transform_type_complex_inverse) && !samestride) { // In-place transforms require identical input and output strides. if(verbose) { std::cout << "istride:"; for(const auto& i : istride) std::cout << " " << i; std::cout << " ostride0:"; for(const auto& i : ostride) std::cout << " " << i; std::cout << " differ; skipped for in-place transforms: skipping test" << std::endl; } return false; } if((transform_type == fft_transform_type_complex_forward || transform_type == fft_transform_type_complex_inverse) && (idist != odist) && nbatch > 1) { // In-place transforms require identical distance, if // batch > 1. If batch is 1 then dist is ignored and // the FFT should still work. if(verbose) { std::cout << "idist:" << idist << " odist:" << odist << " differ; skipped for in-place transforms: skipping test" << std::endl; } return false; } if((transform_type == fft_transform_type_real_forward || transform_type == fft_transform_type_real_inverse) && (istride.back() != 1 || ostride.back() != 1)) { // In-place real/complex transforms require unit strides. if(verbose) { std::cout << "istride.back(): " << istride.back() << " ostride.back(): " << ostride.back() << " must be unitary for in-place real/complex transforms: skipping test" << std::endl; } return false; } if((itype == fft_array_type_complex_interleaved && otype == fft_array_type_complex_planar) || (itype == fft_array_type_complex_planar && otype == fft_array_type_complex_interleaved)) { if(verbose) { std::cout << "In-place c2c transforms require identical io types; skipped.\n"; } return false; } // Check offsets switch(transform_type) { case fft_transform_type_complex_forward: case fft_transform_type_complex_inverse: for(unsigned int i = 0; i < nibuffer(); ++i) { if(ioffset[i] != ooffset[i]) return false; } break; case fft_transform_type_real_forward: if(ioffset[0] != 2 * ooffset[0]) return false; break; case fft_transform_type_real_inverse: if(2 * ioffset[0] != ooffset[0]) return false; break; } } if(!check_iotypes()) return false; // we can only check output strides on out-of-place // transforms, since we need to initialize output to a known // pattern if(placement == fft_placement_inplace && check_output_strides) return false; // Check input and output strides if(valid_length_stride_batch_dist(ilength(), istride, nbatch, idist, verbose) != true) { if(verbose) std::cout << "Invalid input data format.\n"; return false; } if(!(ilength() == olength() && istride == ostride && idist == odist)) { // Only check if different if(valid_length_stride_batch_dist(olength(), ostride, nbatch, odist, verbose) != true) { if(verbose) std::cout << "Invalid output data format.\n"; return false; } } // The parameters are valid. return true; } // Fill in any missing parameters. void validate() { set_iotypes(); compute_istride(); compute_ostride(); set_idist(); set_odist(); compute_isize(); compute_osize(); validate_fields(); } virtual void validate_fields() const { if(!ifields.empty() || !ofields.empty()) throw std::runtime_error("input/output fields are unsupported"); } // Column-major getters: std::vector length_cm() const { auto length_cm = length; std::reverse(std::begin(length_cm), std::end(length_cm)); return length_cm; } std::vector ilength_cm() const { auto ilength_cm = ilength(); std::reverse(std::begin(ilength_cm), std::end(ilength_cm)); return ilength_cm; } std::vector olength_cm() const { auto olength_cm = olength(); std::reverse(std::begin(olength_cm), std::end(olength_cm)); return olength_cm; } std::vector istride_cm() const { auto istride_cm = istride; std::reverse(std::begin(istride_cm), std::end(istride_cm)); return istride_cm; } std::vector ostride_cm() const { auto ostride_cm = ostride; std::reverse(std::begin(ostride_cm), std::end(ostride_cm)); return ostride_cm; } bool is_planar() const { if(itype == fft_array_type_complex_planar || itype == fft_array_type_hermitian_planar) return true; if(otype == fft_array_type_complex_planar || otype == fft_array_type_hermitian_planar) return true; return false; } // Given a data type and dimensions, fill the buffer, imposing Hermitian symmetry if necessary. template inline void compute_input(std::vector& input) { auto deviceProp = get_curr_device_prop(); switch(precision) { case fft_precision_half: set_input( input, igen, itype, length, ilength(), istride, idist, nbatch, deviceProp); break; case fft_precision_double: set_input( input, igen, itype, length, ilength(), istride, idist, nbatch, deviceProp); break; case fft_precision_single: set_input( input, igen, itype, length, ilength(), istride, idist, nbatch, deviceProp); break; } } template void print_ibuffer(const std::vector& buf, Tstream& stream = std::cout) const { switch(itype) { case fft_array_type_complex_interleaved: case fft_array_type_hermitian_interleaved: { switch(precision) { case fft_precision_half: { buffer_printer> s; s.print_buffer(buf, ilength(), istride, nbatch, idist, ioffset); break; } case fft_precision_single: { buffer_printer> s; s.print_buffer(buf, ilength(), istride, nbatch, idist, ioffset); break; } case fft_precision_double: { buffer_printer> s; s.print_buffer(buf, ilength(), istride, nbatch, idist, ioffset); break; } } break; } case fft_array_type_complex_planar: case fft_array_type_hermitian_planar: case fft_array_type_real: { switch(precision) { case fft_precision_half: { buffer_printer<_Float16> s; s.print_buffer(buf, ilength(), istride, nbatch, idist, ioffset); break; } case fft_precision_single: { buffer_printer s; s.print_buffer(buf, ilength(), istride, nbatch, idist, ioffset); break; } case fft_precision_double: { buffer_printer s; s.print_buffer(buf, ilength(), istride, nbatch, idist, ioffset); break; } } break; } default: throw std::runtime_error("Invalid itype in print_ibuffer"); } } template void print_obuffer(const std::vector& buf, Tstream& stream = std::cout) const { switch(otype) { case fft_array_type_complex_interleaved: case fft_array_type_hermitian_interleaved: { switch(precision) { case fft_precision_half: { buffer_printer> s; s.print_buffer(buf, olength(), ostride, nbatch, odist, ooffset); break; } case fft_precision_single: { buffer_printer> s; s.print_buffer(buf, olength(), ostride, nbatch, odist, ooffset); break; } case fft_precision_double: buffer_printer> s; s.print_buffer(buf, olength(), ostride, nbatch, odist, ooffset); break; } break; } case fft_array_type_complex_planar: case fft_array_type_hermitian_planar: case fft_array_type_real: { switch(precision) { case fft_precision_half: { buffer_printer<_Float16> s; s.print_buffer(buf, olength(), ostride, nbatch, odist, ooffset); break; } case fft_precision_single: { buffer_printer s; s.print_buffer(buf, olength(), ostride, nbatch, odist, ooffset); break; } case fft_precision_double: { buffer_printer s; s.print_buffer(buf, olength(), ostride, nbatch, odist, ooffset); break; } } break; } default: throw std::runtime_error("Invalid itype in print_obuffer"); } } void print_ibuffer_flat(const std::vector& buf) const { switch(itype) { case fft_array_type_complex_interleaved: case fft_array_type_hermitian_interleaved: { switch(precision) { case fft_precision_half: { buffer_printer> s; s.print_buffer_flat(buf, osize, ooffset); break; } case fft_precision_single: { buffer_printer> s; s.print_buffer_flat(buf, osize, ooffset); break; } case fft_precision_double: buffer_printer> s; s.print_buffer_flat(buf, osize, ooffset); break; } break; } case fft_array_type_complex_planar: case fft_array_type_hermitian_planar: case fft_array_type_real: { switch(precision) { case fft_precision_half: { buffer_printer<_Float16> s; s.print_buffer_flat(buf, osize, ooffset); break; } case fft_precision_single: { buffer_printer s; s.print_buffer_flat(buf, osize, ooffset); break; } case fft_precision_double: { buffer_printer s; s.print_buffer_flat(buf, osize, ooffset); break; } } break; default: throw std::runtime_error("Invalid itype in print_ibuffer_flat"); } } } void print_obuffer_flat(const std::vector& buf) const { switch(otype) { case fft_array_type_complex_interleaved: case fft_array_type_hermitian_interleaved: { switch(precision) { case fft_precision_half: { buffer_printer> s; s.print_buffer_flat(buf, osize, ooffset); break; } case fft_precision_single: { buffer_printer> s; s.print_buffer_flat(buf, osize, ooffset); break; } case fft_precision_double: buffer_printer> s; s.print_buffer_flat(buf, osize, ooffset); break; } break; } case fft_array_type_complex_planar: case fft_array_type_hermitian_planar: case fft_array_type_real: { switch(precision) { case fft_precision_half: { buffer_printer<_Float16> s; s.print_buffer_flat(buf, osize, ooffset); break; } case fft_precision_single: { buffer_printer s; s.print_buffer_flat(buf, osize, ooffset); break; } case fft_precision_double: { buffer_printer s; s.print_buffer_flat(buf, osize, ooffset); break; } } break; default: throw std::runtime_error("Invalid itype in print_ibuffer_flat"); } } } virtual fft_status set_callbacks(void* load_cb_host, void* load_cb_data, void* store_cb_host, void* store_cb_data) { return fft_status_success; } virtual fft_status execute(void** in, void** out) { return fft_status_success; }; size_t fft_params_vram_footprint() { return fft_params::vram_footprint(); } virtual size_t vram_footprint() { const auto ibuf_size = ibuffer_sizes(); size_t val = std::accumulate(ibuf_size.begin(), ibuf_size.end(), (size_t)1); if(placement == fft_placement_notinplace) { const auto obuf_size = obuffer_sizes(); val += std::accumulate(obuf_size.begin(), obuf_size.end(), (size_t)1); } return val; } // Specific exception type for work buffer allocation failure. // Tests that hit this can't fit on the GPU and should be skipped. struct work_buffer_alloc_failure : public std::runtime_error { work_buffer_alloc_failure(const std::string& s) : std::runtime_error(s) { } }; virtual fft_status create_plan() { return fft_status_success; } // Change a forward transform to it's inverse void inverse_from_forward(fft_params& params_forward) { switch(params_forward.transform_type) { case fft_transform_type_complex_forward: transform_type = fft_transform_type_complex_inverse; break; case fft_transform_type_real_forward: transform_type = fft_transform_type_real_inverse; break; default: throw std::runtime_error("Transform type not forward."); } length = params_forward.length; istride = params_forward.ostride; ostride = params_forward.istride; nbatch = params_forward.nbatch; precision = params_forward.precision; placement = params_forward.placement; idist = params_forward.odist; odist = params_forward.idist; itype = params_forward.otype; otype = params_forward.itype; ioffset = params_forward.ooffset; ooffset = params_forward.ioffset; run_callbacks = params_forward.run_callbacks; check_output_strides = params_forward.check_output_strides; scale_factor = 1 / params_forward.scale_factor; } // prepare for multi-GPU transform. Generated input is in ibuffer. // pibuffer, pobuffer are the pointers that will be passed to the // FFT library's "execute" API. virtual void multi_gpu_prepare(std::vector& ibuffer, std::vector& pibuffer, std::vector& pobuffer) { } // finalize multi-GPU transform. pobuffers are the pointers // provided to the FFT library's "execute" API. obuffer is the // buffer where transform output needs to go for validation virtual void multi_gpu_finalize(std::vector& obuffer, std::vector& pobuffer) {} // create bricks in the specified field for the specified number // of devices. The field is split along the highest FFT // dimension, and the length only includes FFT lengths, not batch // dimension. void distribute_field(int deviceCount, std::vector& fields, const std::vector& field_length) { size_t slowLen = field_length.front(); if(slowLen < static_cast(deviceCount)) throw std::runtime_error("too many devices to distribute length " + std::to_string(slowLen)); auto& field = fields.emplace_back(); for(int i = 0; i < deviceCount; ++i) { // start at origin std::vector field_lower(field_length.size()); std::vector field_upper(field_length.size()); // note: slowest FFT dim is index 0 in these coordinates field_lower[0] = slowLen / deviceCount * i; // last brick needs to include the whole slow len if(i == deviceCount - 1) { field_upper[0] = slowLen; } else { field_upper[0] = std::min(slowLen, field_lower[0] + slowLen / deviceCount); } for(unsigned int upperDim = 1; upperDim < field_length.size(); ++upperDim) { field_upper[upperDim] = field_length[upperDim]; } // field coordinates also need to include batch field_lower.insert(field_lower.begin(), 0); field_upper.insert(field_upper.begin(), nbatch); // bricks have contiguous strides size_t brick_dist = 1; std::vector brick_stride(field_lower.size()); for(size_t distIdx = 0; distIdx < field_lower.size(); ++distIdx) { // fill strides from fastest to slowest *(brick_stride.rbegin() + distIdx) = brick_dist; brick_dist *= *(field_upper.rbegin() + distIdx) - *(field_lower.rbegin() + distIdx); } field.bricks.push_back( fft_params::fft_brick{field_lower, field_upper, brick_stride, i}); } } void distribute_input(int deviceCount) { distribute_field(deviceCount, ifields, length); } void distribute_output(int deviceCount) { distribute_field(deviceCount, ofields, olength()); } }; // This is used with the program_options class so that the user can type an integer on the // command line and we store into an enum varaible template std::basic_istream<_Elem, _Traits>& operator>>(std::basic_istream<_Elem, _Traits>& stream, fft_array_type& atype) { unsigned tmp; stream >> tmp; atype = fft_array_type(tmp); return stream; } // similarly for transform type template std::basic_istream<_Elem, _Traits>& operator>>(std::basic_istream<_Elem, _Traits>& stream, fft_transform_type& ttype) { unsigned tmp; stream >> tmp; ttype = fft_transform_type(tmp); return stream; } // Returns pairs of startindex, endindex, for 1D, 2D, 3D lengths template std::vector> partition_colmajor(const T1& length) { return partition_base(length, compute_partition_count(length)); } // Partition on the rightmost part of the tuple, for col-major indexing template std::vector, std::tuple>> partition_colmajor(const std::tuple& length) { auto partitions = partition_base(std::get<1>(length), compute_partition_count(length)); std::vector, std::tuple>> ret(partitions.size()); for(size_t i = 0; i < partitions.size(); ++i) { std::get<1>(ret[i].first) = partitions[i].first; std::get<0>(ret[i].first) = 0; std::get<1>(ret[i].second) = partitions[i].second; std::get<0>(ret[i].second) = std::get<0>(length); } return ret; } template std::vector, std::tuple>> partition_colmajor(const std::tuple& length) { auto partitions = partition_base(std::get<2>(length), compute_partition_count(length)); std::vector, std::tuple>> ret(partitions.size()); for(size_t i = 0; i < partitions.size(); ++i) { std::get<2>(ret[i].first) = partitions[i].first; std::get<1>(ret[i].first) = 0; std::get<0>(ret[i].first) = 0; std::get<2>(ret[i].second) = partitions[i].second; std::get<1>(ret[i].second) = std::get<1>(length); std::get<0>(ret[i].second) = std::get<0>(length); } return ret; } // Copy data of dimensions length with strides istride and length idist between batches to // a buffer with strides ostride and length odist between batches. The input and output // types are identical. template inline void copy_buffers_1to1(const Tval* input, Tval* output, const Tint1& whole_length, const size_t nbatch, const Tint2& istride, const size_t idist, const Tint3& ostride, const size_t odist, const std::vector& ioffset, const std::vector& ooffset) { const bool idx_equals_odx = istride == ostride && idist == odist; size_t idx_base = 0; size_t odx_base = 0; auto partitions = partition_rowmajor(whole_length); for(size_t b = 0; b < nbatch; b++, idx_base += idist, odx_base += odist) { #ifdef _OPENMP #pragma omp parallel for num_threads(partitions.size()) #endif for(size_t part = 0; part < partitions.size(); ++part) { auto index = partitions[part].first; const auto length = partitions[part].second; do { const auto idx = compute_index(index, istride, idx_base); const auto odx = idx_equals_odx ? idx : compute_index(index, ostride, odx_base); output[odx + ooffset[0]] = input[idx + ioffset[0]]; } while(increment_rowmajor(index, length)); } } } // Copy data of dimensions length with strides istride and length idist between batches to // a buffer with strides ostride and length odist between batches. The input type is // planar and the output type is complex interleaved. template inline void copy_buffers_2to1(const Tval* input0, const Tval* input1, rocfft_complex* output, const Tint1& whole_length, const size_t nbatch, const Tint2& istride, const size_t idist, const Tint3& ostride, const size_t odist, const std::vector& ioffset, const std::vector& ooffset) { const bool idx_equals_odx = istride == ostride && idist == odist; size_t idx_base = 0; size_t odx_base = 0; auto partitions = partition_rowmajor(whole_length); for(size_t b = 0; b < nbatch; b++, idx_base += idist, odx_base += odist) { #ifdef _OPENMP #pragma omp parallel for num_threads(partitions.size()) #endif for(size_t part = 0; part < partitions.size(); ++part) { auto index = partitions[part].first; const auto length = partitions[part].second; do { const auto idx = compute_index(index, istride, idx_base); const auto odx = idx_equals_odx ? idx : compute_index(index, ostride, odx_base); output[odx + ooffset[0]] = rocfft_complex(input0[idx + ioffset[0]], input1[idx + ioffset[1]]); } while(increment_rowmajor(index, length)); } } } // Copy data of dimensions length with strides istride and length idist between batches to // a buffer with strides ostride and length odist between batches. The input type is // complex interleaved and the output type is planar. template inline void copy_buffers_1to2(const rocfft_complex* input, Tval* output0, Tval* output1, const Tint1& whole_length, const size_t nbatch, const Tint2& istride, const size_t idist, const Tint3& ostride, const size_t odist, const std::vector& ioffset, const std::vector& ooffset) { const bool idx_equals_odx = istride == ostride && idist == odist; size_t idx_base = 0; size_t odx_base = 0; auto partitions = partition_rowmajor(whole_length); for(size_t b = 0; b < nbatch; b++, idx_base += idist, odx_base += odist) { #ifdef _OPENMP #pragma omp parallel for num_threads(partitions.size()) #endif for(size_t part = 0; part < partitions.size(); ++part) { auto index = partitions[part].first; const auto length = partitions[part].second; do { const auto idx = compute_index(index, istride, idx_base); const auto odx = idx_equals_odx ? idx : compute_index(index, ostride, odx_base); output0[odx + ooffset[0]] = input[idx + ioffset[0]].real(); output1[odx + ooffset[1]] = input[idx + ioffset[0]].imag(); } while(increment_rowmajor(index, length)); } } } // Copy data of dimensions length with strides istride and length idist between batches to // a buffer with strides ostride and length odist between batches. The input type given // by itype, and the output type is given by otype. template inline void copy_buffers(const std::vector& input, std::vector& output, const Tint1& length, const size_t nbatch, const fft_precision precision, const fft_array_type itype, const Tint2& istride, const size_t idist, const fft_array_type otype, const Tint3& ostride, const size_t odist, const std::vector& ioffset, const std::vector& ooffset) { if(itype == otype) { switch(itype) { case fft_array_type_complex_interleaved: case fft_array_type_hermitian_interleaved: switch(precision) { case fft_precision_half: copy_buffers_1to1( reinterpret_cast*>(input[0].data()), reinterpret_cast*>(output[0].data()), length, nbatch, istride, idist, ostride, odist, ioffset, ooffset); break; case fft_precision_single: copy_buffers_1to1(reinterpret_cast*>(input[0].data()), reinterpret_cast*>(output[0].data()), length, nbatch, istride, idist, ostride, odist, ioffset, ooffset); break; case fft_precision_double: copy_buffers_1to1(reinterpret_cast*>(input[0].data()), reinterpret_cast*>(output[0].data()), length, nbatch, istride, idist, ostride, odist, ioffset, ooffset); break; } break; case fft_array_type_real: case fft_array_type_complex_planar: case fft_array_type_hermitian_planar: for(unsigned int idx = 0; idx < input.size(); ++idx) { switch(precision) { case fft_precision_half: copy_buffers_1to1(reinterpret_cast(input[idx].data()), reinterpret_cast<_Float16*>(output[idx].data()), length, nbatch, istride, idist, ostride, odist, ioffset, ooffset); break; case fft_precision_single: copy_buffers_1to1(reinterpret_cast(input[idx].data()), reinterpret_cast(output[idx].data()), length, nbatch, istride, idist, ostride, odist, ioffset, ooffset); break; case fft_precision_double: copy_buffers_1to1(reinterpret_cast(input[idx].data()), reinterpret_cast(output[idx].data()), length, nbatch, istride, idist, ostride, odist, ioffset, ooffset); break; } } break; default: throw std::runtime_error("Invalid data type"); } } else if((itype == fft_array_type_complex_interleaved && otype == fft_array_type_complex_planar) || (itype == fft_array_type_hermitian_interleaved && otype == fft_array_type_hermitian_planar)) { // copy 1to2 switch(precision) { case fft_precision_half: copy_buffers_1to2(reinterpret_cast*>(input[0].data()), reinterpret_cast<_Float16*>(output[0].data()), reinterpret_cast<_Float16*>(output[1].data()), length, nbatch, istride, idist, ostride, odist, ioffset, ooffset); break; case fft_precision_single: copy_buffers_1to2(reinterpret_cast*>(input[0].data()), reinterpret_cast(output[0].data()), reinterpret_cast(output[1].data()), length, nbatch, istride, idist, ostride, odist, ioffset, ooffset); break; case fft_precision_double: copy_buffers_1to2(reinterpret_cast*>(input[0].data()), reinterpret_cast(output[0].data()), reinterpret_cast(output[1].data()), length, nbatch, istride, idist, ostride, odist, ioffset, ooffset); break; } } else if((itype == fft_array_type_complex_planar && otype == fft_array_type_complex_interleaved) || (itype == fft_array_type_hermitian_planar && otype == fft_array_type_hermitian_interleaved)) { // copy 2 to 1 switch(precision) { case fft_precision_half: copy_buffers_2to1(reinterpret_cast(input[0].data()), reinterpret_cast(input[1].data()), reinterpret_cast*>(output[0].data()), length, nbatch, istride, idist, ostride, odist, ioffset, ooffset); break; case fft_precision_single: copy_buffers_2to1(reinterpret_cast(input[0].data()), reinterpret_cast(input[1].data()), reinterpret_cast*>(output[0].data()), length, nbatch, istride, idist, ostride, odist, ioffset, ooffset); break; case fft_precision_double: copy_buffers_2to1(reinterpret_cast(input[0].data()), reinterpret_cast(input[1].data()), reinterpret_cast*>(output[0].data()), length, nbatch, istride, idist, ostride, odist, ioffset, ooffset); break; } } else { throw std::runtime_error("Invalid input and output types."); } } // unroll arbitrary-dimension copy_buffers into specializations for 1-, 2-, 3-dimensions template inline void copy_buffers(const std::vector& input, std::vector& output, const std::vector& length, const size_t nbatch, const fft_precision precision, const fft_array_type itype, const std::vector& istride, const size_t idist, const fft_array_type otype, const std::vector& ostride, const size_t odist, const std::vector& ioffset, const std::vector& ooffset) { switch(length.size()) { case 1: return copy_buffers(input, output, length[0], nbatch, precision, itype, istride[0], idist, otype, ostride[0], odist, ioffset, ooffset); case 2: return copy_buffers(input, output, std::make_tuple(length[0], length[1]), nbatch, precision, itype, std::make_tuple(istride[0], istride[1]), idist, otype, std::make_tuple(ostride[0], ostride[1]), odist, ioffset, ooffset); case 3: return copy_buffers(input, output, std::make_tuple(length[0], length[1], length[2]), nbatch, precision, itype, std::make_tuple(istride[0], istride[1], istride[2]), idist, otype, std::make_tuple(ostride[0], ostride[1], ostride[2]), odist, ioffset, ooffset); default: abort(); } } // Compute the L-infinity and L-2 distance between two buffers with strides istride and // length idist between batches to a buffer with strides ostride and length odist between // batches. Both buffers are of complex type. struct VectorNorms { double l_2 = 0.0, l_inf = 0.0; }; template inline VectorNorms distance_1to1_complex(const Tcomplex* input, const Tcomplex* output, const Tint1& whole_length, const size_t nbatch, const Tint2& istride, const size_t idist, const Tint3& ostride, const size_t odist, std::vector>* linf_failures, const double linf_cutoff, const std::vector& ioffset, const std::vector& ooffset, const double output_scalar = 1.0) { double linf = 0.0; double l2 = 0.0; std::mutex linf_failure_lock; std::vector> linf_failures_private; const bool idx_equals_odx = istride == ostride && idist == odist; size_t idx_base = 0; size_t odx_base = 0; auto partitions = partition_colmajor(whole_length); for(size_t b = 0; b < nbatch; b++, idx_base += idist, odx_base += odist) { #ifdef _OPENMP #pragma omp parallel for reduction(max : linf) reduction(+ : l2) num_threads(partitions.size()) private(linf_failures_private) #endif for(size_t part = 0; part < partitions.size(); ++part) { double cur_linf = 0.0; double cur_l2 = 0.0; auto index = partitions[part].first; const auto length = partitions[part].second; do { const auto idx = compute_index(index, istride, idx_base); const auto odx = idx_equals_odx ? idx : compute_index(index, ostride, odx_base); const double rdiff = std::abs(static_cast(output[odx + ooffset[0]].real()) * output_scalar - static_cast(input[idx + ioffset[0]].real())); cur_linf = std::max(rdiff, cur_linf); if(cur_linf > linf_cutoff) { std::pair fval(b, idx); if(linf_failures) linf_failures_private.push_back(fval); } cur_l2 += rdiff * rdiff; const double idiff = std::abs(static_cast(output[odx + ooffset[0]].imag()) * output_scalar - static_cast(input[idx + ioffset[0]].imag())); cur_linf = std::max(idiff, cur_linf); if(cur_linf > linf_cutoff) { std::pair fval(b, idx); if(linf_failures) linf_failures_private.push_back(fval); } cur_l2 += idiff * idiff; } while(increment_rowmajor(index, length)); linf = std::max(linf, cur_linf); l2 += cur_l2; if(linf_failures) { linf_failure_lock.lock(); std::copy(linf_failures_private.begin(), linf_failures_private.end(), std::back_inserter(*linf_failures)); linf_failure_lock.unlock(); } } } return {.l_2 = sqrt(l2), .l_inf = linf}; } // Compute the L-infinity and L-2 distance between two buffers with strides istride and // length idist between batches to a buffer with strides ostride and length odist between // batches. Both buffers are of real type. template inline VectorNorms distance_1to1_real(const Tfloat* input, const Tfloat* output, const Tint1& whole_length, const size_t nbatch, const Tint2& istride, const size_t idist, const Tint3& ostride, const size_t odist, std::vector>* linf_failures, const double linf_cutoff, const std::vector& ioffset, const std::vector& ooffset, const double output_scalar = 1.0) { double linf = 0.0; double l2 = 0.0; std::mutex linf_failure_lock; std::vector> linf_failures_private; const bool idx_equals_odx = istride == ostride && idist == odist; size_t idx_base = 0; size_t odx_base = 0; auto partitions = partition_rowmajor(whole_length); for(size_t b = 0; b < nbatch; b++, idx_base += idist, odx_base += odist) { #ifdef _OPENMP #pragma omp parallel for reduction(max : linf) reduction(+ : l2) num_threads(partitions.size()) private(linf_failures_private) #endif for(size_t part = 0; part < partitions.size(); ++part) { double cur_linf = 0.0; double cur_l2 = 0.0; auto index = partitions[part].first; const auto length = partitions[part].second; do { const auto idx = compute_index(index, istride, idx_base); const auto odx = idx_equals_odx ? idx : compute_index(index, ostride, odx_base); const double diff = std::abs(static_cast(output[odx + ooffset[0]]) * output_scalar - static_cast(input[idx + ioffset[0]])); cur_linf = std::max(diff, cur_linf); if(cur_linf > linf_cutoff) { std::pair fval(b, idx); if(linf_failures) linf_failures_private.push_back(fval); } cur_l2 += diff * diff; } while(increment_rowmajor(index, length)); linf = std::max(linf, cur_linf); l2 += cur_l2; if(linf_failures) { linf_failure_lock.lock(); std::copy(linf_failures_private.begin(), linf_failures_private.end(), std::back_inserter(*linf_failures)); linf_failure_lock.unlock(); } } } return {.l_2 = sqrt(l2), .l_inf = linf}; } // Compute the L-infinity and L-2 distance between two buffers with strides istride and // length idist between batches to a buffer with strides ostride and length odist between // batches. input is complex-interleaved, output is complex-planar. template inline VectorNorms distance_1to2(const rocfft_complex* input, const Tval* output0, const Tval* output1, const Tint1& whole_length, const size_t nbatch, const T2& istride, const size_t idist, const T3& ostride, const size_t odist, std::vector>* linf_failures, const double linf_cutoff, const std::vector& ioffset, const std::vector& ooffset, const double output_scalar = 1.0) { double linf = 0.0; double l2 = 0.0; std::mutex linf_failure_lock; std::vector> linf_failures_private; const bool idx_equals_odx = istride == ostride && idist == odist; size_t idx_base = 0; size_t odx_base = 0; auto partitions = partition_rowmajor(whole_length); for(size_t b = 0; b < nbatch; b++, idx_base += idist, odx_base += odist) { #ifdef _OPENMP #pragma omp parallel for reduction(max : linf) reduction(+ : l2) num_threads(partitions.size()) private(linf_failures_private) #endif for(size_t part = 0; part < partitions.size(); ++part) { double cur_linf = 0.0; double cur_l2 = 0.0; auto index = partitions[part].first; const auto length = partitions[part].second; do { const auto idx = compute_index(index, istride, idx_base); const auto odx = idx_equals_odx ? idx : compute_index(index, ostride, odx_base); const double rdiff = std::abs(static_cast(output0[odx + ooffset[0]]) * output_scalar - static_cast(input[idx + ioffset[0]].real())); cur_linf = std::max(rdiff, cur_linf); if(cur_linf > linf_cutoff) { std::pair fval(b, idx); if(linf_failures) linf_failures_private.push_back(fval); } cur_l2 += rdiff * rdiff; const double idiff = std::abs(static_cast(output1[odx + ooffset[1]]) * output_scalar - static_cast(input[idx + ioffset[0]].imag())); cur_linf = std::max(idiff, cur_linf); if(cur_linf > linf_cutoff) { std::pair fval(b, idx); if(linf_failures) linf_failures_private.push_back(fval); } cur_l2 += idiff * idiff; } while(increment_rowmajor(index, length)); linf = std::max(linf, cur_linf); l2 += cur_l2; if(linf_failures) { linf_failure_lock.lock(); std::copy(linf_failures_private.begin(), linf_failures_private.end(), std::back_inserter(*linf_failures)); linf_failure_lock.unlock(); } } } return {.l_2 = sqrt(l2), .l_inf = linf}; } // Compute the L-inifnity and L-2 distance between two buffers of dimension length and // with types given by itype, otype, and precision. template inline VectorNorms distance(const std::vector& input, const std::vector& output, const Tint1& length, const size_t nbatch, const fft_precision precision, const fft_array_type itype, const Tint2& istride, const size_t idist, const fft_array_type otype, const Tint3& ostride, const size_t odist, std::vector>* linf_failures, const double linf_cutoff, const std::vector& ioffset, const std::vector& ooffset, const double output_scalar = 1.0) { VectorNorms dist; if(itype == otype) { switch(itype) { case fft_array_type_complex_interleaved: case fft_array_type_hermitian_interleaved: switch(precision) { case fft_precision_half: dist = distance_1to1_complex( reinterpret_cast*>(input[0].data()), reinterpret_cast*>(output[0].data()), length, nbatch, istride, idist, ostride, odist, linf_failures, linf_cutoff, ioffset, ooffset, output_scalar); break; case fft_precision_single: dist = distance_1to1_complex( reinterpret_cast*>(input[0].data()), reinterpret_cast*>(output[0].data()), length, nbatch, istride, idist, ostride, odist, linf_failures, linf_cutoff, ioffset, ooffset, output_scalar); break; case fft_precision_double: dist = distance_1to1_complex( reinterpret_cast*>(input[0].data()), reinterpret_cast*>(output[0].data()), length, nbatch, istride, idist, ostride, odist, linf_failures, linf_cutoff, ioffset, ooffset, output_scalar); break; } dist.l_2 *= dist.l_2; break; case fft_array_type_real: case fft_array_type_complex_planar: case fft_array_type_hermitian_planar: for(unsigned int idx = 0; idx < input.size(); ++idx) { VectorNorms d; switch(precision) { case fft_precision_half: d = distance_1to1_real(reinterpret_cast(input[idx].data()), reinterpret_cast(output[idx].data()), length, nbatch, istride, idist, ostride, odist, linf_failures, linf_cutoff, ioffset, ooffset, output_scalar); break; case fft_precision_single: d = distance_1to1_real(reinterpret_cast(input[idx].data()), reinterpret_cast(output[idx].data()), length, nbatch, istride, idist, ostride, odist, linf_failures, linf_cutoff, ioffset, ooffset, output_scalar); break; case fft_precision_double: d = distance_1to1_real(reinterpret_cast(input[idx].data()), reinterpret_cast(output[idx].data()), length, nbatch, istride, idist, ostride, odist, linf_failures, linf_cutoff, ioffset, ooffset, output_scalar); break; } dist.l_inf = std::max(d.l_inf, dist.l_inf); dist.l_2 += d.l_2 * d.l_2; } break; default: throw std::runtime_error("Invalid input and output types."); } } else if((itype == fft_array_type_complex_interleaved && otype == fft_array_type_complex_planar) || (itype == fft_array_type_hermitian_interleaved && otype == fft_array_type_hermitian_planar)) { switch(precision) { case fft_precision_half: dist = distance_1to2(reinterpret_cast*>(input[0].data()), reinterpret_cast(output[0].data()), reinterpret_cast(output[1].data()), length, nbatch, istride, idist, ostride, odist, linf_failures, linf_cutoff, ioffset, ooffset, output_scalar); break; case fft_precision_single: dist = distance_1to2(reinterpret_cast*>(input[0].data()), reinterpret_cast(output[0].data()), reinterpret_cast(output[1].data()), length, nbatch, istride, idist, ostride, odist, linf_failures, linf_cutoff, ioffset, ooffset, output_scalar); break; case fft_precision_double: dist = distance_1to2(reinterpret_cast*>(input[0].data()), reinterpret_cast(output[0].data()), reinterpret_cast(output[1].data()), length, nbatch, istride, idist, ostride, odist, linf_failures, linf_cutoff, ioffset, ooffset, output_scalar); break; } dist.l_2 *= dist.l_2; } else if((itype == fft_array_type_complex_planar && otype == fft_array_type_complex_interleaved) || (itype == fft_array_type_hermitian_planar && otype == fft_array_type_hermitian_interleaved)) { switch(precision) { case fft_precision_half: dist = distance_1to2(reinterpret_cast*>(output[0].data()), reinterpret_cast(input[0].data()), reinterpret_cast(input[1].data()), length, nbatch, ostride, odist, istride, idist, linf_failures, linf_cutoff, ioffset, ooffset, output_scalar); break; case fft_precision_single: dist = distance_1to2(reinterpret_cast*>(output[0].data()), reinterpret_cast(input[0].data()), reinterpret_cast(input[1].data()), length, nbatch, ostride, odist, istride, idist, linf_failures, linf_cutoff, ioffset, ooffset, output_scalar); break; case fft_precision_double: dist = distance_1to2(reinterpret_cast*>(output[0].data()), reinterpret_cast(input[0].data()), reinterpret_cast(input[1].data()), length, nbatch, ostride, odist, istride, idist, linf_failures, linf_cutoff, ioffset, ooffset, output_scalar); break; } dist.l_2 *= dist.l_2; } else { throw std::runtime_error("Invalid input and output types."); } dist.l_2 = sqrt(dist.l_2); return dist; } // check if the specified length + stride/dist is contiguous template bool is_contiguous_rowmajor(const std::vector& length, const std::vector& stride, size_t dist) { size_t expected_stride = 1; auto stride_it = stride.rbegin(); auto length_it = length.rbegin(); for(; stride_it != stride.rend() && length_it != length.rend(); ++stride_it, ++length_it) { if(*stride_it != expected_stride) return false; expected_stride *= *length_it; } return expected_stride == dist; } // Unroll arbitrary-dimension distance into specializations for 1-, 2-, 3-dimensions template inline VectorNorms distance(const std::vector& input, const std::vector& output, std::vector length, size_t nbatch, const fft_precision precision, const fft_array_type itype, std::vector istride, const size_t idist, const fft_array_type otype, std::vector ostride, const size_t odist, std::vector>* linf_failures, const double linf_cutoff, const std::vector& ioffset, const std::vector& ooffset, const double output_scalar = 1.0) { // If istride and ostride are both contiguous, collapse them down // to one dimension. Index calculation is simpler (and faster) // in the 1D case. if(is_contiguous_rowmajor(length, istride, idist) && is_contiguous_rowmajor(length, ostride, odist)) { length = {product(length.begin(), length.end()) * nbatch}; istride = {static_cast(1)}; ostride = {static_cast(1)}; nbatch = 1; } switch(length.size()) { case 1: return distance(input, output, length[0], nbatch, precision, itype, istride[0], idist, otype, ostride[0], odist, linf_failures, linf_cutoff, ioffset, ooffset, output_scalar); case 2: return distance(input, output, std::make_tuple(length[0], length[1]), nbatch, precision, itype, std::make_tuple(istride[0], istride[1]), idist, otype, std::make_tuple(ostride[0], ostride[1]), odist, linf_failures, linf_cutoff, ioffset, ooffset, output_scalar); case 3: return distance(input, output, std::make_tuple(length[0], length[1], length[2]), nbatch, precision, itype, std::make_tuple(istride[0], istride[1], istride[2]), idist, otype, std::make_tuple(ostride[0], ostride[1], ostride[2]), odist, linf_failures, linf_cutoff, ioffset, ooffset, output_scalar); default: abort(); } } // Compute the L-infinity and L-2 norm of a buffer with strides istride and // length idist. Data is rocfft_complex. template inline VectorNorms norm_complex(const Tcomplex* input, const T1& whole_length, const size_t nbatch, const T2& istride, const size_t idist, const std::vector& offset) { double linf = 0.0; double l2 = 0.0; size_t idx_base = 0; auto partitions = partition_rowmajor(whole_length); for(size_t b = 0; b < nbatch; b++, idx_base += idist) { #ifdef _OPENMP #pragma omp parallel for reduction(max : linf) reduction(+ : l2) num_threads(partitions.size()) #endif for(size_t part = 0; part < partitions.size(); ++part) { double cur_linf = 0.0; double cur_l2 = 0.0; auto index = partitions[part].first; const auto length = partitions[part].second; do { const auto idx = compute_index(index, istride, idx_base); const double rval = std::abs(static_cast(input[idx + offset[0]].real())); cur_linf = std::max(rval, cur_linf); cur_l2 += rval * rval; const double ival = std::abs(static_cast(input[idx + offset[0]].imag())); cur_linf = std::max(ival, cur_linf); cur_l2 += ival * ival; } while(increment_rowmajor(index, length)); linf = std::max(linf, cur_linf); l2 += cur_l2; } } return {.l_2 = sqrt(l2), .l_inf = linf}; } // Compute the L-infinity and L-2 norm of abuffer with strides istride and // length idist. Data is real-valued. template inline VectorNorms norm_real(const Tfloat* input, const T1& whole_length, const size_t nbatch, const T2& istride, const size_t idist, const std::vector& offset) { double linf = 0.0; double l2 = 0.0; size_t idx_base = 0; auto partitions = partition_rowmajor(whole_length); for(size_t b = 0; b < nbatch; b++, idx_base += idist) { #ifdef _OPENMP #pragma omp parallel for reduction(max : linf) reduction(+ : l2) num_threads(partitions.size()) #endif for(size_t part = 0; part < partitions.size(); ++part) { double cur_linf = 0.0; double cur_l2 = 0.0; auto index = partitions[part].first; const auto length = partitions[part].second; do { const auto idx = compute_index(index, istride, idx_base); const double val = std::abs(static_cast(input[idx + offset[0]])); cur_linf = std::max(val, cur_linf); cur_l2 += val * val; } while(increment_rowmajor(index, length)); linf = std::max(linf, cur_linf); l2 += cur_l2; } } return {.l_2 = sqrt(l2), .l_inf = linf}; } // Compute the L-infinity and L-2 norm of abuffer with strides istride and // length idist. Data format is given by precision and itype. template inline VectorNorms norm(const std::vector& input, const T1& length, const size_t nbatch, const fft_precision precision, const fft_array_type itype, const T2& istride, const size_t idist, const std::vector& offset) { VectorNorms norm; switch(itype) { case fft_array_type_complex_interleaved: case fft_array_type_hermitian_interleaved: switch(precision) { case fft_precision_half: norm = norm_complex(reinterpret_cast*>(input[0].data()), length, nbatch, istride, idist, offset); break; case fft_precision_single: norm = norm_complex(reinterpret_cast*>(input[0].data()), length, nbatch, istride, idist, offset); break; case fft_precision_double: norm = norm_complex(reinterpret_cast*>(input[0].data()), length, nbatch, istride, idist, offset); break; } norm.l_2 *= norm.l_2; break; case fft_array_type_real: case fft_array_type_complex_planar: case fft_array_type_hermitian_planar: for(unsigned int idx = 0; idx < input.size(); ++idx) { VectorNorms n; switch(precision) { case fft_precision_half: n = norm_real(reinterpret_cast(input[idx].data()), length, nbatch, istride, idist, offset); break; case fft_precision_single: n = norm_real(reinterpret_cast(input[idx].data()), length, nbatch, istride, idist, offset); break; case fft_precision_double: n = norm_real(reinterpret_cast(input[idx].data()), length, nbatch, istride, idist, offset); break; } norm.l_inf = std::max(n.l_inf, norm.l_inf); norm.l_2 += n.l_2 * n.l_2; } break; default: throw std::runtime_error("Invalid data type"); } norm.l_2 = sqrt(norm.l_2); return norm; } // Unroll arbitrary-dimension norm into specializations for 1-, 2-, 3-dimensions template inline VectorNorms norm(const std::vector& input, std::vector length, size_t nbatch, const fft_precision precision, const fft_array_type type, std::vector stride, const size_t dist, const std::vector& offset) { // If stride is contiguous, collapse it down to one dimension. // Index calculation is simpler (and faster) in the 1D case. if(is_contiguous_rowmajor(length, stride, dist)) { length = {product(length.begin(), length.end()) * nbatch}; stride = {static_cast(1)}; nbatch = 1; } switch(length.size()) { case 1: return norm(input, length[0], nbatch, precision, type, stride[0], dist, offset); case 2: return norm(input, std::make_tuple(length[0], length[1]), nbatch, precision, type, std::make_tuple(stride[0], stride[1]), dist, offset); case 3: return norm(input, std::make_tuple(length[0], length[1], length[2]), nbatch, precision, type, std::make_tuple(stride[0], stride[1], stride[2]), dist, offset); default: abort(); } } // Given a data type and precision, the distance between batches, and // the batch size, allocate the required host buffer(s). static std::vector allocate_host_buffer(const fft_precision precision, const fft_array_type type, const std::vector& size) { std::vector buffers(size.size()); for(unsigned int i = 0; i < size.size(); ++i) { buffers[i].alloc(size[i] * var_size(precision, type)); } return buffers; } // Check if the required buffers fit in the device vram. inline bool vram_fits_problem(const size_t prob_size, const size_t vram_avail, int deviceId = 0) { // We keep a small margin of error for fitting the problem into vram: const size_t extra = 1 << 27; return vram_avail > prob_size + extra; } // Computes the twiddle table VRAM footprint for r2c/c2r transforms. // This function will return 0 for the other transform types, since // the VRAM footprint in rocFFT is negligible for the other cases. inline size_t twiddle_table_vram_footprint(const fft_params& params) { size_t vram_footprint = 0; // Add vram footprint from real/complex even twiddle buffer size. if(params.transform_type == fft_transform_type_real_forward || params.transform_type == fft_transform_type_real_inverse) { const auto realdim = params.length.back(); if(realdim % 2 == 0) { const auto complex_size = params.precision == fft_precision_single ? 8 : 16; // even length twiddle size is 1/4 of the real size, but // in complex elements vram_footprint += realdim * complex_size / 4; } } return vram_footprint; } #endif upstream/shared/environment.h0000664000175000017500000000570714637212246015361 0ustar kaolkaol// Copyright (C) 2021 - 2022 Advanced Micro Devices, Inc. All rights reserved. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. // wrappers around environment variable routines #pragma once #include // Windows provides "getenv" and "_putenv", but those modify the // runtime's copy of the environment. The actual environment in the // process control block is accessed using GetEnvironmentVariable and // SetEnvironmentVariable. #ifdef WIN32 #include static void rocfft_setenv(const char* var, const char* value) { SetEnvironmentVariable(var, value); } static void rocfft_unsetenv(const char* var) { SetEnvironmentVariable(var, nullptr); } static std::string rocfft_getenv(const char* var) { DWORD size = GetEnvironmentVariable(var, nullptr, 0); std::string ret; if(size) { ret.resize(size); GetEnvironmentVariable(var, ret.data(), size); // GetEnvironmentVariable counts the terminating null, so remove it while(!ret.empty() && ret.back() == 0) ret.pop_back(); } return ret; } #else #include static void rocfft_setenv(const char* var, const char* value) { setenv(var, value, 1); } static void rocfft_unsetenv(const char* var) { unsetenv(var); } static std::string rocfft_getenv(const char* var) { auto value = getenv(var); return value ? value : ""; } #endif // RAII object to set an environment variable and restore it to its // previous value on destruction struct EnvironmentSetTemp { EnvironmentSetTemp(const char* _var, const char* val) : var(_var) { auto val_ptr = rocfft_getenv(_var); if(!val_ptr.empty()) oldvalue = val_ptr; rocfft_setenv(_var, val); } ~EnvironmentSetTemp() { if(oldvalue.empty()) rocfft_unsetenv(var.c_str()); else rocfft_setenv(var.c_str(), oldvalue.c_str()); } std::string var; std::string oldvalue; }; upstream/shared/rocfft_hip.h0000664000175000017500000000363014637212246015131 0ustar kaolkaol// Copyright (C) 2023 Advanced Micro Devices, Inc. All rights reserved. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. #ifndef __ROCFFT_HIP_H__ #define __ROCFFT_HIP_H__ #include #include class rocfft_scoped_device { public: rocfft_scoped_device(int device) { if(hipGetDevice(&orig_device) != hipSuccess) throw std::runtime_error("hipGetDevice failure"); if(hipSetDevice(device) != hipSuccess) throw std::runtime_error("hipSetDevice failure"); } ~rocfft_scoped_device() { (void)hipSetDevice(orig_device); } // not copyable or movable rocfft_scoped_device(const rocfft_scoped_device&) = delete; rocfft_scoped_device(rocfft_scoped_device&&) = delete; rocfft_scoped_device& operator=(const rocfft_scoped_device&) = delete; private: int orig_device; }; #endif // __ROCFFT_HIP_H__ upstream/shared/device_properties.h0000664000175000017500000000636614637212246016532 0ustar kaolkaol// Copyright (C) 2023 Advanced Micro Devices, Inc. All rights reserved. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. #ifndef ROCFFT_DEVICE_PROPS_H #define ROCFFT_DEVICE_PROPS_H #include #include #include // get device properties static hipDeviceProp_t get_curr_device_prop() { hipDeviceProp_t prop; int deviceId = 0; if(hipGetDevice(&deviceId) != hipSuccess) throw std::runtime_error("hipGetDevice failed."); if(hipGetDeviceProperties(&prop, deviceId) != hipSuccess) throw std::runtime_error("hipGetDeviceProperties failed for deviceId " + std::to_string(deviceId)); return prop; } // check that the given grid/block dims will fit into the limits in // the device properties. throws std::runtime_error if the limits // are exceeded. static void launch_limits_check(const std::string& kernel_name, const dim3 gridDim, const dim3 blockDim, const hipDeviceProp_t& deviceProp) { // Need lots of casting here because dim3 is unsigned but device // props are signed. Cast direct comparisons to fix signedness // issues. Promote types to 64-bit when multiplying to try to // avoid overflow. // Block limits along each dimension if(blockDim.x > static_cast(deviceProp.maxThreadsDim[0]) || blockDim.y > static_cast(deviceProp.maxThreadsDim[1]) || blockDim.z > static_cast(deviceProp.maxThreadsDim[2])) throw std::runtime_error("max threads per dim exceeded: " + kernel_name); // Total threads for the whole block if(static_cast(blockDim.x) * blockDim.y * blockDim.z > static_cast(deviceProp.maxThreadsPerBlock)) throw std::runtime_error("max threads per block exceeded: " + kernel_name); // Grid dimension limits if(gridDim.x > static_cast(deviceProp.maxGridSize[0]) || gridDim.y > static_cast(deviceProp.maxGridSize[1]) || gridDim.z > static_cast(deviceProp.maxGridSize[2])) throw std::runtime_error("max grid size exceeded: " + kernel_name); } #endif upstream/shared/array_validator.cpp0000664000175000017500000003644114637212246016532 0ustar kaolkaol// Copyright (C) 2022 Advanced Micro Devices, Inc. All rights reserved. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. #include #include #include #include "array_validator.h" #include "increment.h" // Check a 2D array for collisions. // The 2D case can be determined via a number-theoretic argument. bool valid_length_stride_2d(const size_t l0, const size_t l1, const size_t s0, const size_t s1) { if(s0 == s1) return false; const auto c = std::lcm(s0, s1); return !((s0 * (l0 - 1) >= c) && (s1 * (l1 - 1) >= c)); } // Compare a 1D direction with a multi-index hyperface for collisions. bool valid_length_stride_1d_multi(const unsigned int idx, const std::vector l, const std::vector s, const int verbose) { size_t l0{0}, s0{0}; std::vector l1{}, s1{}; for(unsigned int i = 0; i < l.size(); ++i) { if(i == idx) { l0 = l[i]; s0 = s[i]; } else { l1.push_back(l[i]); s1.push_back(s[i]); } } if(verbose > 4) { std::cout << "l0: " << l0 << "\ts0: " << s0 << std::endl; } // We only need to go to the maximum pointer offset for (l1,s1). const auto max_offset = std::accumulate(l1.begin(), l1.end(), (size_t)1, std::multiplies()) - std ::inner_product(l1.begin(), l1.end(), s1.begin(), (size_t)0); std::unordered_set a0{}; for(size_t i = 1; i < l0; ++i) { const auto val = i * s0; if(val <= max_offset) a0.insert(val); else break; } if(verbose > 5) { std::cout << "a0:"; for(auto i : a0) std::cout << " " << i; std::cout << std::endl; std::cout << "l1:"; for(auto i : l1) std::cout << " " << i; std::cout << std::endl; std::cout << "s1:"; for(auto i : s1) std::cout << " " << i; std::cout << std::endl; } // TODO: this can be multi-threaded, since find(...) is thread-safe. std::vector index(l1.size()); std::fill(index.begin(), index.end(), 0); do { const int i = std::inner_product(index.begin(), index.end(), s1.begin(), (size_t)0); if(i > 0 && (i % s0 == 0)) { // TODO: use an ordered set and binary search if(verbose > 6) std::cout << i << std::endl; if(a0.find(i) != a0.end()) { if(verbose > 4) { std::cout << "l0: " << l0 << "\ts0: " << s0 << std::endl; std::cout << "l1:"; for(const auto li : l1) std::cout << " " << li; std::cout << " s1:"; for(const auto si : s1) std::cout << " " << si; std::cout << std::endl; std::cout << "Found duplicate: " << i << std::endl; } return false; } } } while(increment_rowmajor(index, l1)); return true; } // Compare a hyperface with another hyperface for collisions. bool valid_length_stride_multi_multi(const std::vector l0, const std::vector s0, const std::vector l1, const std::vector s1) { std::unordered_set a0{}; const auto max_offset = std::accumulate(l1.begin(), l1.end(), (size_t)1, std::multiplies()) - std::inner_product(l1.begin(), l1.end(), s1.begin(), (size_t)0); std::vector index0(l0.size()); // TODO: check this std::fill(index0.begin(), index0.end(), 0); do { const auto i = std::inner_product(index0.begin(), index0.end(), s0.begin(), (size_t)0); if(i > max_offset) a0.insert(i); } while(increment_rowmajor(index0, l0)); std::vector index1(l1.size()); std::fill(index1.begin(), index1.end(), 0); do { const auto i = std::inner_product(index1.begin(), index1.end(), s1.begin(), (size_t)0); if(i > 0) { // TODO: use an ordered set and binary search if(a0.find(i) != a0.end()) { return false; } } } while(increment_rowmajor(index1, l1)); return true; } bool valid_length_stride_3d(const std::vector& l, const std::vector& s, const int verbose) { // Check that 2D faces are valid: if(!valid_length_stride_2d(l[0], l[1], s[0], s[1])) return false; if(!valid_length_stride_2d(l[0], l[2], s[0], s[2])) return false; if(!valid_length_stride_2d(l[1], l[2], s[1], s[2])) return false; // If the 2D faces are valid, check an axis vs a face for collisions: bool invalid = false; #ifdef _OPENMP #pragma omp parallel for #endif for(int idx = 0; idx < 3; ++idx) { if(!valid_length_stride_1d_multi(idx, l, s, verbose)) { #ifdef _OPENMP #pragma omp cancel for #endif invalid = true; } } if(invalid) return false; return true; } bool valid_length_stride_4d(const std::vector& l, const std::vector& s, const int verbose) { if(l.size() != 4) { throw std::runtime_error("Incorrect dimensions for valid_length_stride_4d"); } // Check that 2D faces are valid: for(int idx0 = 0; idx0 < 3; ++idx0) { for(int idx1 = idx0 + 1; idx1 < 4; ++idx1) { if(!valid_length_stride_2d(l[idx0], l[idx1], s[idx0], s[idx1])) return false; } } bool invalid = false; // Check that 1D vs 3D faces are valid: #ifdef _OPENMP #pragma omp parallel for #endif for(int idx0 = 0; idx0 < 4; ++idx0) { if(!valid_length_stride_1d_multi(idx0, l, s, verbose)) { #ifdef _OPENMP #pragma omp cancel for #endif invalid = true; } } if(invalid) return false; // Check that 2D vs 2D faces are valid: // First, get all the permutations std::vector> perms; std::vector v(l.size()); std::fill(v.begin(), v.begin() + 2, 0); std::fill(v.begin() + 2, v.end(), 1); do { perms.push_back(v); if(verbose > 3) { std::cout << "v:"; for(const auto i : v) { std::cout << " " << i; } std::cout << "\n"; } } while(std::next_permutation(v.begin(), v.end())); // Then loop over all of the permutations. #ifdef _OPENMP #pragma omp parallel for #endif for(size_t iperm = 0; iperm < perms.size(); ++iperm) { std::vector l0(2); std::vector s0(2); std::vector l1(2); std::vector s1(2); for(size_t i = 0; i < l.size(); ++i) { if(perms[iperm][i] == 0) { l0.push_back(l[i]); s0.push_back(s[i]); } else { l1.push_back(l[i]); s1.push_back(s[i]); } } if(verbose > 3) { std::cout << "\tl0:"; for(const auto i : l0) { std::cout << " " << i; } std::cout << "\n"; std::cout << "\ts0:"; for(const auto i : s0) { std::cout << " " << i; } std::cout << "\n"; std::cout << "\tl1:"; for(const auto i : l1) { std::cout << " " << i; } std::cout << "\n"; std::cout << "\ts1:"; for(const auto i : s1) { std::cout << " " << i; } std::cout << "\n"; } if(!valid_length_stride_multi_multi(l0, s0, l1, s1)) { #ifdef _OPENMP #pragma omp cancel for #endif invalid = true; } } if(invalid) return false; return true; } bool valid_length_stride_generald(const std::vector l, const std::vector s, const int verbose) { if(verbose > 2) { std::cout << "checking dimension " << l.size() << std::endl; } // Recurse on d-1 hyper-faces: for(unsigned int idx = 0; idx < l.size(); ++idx) { std::vector l0{}; std::vector s0{}; for(size_t i = 0; i < l.size(); ++i) { if(i != idx) { l0.push_back(l[i]); s0.push_back(s[i]); } } if(!array_valid(l0, s0, verbose)) return false; } // Handle the 1D vs (N-1) case: for(unsigned int idx = 0; idx < l.size(); ++idx) { if(!valid_length_stride_1d_multi(idx, l, s, verbose)) return false; } for(size_t dim0 = 2; dim0 <= l.size() / 2; ++dim0) { const size_t dim1 = l.size() - dim0; if(verbose > 2) std::cout << "dims: " << dim0 << " " << dim1 << std::endl; // We iterate over all permutations of an array of length l.size() which contains dim0 zeros // and dim1 ones. We start with {0, ..., 0, 1, ... 1} to guarantee that we hit all the // possibilities. // First, get all the permutations std::vector> perms; std::vector v(l.size()); std::fill(v.begin(), v.begin() + dim1, 0); std::fill(v.begin() + dim1, v.end(), 1); do { perms.push_back(v); if(verbose > 3) { std::cout << "v:"; for(const auto i : v) { std::cout << " " << i; } std::cout << "\n"; } } while(std::next_permutation(v.begin(), v.end())); bool invalid = false; // Then loop over all of the permutations. #ifdef _OPENMP #pragma omp parallel for #endif for(size_t iperm = 0; iperm < perms.size(); ++iperm) { std::vector l0(dim0); std::vector s0(dim0); std::vector l1(dim1); std::vector s1(dim1); for(size_t i = 0; i < l.size(); ++i) { if(v[i] == 0) { l0.push_back(l[i]); s0.push_back(s[i]); } else { l1.push_back(l[i]); s1.push_back(s[i]); } } if(verbose > 3) { std::cout << "\tl0:"; for(const auto i : l0) { std::cout << " " << i; } std::cout << "\n"; std::cout << "\ts0:"; for(const auto i : s0) { std::cout << " " << i; } std::cout << "\n"; std::cout << "\tl1:"; for(const auto i : l1) { std::cout << " " << i; } std::cout << "\n"; std::cout << "\ts1:"; for(const auto i : s1) { std::cout << " " << i; } std::cout << "\n"; } if(!valid_length_stride_multi_multi(l0, s0, l1, s1)) { #ifdef _OPENMP #pragma omp cancel for #endif invalid = true; } } if(invalid) return false; } return true; } bool sort_by_stride(const std::pair& ls0, const std::pair& ls1) { return ls0.second < ls1.second; } bool array_valid(const std::vector& length, const std::vector& stride, const int verbose) { if(length.size() != stride.size()) return false; // If a length is 1, then the stride is irrelevant. // If a length is > 1, then the corresponding stride must be > 1. std::vector l{}, s{}; for(unsigned int i = 0; i < length.size(); ++i) { if(length[i] > 1) { if(stride[i] == 0) return false; l.push_back(length[i]); s.push_back(stride[i]); } } if(length.size() > 1) { // Check happy path. bool happy_path = true; std::vector> ls; for(size_t idx = 0; idx < length.size(); ++idx) { ls.push_back(std::pair(length[idx], stride[idx])); } std::sort(ls.begin(), ls.end(), sort_by_stride); if(verbose > 2) { for(size_t idx = 0; idx < ls.size(); ++idx) { std::cout << ls[idx].first << "\t" << ls[idx].second << "\n"; } } for(size_t idx = 1; idx < ls.size(); ++idx) { if(ls[idx].second < ls[idx - 1].first * ls[idx - 1].second) { happy_path = false; break; } } if(happy_path) { if(verbose > 2) { std::cout << "happy path\n"; } return true; } } switch(l.size()) { case 0: return true; break; case 1: return s[0] != 0; break; case 2: { return valid_length_stride_2d(l[0], l[1], s[0], s[1]); break; } case 3: { return valid_length_stride_3d(l, s, verbose); break; } case 4: { return valid_length_stride_4d(l, s, verbose); break; } default: return valid_length_stride_generald(l, s, verbose); return true; } return true; } upstream/shared/concurrency.h0000664000175000017500000000324414637212246015341 0ustar kaolkaol// Copyright (C) 2022 Advanced Micro Devices, Inc. All rights reserved. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. #pragma once #include #ifndef WIN32 #include #endif // work out how many parallel tasks to run, based on available // resources. on Linux, this will look at the cpu affinity mask (if // available) which might be restricted in a container. otherwise, // return std::thread::hardware_concurrency(). static unsigned int rocfft_concurrency() { #ifndef WIN32 cpu_set_t cpuset; if(sched_getaffinity(0, sizeof(cpuset), &cpuset) == 0) return CPU_COUNT(&cpuset); #endif return std::thread::hardware_concurrency(); } upstream/shared/data_gen_device.h0000664000175000017500000014426514637212425016100 0ustar kaolkaol// Copyright (C) 2023 Advanced Micro Devices, Inc. All rights reserved. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. #ifndef DATA_GEN_DEVICE_H #define DATA_GEN_DEVICE_H // rocRAND can generate warnings if inline asm is not available for // some architectures. data generation isn't performance-critical, // so just disable inline asm to prevent the warnings. #define ROCRAND_DISABLE_INLINE_ASM #include "../shared/arithmetic.h" #include "../shared/device_properties.h" #include "../shared/gpubuf.h" #include "../shared/increment.h" #include "../shared/rocfft_complex.h" #include #include #include #include #include #include static const unsigned int DATA_GEN_THREADS = 8; static const unsigned int DATA_GEN_GRID_Y_MAX = 64; template struct input_val_1D { T val1; }; template struct input_val_2D { T val1; T val2; }; template struct input_val_3D { T val1; T val2; T val3; }; template static input_val_1D get_input_val(const T& val) { return input_val_1D{val}; } template static input_val_2D get_input_val(const std::tuple& val) { return input_val_2D{std::get<0>(val), std::get<1>(val)}; } template static input_val_3D get_input_val(const std::tuple& val) { return input_val_3D{std::get<0>(val), std::get<1>(val), std::get<2>(val)}; } template __device__ static size_t compute_index(const input_val_1D& length, const input_val_1D& stride, size_t base) { return (length.val1 * stride.val1) + base; } template __device__ static size_t compute_index(const input_val_2D& length, const input_val_2D& stride, size_t base) { return (length.val1 * stride.val1) + (length.val2 * stride.val2) + base; } template __device__ static size_t compute_index(const input_val_3D& length, const input_val_3D& stride, size_t base) { return (length.val1 * stride.val1) + (length.val2 * stride.val2) + (length.val3 * stride.val3) + base; } template static inline input_val_1D make_zero_length(const input_val_1D& whole_length) { return input_val_1D{0}; } template static inline input_val_2D make_zero_length(const input_val_2D& whole_length) { return input_val_2D{0, 0}; } template static inline input_val_3D make_zero_length(const input_val_3D& whole_length) { return input_val_3D{0, 0, 0}; } template static inline input_val_1D make_unit_stride(const input_val_1D& whole_length) { return input_val_1D{1}; } template static inline input_val_2D make_unit_stride(const input_val_2D& whole_length) { return input_val_2D{1, whole_length.val1}; } template static inline input_val_3D make_unit_stride(const input_val_3D& whole_length) { return input_val_3D{1, whole_length.val1, whole_length.val1 * whole_length.val2}; } template __device__ static input_val_1D get_length(const size_t i, const input_val_1D& whole_length) { auto xlen = whole_length.val1; auto xidx = i % xlen; return input_val_1D{xidx}; } template __device__ static input_val_2D get_length(const size_t i, const input_val_2D& whole_length) { auto xlen = whole_length.val1; auto ylen = whole_length.val2; auto xidx = i % xlen; auto yidx = i / xlen % ylen; return input_val_2D{xidx, yidx}; } template __device__ static input_val_3D get_length(const size_t i, const input_val_3D& whole_length) { auto xlen = whole_length.val1; auto ylen = whole_length.val2; auto zlen = whole_length.val3; auto xidx = i % xlen; auto yidx = i / xlen % ylen; auto zidx = i / xlen / ylen % zlen; return input_val_3D{xidx, yidx, zidx}; } template __device__ static size_t get_batch(const size_t i, const input_val_1D& whole_length) { auto xlen = whole_length.val1; auto yidx = i / xlen; return yidx; } template __device__ static size_t get_batch(const size_t i, const input_val_2D& whole_length) { auto xlen = whole_length.val1; auto ylen = whole_length.val2; auto zidx = i / xlen / ylen; return zidx; } template __device__ static size_t get_batch(const size_t i, const input_val_3D& length) { auto xlen = length.val1; auto ylen = length.val2; auto zlen = length.val3; auto widx = i / xlen / ylen / zlen; return widx; } __device__ static double make_random_val(hiprandStatePhilox4_32_10* gen_state, double offset) { return hiprand_uniform_double(gen_state) + offset; } __device__ static float make_random_val(hiprandStatePhilox4_32_10* gen_state, float offset) { return hiprand_uniform(gen_state) + offset; } __device__ static _Float16 make_random_val(hiprandStatePhilox4_32_10* gen_state, _Float16 offset) { return static_cast<_Float16>(hiprand_uniform(gen_state)) + offset; } template __device__ static void set_imag_zero(const size_t pos, Tcomplex* x) { x[pos].y = 0.0; } template __device__ static void set_imag_zero(const size_t pos, Tfloat* xreal, Tfloat* ximag) { ximag[pos] = 0.0; } template __device__ static void conjugate(const size_t pos, const size_t cpos, Tcomplex* x) { x[pos].x = x[cpos].x; x[pos].y = -x[cpos].y; } template __device__ static void conjugate(const size_t pos, const size_t cpos, Tfloat* xreal, Tfloat* ximag) { xreal[pos] = xreal[cpos]; ximag[pos] = -ximag[cpos]; } template __global__ static void __launch_bounds__(DATA_GEN_THREADS) generate_random_interleaved_data_kernel(const Tint whole_length, const Tint zero_length, const size_t idist, const size_t isize, const Tint istride, rocfft_complex* data) { auto const i = static_cast(threadIdx.x) + blockIdx.x * blockDim.x + blockIdx.y * gridDim.x * DATA_GEN_THREADS; static_assert(sizeof(i) >= sizeof(isize)); if(i < isize) { auto i_length = get_length(i, whole_length); auto i_batch = get_batch(i, whole_length); auto i_base = i_batch * idist; auto seed = compute_index(zero_length, istride, i_base); auto idx = compute_index(i_length, istride, i_base); hiprandStatePhilox4_32_10 gen_state; hiprand_init(seed, idx, 0, &gen_state); data[idx].x = make_random_val(&gen_state, static_cast(-0.5)); data[idx].y = make_random_val(&gen_state, static_cast(-0.5)); } } template __global__ static void __launch_bounds__(DATA_GEN_THREADS) generate_interleaved_data_kernel(const Tint whole_length, const size_t idist, const size_t isize, const Tint istride, const Tint ustride, const Treal inv_scale, rocfft_complex* data) { auto const i = static_cast(threadIdx.x) + blockIdx.x * blockDim.x + blockIdx.y * gridDim.x * DATA_GEN_THREADS; static_assert(sizeof(i) >= sizeof(isize)); if(i < isize) { const auto i_length = get_length(i, whole_length); const auto i_batch = get_batch(i, whole_length); const auto i_base = i_batch * idist; const auto val = static_cast(-0.5) + static_cast( static_cast(compute_index(i_length, ustride, 0))) * inv_scale; const auto idx = compute_index(i_length, istride, i_base); data[idx].x = val; data[idx].y = val; } } template __global__ static void __launch_bounds__(DATA_GEN_THREADS) generate_random_planar_data_kernel(const Tint whole_length, const Tint zero_length, const size_t idist, const size_t isize, const Tint istride, Treal* real_data, Treal* imag_data) { auto const i = static_cast(threadIdx.x) + blockIdx.x * blockDim.x + blockIdx.y * gridDim.x * DATA_GEN_THREADS; static_assert(sizeof(i) >= sizeof(isize)); if(i < isize) { auto i_length = get_length(i, whole_length); auto i_batch = get_batch(i, whole_length); auto i_base = i_batch * idist; auto seed = compute_index(zero_length, istride, i_base); auto idx = compute_index(i_length, istride, i_base); hiprandStatePhilox4_32_10 gen_state; hiprand_init(seed, idx, 0, &gen_state); real_data[idx] = make_random_val(&gen_state, static_cast(-0.5)); imag_data[idx] = make_random_val(&gen_state, static_cast(-0.5)); } } template __global__ static void __launch_bounds__(DATA_GEN_THREADS) generate_planar_data_kernel(const Tint whole_length, const size_t idist, const size_t isize, const Tint istride, const Tint ustride, const Treal inv_scale, Treal* real_data, Treal* imag_data) { auto const i = static_cast(threadIdx.x) + blockIdx.x * blockDim.x + blockIdx.y * gridDim.x * DATA_GEN_THREADS; static_assert(sizeof(i) >= sizeof(isize)); if(i < isize) { const auto i_length = get_length(i, whole_length); const auto i_batch = get_batch(i, whole_length); const auto i_base = i_batch * idist; const auto val = static_cast(-0.5) + static_cast( static_cast(compute_index(i_length, ustride, 0))) * inv_scale; const auto idx = compute_index(i_length, istride, i_base); real_data[idx] = val; imag_data[idx] = val; } } template __global__ static void __launch_bounds__(DATA_GEN_THREADS) generate_random_real_data_kernel(const Tint whole_length, const Tint zero_length, const size_t idist, const size_t isize, const Tint istride, Treal* data) { auto const i = static_cast(threadIdx.x) + blockIdx.x * blockDim.x + blockIdx.y * gridDim.x * DATA_GEN_THREADS; static_assert(sizeof(i) >= sizeof(isize)); if(i < isize) { auto i_length = get_length(i, whole_length); auto i_batch = get_batch(i, whole_length); auto i_base = i_batch * idist; auto seed = compute_index(zero_length, istride, i_base); auto idx = compute_index(i_length, istride, i_base); hiprandStatePhilox4_32_10 gen_state; hiprand_init(seed, idx, 0, &gen_state); data[idx] = make_random_val(&gen_state, static_cast(-0.5)); } } template __global__ static void __launch_bounds__(DATA_GEN_THREADS) generate_real_data_kernel(const Tint whole_length, const size_t idist, const size_t isize, const Tint istride, const Tint ustride, const Treal inv_scale, Treal* data) { auto const i = static_cast(threadIdx.x) + blockIdx.x * blockDim.x + blockIdx.y * gridDim.x * DATA_GEN_THREADS; static_assert(sizeof(i) >= sizeof(isize)); if(i < isize) { const auto i_length = get_length(i, whole_length); const auto i_batch = get_batch(i, whole_length); const auto i_base = i_batch * idist; const auto val = static_cast(-0.5) + static_cast( static_cast(compute_index(i_length, ustride, 0))) * inv_scale; const auto idx = compute_index(i_length, istride, i_base); data[idx] = val; } } // For complex-to-real transforms, the input data must be Hermitiam-symmetric. // That is, u_k is the complex conjugate of u_{-k}, where k is the wavevector in Fourier // space. For multi-dimensional data, this means that we only need to store a bit more // than half of the complex values; the rest are redundant. However, there are still // some restrictions: // * the origin and Nyquist value(s) must be real-valued // * some of the remaining values are still redundant, and you might get different results // than you expect if the values don't agree. template __global__ static void impose_hermitian_symmetry_interleaved_1D_kernel(Tcomplex* x, const size_t Nx, const size_t xstride, const size_t dist, const size_t batch_total, const bool Nxeven) { auto id_batch = static_cast(threadIdx.x) + blockIdx.x * blockDim.x; static_assert(sizeof(id_batch) == sizeof(size_t)); if(id_batch < batch_total) { id_batch *= dist; set_imag_zero(id_batch, x); if(Nxeven) set_imag_zero(id_batch + (Nx / 2) * xstride, x); } } template __global__ static void impose_hermitian_symmetry_planar_1D_kernel(Tfloat* xreal, Tfloat* ximag, const size_t Nx, const size_t xstride, const size_t dist, const size_t batch_total, const bool Nxeven) { auto id_batch = static_cast(threadIdx.x) + blockIdx.x * blockDim.x; static_assert(sizeof(id_batch) == sizeof(size_t)); if(id_batch < batch_total) { id_batch *= dist; set_imag_zero(id_batch, xreal, ximag); if(Nxeven) set_imag_zero(id_batch + (Nx / 2) * xstride, xreal, ximag); } } template __global__ static void impose_hermitian_symmetry_interleaved_2D_kernel(Tcomplex* x, const size_t Nx, const size_t Ny, const size_t xstride, const size_t ystride, const size_t dist, const size_t batch_total, const size_t x_total, const bool Nxeven, const bool Nyeven) { auto id_batch = static_cast(threadIdx.x) + blockIdx.x * blockDim.x; const auto id_x = static_cast(threadIdx.y) + blockIdx.y * blockDim.y; static_assert(sizeof(id_batch) == sizeof(size_t)); static_assert(sizeof(id_x) == sizeof(size_t)); if(id_batch < batch_total) { id_batch *= dist; if(id_x == 0) set_imag_zero(id_batch, x); if(id_x == 0 && Nxeven) set_imag_zero(id_batch + (Nx / 2) * xstride, x); if(id_x == 0 && Nyeven) set_imag_zero(id_batch + ystride * (Ny / 2), x); if(id_x == 0 && Nxeven && Nyeven) set_imag_zero(id_batch + xstride * (Nx / 2) + ystride * (Ny / 2), x); if(id_x < x_total) { conjugate(id_batch + xstride * (Nx - (id_x + 1)), id_batch + xstride * (id_x + 1), x); if(Nyeven) conjugate(id_batch + xstride * (Nx - (id_x + 1)) + ystride * (Ny / 2), id_batch + xstride * (id_x + 1) + ystride * (Ny / 2), x); } } } template __global__ static void impose_hermitian_symmetry_planar_2D_kernel(Tfloat* xreal, Tfloat* ximag, const size_t Nx, const size_t Ny, const size_t xstride, const size_t ystride, const size_t dist, const size_t batch_total, const size_t x_total, const bool Nxeven, const bool Nyeven) { auto id_batch = static_cast(threadIdx.x) + blockIdx.x * blockDim.x; const auto id_x = static_cast(threadIdx.y) + blockIdx.y * blockDim.y; static_assert(sizeof(id_batch) == sizeof(size_t)); static_assert(sizeof(id_x) == sizeof(size_t)); if(id_batch < batch_total) { id_batch *= dist; if(id_x == 0) set_imag_zero(id_batch, xreal, ximag); if(id_x == 0 && Nxeven) set_imag_zero(id_batch + (Nx / 2) * xstride, xreal, ximag); if(id_x == 0 && Nyeven) set_imag_zero(id_batch + ystride * (Ny / 2), xreal, ximag); if(id_x == 0 && Nxeven && Nyeven) set_imag_zero(id_batch + xstride * (Nx / 2) + ystride * (Ny / 2), xreal, ximag); if(id_x < x_total) { conjugate(id_batch + xstride * (Nx - (id_x + 1)), id_batch + xstride * (id_x + 1), xreal, ximag); if(Nyeven) conjugate(id_batch + xstride * (Nx - (id_x + 1)) + ystride * (Ny / 2), id_batch + xstride * (id_x + 1) + ystride * (Ny / 2), xreal, ximag); } } } template __global__ static void impose_hermitian_symmetry_interleaved_3D_kernel(Tcomplex* x, const size_t Nx, const size_t Ny, const size_t Nz, const size_t xstride, const size_t ystride, const size_t zstride, const size_t dist, const size_t batch_total, const size_t x_total, const size_t y_total, const size_t y_total_half, const bool Nxeven, const bool Nyeven, const bool Nzeven) { auto id_batch = static_cast(threadIdx.x) + blockIdx.x * blockDim.x; const auto id_x = static_cast(threadIdx.y) + blockIdx.y * blockDim.y; const auto id_y = static_cast(threadIdx.z) + blockIdx.z * blockDim.z; static_assert(sizeof(id_batch) == sizeof(size_t)); static_assert(sizeof(id_x) == sizeof(size_t)); static_assert(sizeof(id_y) == sizeof(size_t)); if(id_batch < batch_total) { auto id_x_y_zero = (id_x == 0 && id_y == 0); id_batch *= dist; if(id_x_y_zero) set_imag_zero(id_batch, x); if(Nxeven && id_x_y_zero) set_imag_zero(id_batch + xstride * (Nx / 2), x); if(Nyeven && id_x_y_zero) set_imag_zero(id_batch + ystride * (Ny / 2), x); if(Nzeven && id_x_y_zero) set_imag_zero(id_batch + zstride * (Nz / 2), x); if(Nxeven && Nyeven && id_x_y_zero) set_imag_zero(id_batch + xstride * (Nx / 2) + ystride * (Ny / 2), x); if(Nxeven && Nzeven && id_x_y_zero) set_imag_zero(id_batch + xstride * (Nx / 2) + zstride * (Nz / 2), x); if(Nyeven && Nzeven && id_x_y_zero) set_imag_zero(id_batch + ystride * (Ny / 2) + zstride * (Nz / 2), x); if(Nxeven && Nyeven && Nzeven && id_x_y_zero) set_imag_zero(id_batch + xstride * (Nx / 2) + ystride * (Ny / 2) + zstride * (Nz / 2), x); if(id_x == 0 && id_y < y_total_half) conjugate(id_batch + ystride * (Ny - (id_y + 1)), id_batch + ystride * (id_y + 1), x); if(Nxeven && id_x == 0 && id_y < y_total_half) conjugate(id_batch + xstride * (Nx / 2) + ystride * (Ny - (id_y + 1)), id_batch + xstride * (Nx / 2) + ystride * (id_y + 1), x); if(id_x < x_total && id_y == 0) conjugate(id_batch + xstride * (Nx - (id_x + 1)), id_batch + xstride * (id_x + 1), x); if(Nyeven && id_x < x_total && id_y == 0) conjugate(id_batch + xstride * (Nx - (id_x + 1)) + ystride * (Ny / 2), id_batch + xstride * (id_x + 1) + ystride * (Ny / 2), x); if(id_x < x_total && id_y < y_total) conjugate(id_batch + xstride * (Nx - (id_x + 1)) + ystride * (Ny - (id_y + 1)), id_batch + xstride * (id_x + 1) + ystride * (id_y + 1), x); if(Nzeven) { if(id_x < x_total && id_y == 0) conjugate(id_batch + xstride * (Nx - (id_x + 1)) + zstride * (Nz / 2), id_batch + xstride * (id_x + 1) + zstride * (Nz / 2), x); if(Nyeven && id_x < x_total && id_y == 0) conjugate(id_batch + xstride * (Nx - (id_x + 1)) + zstride * (Nz / 2), id_batch + xstride * (id_x + 1) + zstride * (Nz / 2), x); if(id_x == 0 && id_y < y_total_half) conjugate(id_batch + ystride * (Ny - (id_y + 1)) + zstride * (Nz / 2), id_batch + ystride * (id_y + 1) + zstride * (Nz / 2), x); if(Nxeven && id_x == 0 && id_y < y_total_half) conjugate(id_batch + xstride * (Nx / 2) + ystride * (Ny - (id_y + 1)) + zstride * (Nz / 2), id_batch + xstride * (Nx / 2) + ystride * (id_y + 1) + zstride * (Nz / 2), x); if(id_x < x_total && id_y < y_total) conjugate(id_batch + xstride * (Nx - (id_x + 1)) + ystride * (Ny - (id_y + 1)) + zstride * (Nz / 2), id_batch + xstride * (id_x + 1) + ystride * (id_y + 1) + zstride * (Nz / 2), x); } } } template __global__ static void impose_hermitian_symmetry_planar_3D_kernel(Tfloat* xreal, Tfloat* ximag, const size_t Nx, const size_t Ny, const size_t Nz, const size_t xstride, const size_t ystride, const size_t zstride, const size_t dist, const size_t batch_total, const size_t x_total, const size_t y_total, const size_t y_total_half, const bool Nxeven, const bool Nyeven, const bool Nzeven) { auto id_batch = static_cast(threadIdx.x) + blockIdx.x * blockDim.x; const auto id_x = static_cast(threadIdx.y) + blockIdx.y * blockDim.y; const auto id_y = static_cast(threadIdx.z) + blockIdx.z * blockDim.z; static_assert(sizeof(id_batch) == sizeof(size_t)); static_assert(sizeof(id_x) == sizeof(size_t)); static_assert(sizeof(id_y) == sizeof(size_t)); if(id_batch < batch_total) { auto id_x_y_zero = (id_x == 0 && id_y == 0); id_batch *= dist; if(id_x_y_zero) set_imag_zero(id_batch, xreal, ximag); if(Nxeven && id_x_y_zero) set_imag_zero(id_batch + xstride * (Nx / 2), xreal, ximag); if(Nyeven && id_x_y_zero) set_imag_zero(id_batch + ystride * (Ny / 2), xreal, ximag); if(Nzeven && id_x_y_zero) set_imag_zero(id_batch + zstride * (Nz / 2), xreal, ximag); if(Nxeven && Nyeven && id_x_y_zero) set_imag_zero(id_batch + xstride * (Nx / 2) + ystride * (Ny / 2), xreal, ximag); if(Nxeven && Nzeven && id_x_y_zero) set_imag_zero(id_batch + xstride * (Nx / 2) + zstride * (Nz / 2), xreal, ximag); if(Nyeven && Nzeven && id_x_y_zero) set_imag_zero(id_batch + ystride * (Ny / 2) + zstride * (Nz / 2), xreal, ximag); if(Nxeven && Nyeven && Nzeven && id_x_y_zero) set_imag_zero(id_batch + xstride * (Nx / 2) + ystride * (Ny / 2) + zstride * (Nz / 2), xreal, ximag); if(id_x == 0 && id_y < y_total_half) conjugate(id_batch + ystride * (Ny - (id_y + 1)), id_batch + ystride * (id_y + 1), xreal, ximag); if(Nxeven && id_x == 0 && id_y < y_total_half) conjugate(id_batch + xstride * (Nx / 2) + ystride * (Ny - (id_y + 1)), id_batch + xstride * (Nx / 2) + ystride * (id_y + 1), xreal, ximag); if(id_x < x_total && id_y == 0) conjugate(id_batch + xstride * (Nx - (id_x + 1)), id_batch + xstride * (id_x + 1), xreal, ximag); if(Nyeven && id_x < x_total && id_y == 0) conjugate(id_batch + xstride * (Nx - (id_x + 1)) + ystride * (Ny / 2), id_batch + xstride * (id_x + 1) + ystride * (Ny / 2), xreal, ximag); if(id_x < x_total && id_y < y_total) conjugate(id_batch + xstride * (Nx - (id_x + 1)) + ystride * (Ny - (id_y + 1)), id_batch + xstride * (id_x + 1) + ystride * (id_y + 1), xreal, ximag); if(Nzeven) { if(id_x < x_total && id_y == 0) conjugate(id_batch + xstride * (Nx - (id_x + 1)) + zstride * (Nz / 2), id_batch + xstride * (id_x + 1) + zstride * (Nz / 2), xreal, ximag); if(Nyeven && id_x < x_total && id_y == 0) conjugate(id_batch + xstride * (Nx - (id_x + 1)) + zstride * (Nz / 2), id_batch + xstride * (id_x + 1) + zstride * (Nz / 2), xreal, ximag); if(id_x == 0 && id_y < y_total_half) conjugate(id_batch + ystride * (Ny - (id_y + 1)) + zstride * (Nz / 2), id_batch + ystride * (id_y + 1) + zstride * (Nz / 2), xreal, ximag); if(Nxeven && id_x == 0 && id_y < y_total_half) conjugate(id_batch + xstride * (Nx / 2) + ystride * (Ny - (id_y + 1)) + zstride * (Nz / 2), id_batch + xstride * (Nx / 2) + ystride * (id_y + 1) + zstride * (Nz / 2), xreal, ximag); if(id_x < x_total && id_y < y_total) conjugate(id_batch + xstride * (Nx - (id_x + 1)) + ystride * (Ny - (id_y + 1)) + zstride * (Nz / 2), id_batch + xstride * (id_x + 1) + ystride * (id_y + 1) + zstride * (Nz / 2), xreal, ximag); } } } // get grid dimensions for data gen kernel static dim3 generate_data_gridDim(const size_t isize) { auto blockSize = DATA_GEN_THREADS; // total number of blocks needed in the grid auto numBlocks_setup = DivRoundingUp(isize, blockSize); // Total work items per dimension in the grid is counted in // uint32_t. Since each thread initializes one element, very // large amounts of data will overflow this total size if we do // all this work in one grid dimension, causing launch failure. // // CUDA also generally allows for effectively unlimited grid X // dim, but Y and Z are more limited. auto gridDim_y = std::min(DATA_GEN_GRID_Y_MAX, numBlocks_setup); auto gridDim_x = DivRoundingUp(numBlocks_setup, DATA_GEN_GRID_Y_MAX); return {gridDim_x, gridDim_y}; } // get grid dimensions for hermitian symmetrizer kernel static dim3 generate_hermitian_gridDim(const std::vector& length, const size_t batch, const size_t blockSize) { dim3 gridDim; switch(length.size()) { case 1: gridDim = dim3(DivRoundingUp(batch, blockSize)); break; case 2: gridDim = dim3(DivRoundingUp(batch, blockSize), DivRoundingUp((length[0] + 1) / 2 - 1, blockSize)); break; case 3: gridDim = dim3(DivRoundingUp(batch, blockSize), DivRoundingUp((length[0] + 1) / 2 - 1, blockSize), DivRoundingUp(length[1] - 1, blockSize)); break; default: throw std::runtime_error("Invalid dimension for impose_hermitian_symmetry"); } return gridDim; } static dim3 generate_blockDim(const std::vector& length, const size_t blockSize) { dim3 blockDim; switch(length.size()) { case 1: blockDim = dim3(blockSize); break; case 2: blockDim = dim3(blockSize, blockSize); break; case 3: blockDim = dim3(blockSize, blockSize, blockSize); break; default: throw std::runtime_error("Invalid dimension for impose_hermitian_symmetry"); } return blockDim; } template static void generate_random_interleaved_data(const Tint& whole_length, const size_t idist, const size_t isize, const Tint& whole_stride, rocfft_complex* input_data, const hipDeviceProp_t& deviceProp) { auto input_length = get_input_val(whole_length); auto zero_length = make_zero_length(input_length); auto input_stride = get_input_val(whole_stride); dim3 gridDim = generate_data_gridDim(isize); dim3 blockDim{DATA_GEN_THREADS}; launch_limits_check("generate_random_interleaved_data_kernel", gridDim, blockDim, deviceProp); hipLaunchKernelGGL( HIP_KERNEL_NAME(generate_random_interleaved_data_kernel), gridDim, blockDim, 0, // sharedMemBytes 0, // stream input_length, zero_length, idist, isize, input_stride, input_data); auto err = hipGetLastError(); if(err != hipSuccess) throw std::runtime_error("generate_random_interleaved_data_kernel launch failure: " + std::string(hipGetErrorName(err))); } template static void generate_interleaved_data(const Tint& whole_length, const size_t idist, const size_t isize, const Tint& whole_stride, const size_t nbatch, rocfft_complex* input_data, const hipDeviceProp_t& deviceProp) { const auto input_length = get_input_val(whole_length); const auto input_stride = get_input_val(whole_stride); const auto unit_stride = make_unit_stride(input_length); const auto inv_scale = static_cast(1.0) / static_cast(static_cast(isize) / nbatch - 1); dim3 gridDim = generate_data_gridDim(isize); dim3 blockDim{DATA_GEN_THREADS}; launch_limits_check("generate_interleaved_data_kernel", gridDim, blockDim, deviceProp); hipLaunchKernelGGL( HIP_KERNEL_NAME(generate_interleaved_data_kernel), gridDim, blockDim, 0, // sharedMemBytes 0, // stream input_length, idist, isize, input_stride, unit_stride, inv_scale, input_data); auto err = hipGetLastError(); if(err != hipSuccess) throw std::runtime_error("generate_interleaved_data_kernel launch failure: " + std::string(hipGetErrorName(err))); } template static void generate_random_planar_data(const Tint& whole_length, const size_t idist, const size_t isize, const Tint& whole_stride, Treal* real_data, Treal* imag_data, const hipDeviceProp_t& deviceProp) { const auto input_length = get_input_val(whole_length); const auto zero_length = make_zero_length(input_length); const auto input_stride = get_input_val(whole_stride); dim3 gridDim = generate_data_gridDim(isize); dim3 blockDim{DATA_GEN_THREADS}; launch_limits_check("generate_random_planar_data_kernel", gridDim, blockDim, deviceProp); hipLaunchKernelGGL( HIP_KERNEL_NAME(generate_random_planar_data_kernel), gridDim, blockDim, 0, // sharedMemBytes 0, // stream input_length, zero_length, idist, isize, input_stride, real_data, imag_data); auto err = hipGetLastError(); if(err != hipSuccess) throw std::runtime_error("generate_random_planar_data_kernel launch failure: " + std::string(hipGetErrorName(err))); } template static void generate_planar_data(const Tint& whole_length, const size_t idist, const size_t isize, const Tint& whole_stride, const size_t nbatch, Treal* real_data, Treal* imag_data, const hipDeviceProp_t& deviceProp) { const auto input_length = get_input_val(whole_length); const auto input_stride = get_input_val(whole_stride); const auto unit_stride = make_unit_stride(input_length); const auto inv_scale = static_cast(1.0) / static_cast(static_cast(isize) / nbatch - 1); dim3 gridDim = generate_data_gridDim(isize); dim3 blockDim{DATA_GEN_THREADS}; launch_limits_check("generate_planar_data_kernel", gridDim, blockDim, deviceProp); hipLaunchKernelGGL(HIP_KERNEL_NAME(generate_planar_data_kernel), gridDim, blockDim, 0, // sharedMemBytes 0, // stream input_length, idist, isize, input_stride, unit_stride, inv_scale, real_data, imag_data); auto err = hipGetLastError(); if(err != hipSuccess) throw std::runtime_error("generate_planar_data_kernel launch failure: " + std::string(hipGetErrorName(err))); } template static void generate_random_real_data(const Tint& whole_length, const size_t idist, const size_t isize, const Tint& whole_stride, Treal* input_data, const hipDeviceProp_t& deviceProp) { const auto input_length = get_input_val(whole_length); const auto zero_length = make_zero_length(input_length); const auto input_stride = get_input_val(whole_stride); dim3 gridDim = generate_data_gridDim(isize); dim3 blockDim{DATA_GEN_THREADS}; launch_limits_check("generate_random_real_data_kernel", gridDim, blockDim, deviceProp); hipLaunchKernelGGL( HIP_KERNEL_NAME(generate_random_real_data_kernel), gridDim, blockDim, 0, // sharedMemBytes 0, // stream input_length, zero_length, idist, isize, input_stride, input_data); auto err = hipGetLastError(); if(err != hipSuccess) throw std::runtime_error("generate_random_real_data_kernel launch failure: " + std::string(hipGetErrorName(err))); } template static void generate_real_data(const Tint& whole_length, const size_t idist, const size_t isize, const Tint& whole_stride, const size_t nbatch, Treal* input_data, const hipDeviceProp_t& deviceProp) { const auto input_length = get_input_val(whole_length); const auto input_stride = get_input_val(whole_stride); const auto unit_stride = make_unit_stride(input_length); const auto inv_scale = static_cast(1.0) / static_cast(static_cast(isize) / nbatch - 1); dim3 gridDim = generate_data_gridDim(isize); dim3 blockDim{DATA_GEN_THREADS}; launch_limits_check("generate_real_data_kernel", gridDim, blockDim, deviceProp); hipLaunchKernelGGL(HIP_KERNEL_NAME(generate_real_data_kernel), gridDim, blockDim, 0, // sharedMemBytes 0, // stream input_length, idist, isize, input_stride, unit_stride, inv_scale, input_data); auto err = hipGetLastError(); if(err != hipSuccess) throw std::runtime_error("generate_real_data_kernel launch failure: " + std::string(hipGetErrorName(err))); } template static void impose_hermitian_symmetry_interleaved(const std::vector& length, const std::vector& ilength, const std::vector& stride, const size_t dist, const size_t batch, Tcomplex* input_data, const hipDeviceProp_t& deviceProp) { auto blockSize = DATA_GEN_THREADS; auto blockDim = generate_blockDim(length, blockSize); auto gridDim = generate_hermitian_gridDim(length, batch, blockSize); switch(length.size()) { case 1: { launch_limits_check( "impose_hermitian_symmetry_interleaved_1D_kernel", gridDim, blockDim, deviceProp); hipLaunchKernelGGL(impose_hermitian_symmetry_interleaved_1D_kernel, gridDim, blockDim, 0, 0, input_data, length[0], stride[0], dist, batch, length[0] % 2 == 0); break; } case 2: { launch_limits_check( "impose_hermitian_symmetry_interleaved_2D_kernel", gridDim, blockDim, deviceProp); hipLaunchKernelGGL(impose_hermitian_symmetry_interleaved_2D_kernel, gridDim, blockDim, 0, 0, input_data, length[0], length[1], stride[0], stride[1], dist, batch, (ilength[0] + 1) / 2 - 1, length[0] % 2 == 0, length[1] % 2 == 0); break; } case 3: { launch_limits_check( "impose_hermitian_symmetry_interleaved_3D_kernel", gridDim, blockDim, deviceProp); hipLaunchKernelGGL(impose_hermitian_symmetry_interleaved_3D_kernel, gridDim, blockDim, 0, 0, input_data, length[0], length[1], length[2], stride[0], stride[1], stride[2], dist, batch, (ilength[0] + 1) / 2 - 1, ilength[1] - 1, (ilength[1] + 1) / 2 - 1, length[0] % 2 == 0, length[1] % 2 == 0, length[2] % 2 == 0); break; } default: throw std::runtime_error("Invalid dimension for impose_hermitian_symmetry"); } auto err = hipGetLastError(); if(err != hipSuccess) throw std::runtime_error("impose_hermitian_symmetry_interleaved_kernel launch failure: " + std::string(hipGetErrorName(err))); } template static void impose_hermitian_symmetry_planar(const std::vector& length, const std::vector& ilength, const std::vector& stride, const size_t dist, const size_t batch, Tfloat* input_data_real, Tfloat* input_data_imag, const hipDeviceProp_t& deviceProp) { auto blockSize = DATA_GEN_THREADS; auto blockDim = generate_blockDim(length, blockSize); auto gridDim = generate_hermitian_gridDim(length, batch, blockSize); switch(length.size()) { case 1: { launch_limits_check( "impose_hermitian_symmetry_planar_1D_kernel", gridDim, blockDim, deviceProp); hipLaunchKernelGGL(impose_hermitian_symmetry_planar_1D_kernel, gridDim, blockDim, 0, 0, input_data_real, input_data_imag, length[0], stride[0], dist, batch, length[0] % 2 == 0); break; } case 2: { launch_limits_check( "impose_hermitian_symmetry_planar_2D_kernel", gridDim, blockDim, deviceProp); hipLaunchKernelGGL(impose_hermitian_symmetry_planar_2D_kernel, gridDim, blockDim, 0, 0, input_data_real, input_data_imag, length[0], length[1], stride[0], stride[1], dist, batch, (ilength[0] + 1) / 2 - 1, length[0] % 2 == 0, length[1] % 2 == 0); break; } case 3: { launch_limits_check( "impose_hermitian_symmetry_planar_3D_kernel", gridDim, blockDim, deviceProp); hipLaunchKernelGGL(impose_hermitian_symmetry_planar_3D_kernel, gridDim, blockDim, 0, 0, input_data_real, input_data_imag, length[0], length[1], length[2], stride[0], stride[1], stride[2], dist, batch, (ilength[0] + 1) / 2 - 1, ilength[1] - 1, (ilength[1] + 1) / 2 - 1, length[0] % 2 == 0, length[1] % 2 == 0, length[2] % 2 == 0); break; } default: throw std::runtime_error("Invalid dimension for impose_hermitian_symmetry"); } auto err = hipGetLastError(); if(err != hipSuccess) throw std::runtime_error("impose_hermitian_symmetry_planar_kernel launch failure: " + std::string(hipGetErrorName(err))); } #endif // DATA_GEN_DEVICE_H upstream/shared/array_validator.h0000664000175000017500000000270414637212246016172 0ustar kaolkaol// Copyright (C) 2022 Advanced Micro Devices, Inc. All rights reserved. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. #ifndef ARRAY_VALIDATOR_H #define ARRAY_VALIDATOR_H #include // Checks whether the array with given length and stride has multi-index collisions. bool array_valid(const std::vector& length, const std::vector& stride, const int verbose = 0); #endif upstream/shared/data_gen_host.h0000664000175000017500000010370214637212425015605 0ustar kaolkaol// Copyright (C) 2023 Advanced Micro Devices, Inc. All rights reserved. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. #ifndef DATA_GEN_HOST_H #define DATA_GEN_HOST_H #include "../shared/hostbuf.h" #include "../shared/increment.h" #include #include #include #include #include // Specialized computation of index given 1-, 2-, 3- dimension length + stride template size_t compute_index(T1 length, T2 stride, size_t base) { return (length * stride) + base; } template size_t compute_index(const std::tuple& length, const std::tuple& stride, size_t base) { static_assert(std::is_integral::value, "Integral required."); static_assert(std::is_integral::value, "Integral required."); return (std::get<0>(length) * std::get<0>(stride)) + (std::get<1>(length) * std::get<1>(stride)) + base; } template size_t compute_index(const std::tuple& length, const std::tuple& stride, size_t base) { static_assert(std::is_integral::value, "Integral required."); static_assert(std::is_integral::value, "Integral required."); return (std::get<0>(length) * std::get<0>(stride)) + (std::get<1>(length) * std::get<1>(stride)) + (std::get<2>(length) * std::get<2>(stride)) + base; } // count the number of total iterations for 1-, 2-, and 3-D dimensions template size_t count_iters(const T1& i) { return i; } template size_t count_iters(const std::tuple& i) { return std::get<0>(i) * std::get<1>(i); } template size_t count_iters(const std::tuple& i) { return std::get<0>(i) * std::get<1>(i) * std::get<2>(i); } template T1 make_unit_stride(const T1& whole_length) { return static_cast(1); } template std::tuple make_unit_stride(const std::tuple& whole_length) { return std::make_tuple(static_cast(1), static_cast(std::get<0>(whole_length))); } template std::tuple make_unit_stride(const std::tuple& whole_length) { return std::make_tuple(static_cast(1), static_cast(std::get<0>(whole_length)), static_cast(std::get<0>(whole_length)) * static_cast(std::get<1>(whole_length))); } // Work out how many partitions to break our iteration problem into template static size_t compute_partition_count(T1 length) { #ifdef _OPENMP // we seem to get contention from too many threads, which slows // things down. particularly noticeable with mix_3D tests static const size_t MAX_PARTITIONS = 8; size_t iters = count_iters(length); size_t hw_threads = std::min(MAX_PARTITIONS, static_cast(omp_get_num_procs())); if(!hw_threads) return 1; // don't bother threading problem sizes that are too small. pick // an arbitrary number of iterations and ensure that each thread // has at least that many iterations to process static const size_t MIN_ITERS_PER_THREAD = 2048; // either use the whole CPU, or use ceil(iters/iters_per_thread) return std::min(hw_threads, (iters + MIN_ITERS_PER_THREAD + 1) / MIN_ITERS_PER_THREAD); #else return 1; #endif } // Break a scalar length into some number of pieces, returning // [(start0, end0), (start1, end1), ...] template std::vector> partition_base(const T1& length, size_t num_parts) { static_assert(std::is_integral::value, "Integral required."); // make sure we don't exceed the length num_parts = std::min(length, num_parts); std::vector> ret(num_parts); auto partition_size = length / num_parts; T1 cur_partition = 0; for(size_t i = 0; i < num_parts; ++i, cur_partition += partition_size) { ret[i].first = cur_partition; ret[i].second = cur_partition + partition_size; } // last partition might not divide evenly, fix it up ret.back().second = length; return ret; } // Returns pairs of startindex, endindex, for 1D, 2D, 3D lengths template std::vector> partition_rowmajor(const T1& length) { return partition_base(length, compute_partition_count(length)); } // Partition on the leftmost part of the tuple, for row-major indexing template std::vector, std::tuple>> partition_rowmajor(const std::tuple& length) { auto partitions = partition_base(std::get<0>(length), compute_partition_count(length)); std::vector, std::tuple>> ret(partitions.size()); for(size_t i = 0; i < partitions.size(); ++i) { std::get<0>(ret[i].first) = partitions[i].first; std::get<1>(ret[i].first) = 0; std::get<0>(ret[i].second) = partitions[i].second; std::get<1>(ret[i].second) = std::get<1>(length); } return ret; } template std::vector, std::tuple>> partition_rowmajor(const std::tuple& length) { auto partitions = partition_base(std::get<0>(length), compute_partition_count(length)); std::vector, std::tuple>> ret(partitions.size()); for(size_t i = 0; i < partitions.size(); ++i) { std::get<0>(ret[i].first) = partitions[i].first; std::get<1>(ret[i].first) = 0; std::get<2>(ret[i].first) = 0; std::get<0>(ret[i].second) = partitions[i].second; std::get<1>(ret[i].second) = std::get<1>(length); std::get<2>(ret[i].second) = std::get<2>(length); } return ret; } // For complex-to-real transforms, the input data must be Hermitiam-symmetric. // That is, u_k is the complex conjugate of u_{-k}, where k is the wavevector in Fourier // space. For multi-dimensional data, this means that we only need to store a bit more // than half of the complex values; the rest are redundant. However, there are still // some restrictions: // * the origin and Nyquist value(s) must be real-valued // * some of the remaining values are still redundant, and you might get different results // than you expect if the values don't agree. // Below are some example kernels which impose Hermitian symmetry on a complex array // of the given dimensions. template static void impose_hermitian_symmetry_interleaved_1D(std::vector& vals, const std::vector& length, const std::vector& istride, const Tsize idist, const Tsize nbatch) { for(unsigned int ibatch = 0; ibatch < nbatch; ++ibatch) { auto data = ((std::complex*)vals[0].data()) + ibatch * idist; data[0].imag(0.0); if(length[0] % 2 == 0) { data[istride[0] * (length[0] / 2)].imag(0.0); } } } template static void impose_hermitian_symmetry_planar_1D(std::vector& vals, const std::vector& length, const std::vector& istride, const Tsize idist, const Tsize nbatch) { for(unsigned int ibatch = 0; ibatch < nbatch; ++ibatch) { auto data_imag = ((Tfloat*)vals[1].data()) + ibatch * idist; data_imag[0] = 0.0; if(length[0] % 2 == 0) { data_imag[istride[0] * (length[0] / 2)] = 0.0; } } } template static void impose_hermitian_symmetry_interleaved_2D(std::vector& vals, const std::vector& length, const std::vector& istride, const Tsize idist, const Tsize nbatch) { for(unsigned int ibatch = 0; ibatch < nbatch; ++ibatch) { auto data = ((std::complex*)vals[0].data()) + ibatch * idist; data[0].imag(0.0); if(length[0] % 2 == 0) { data[istride[0] * (length[0] / 2)].imag(0.0); } if(length[1] % 2 == 0) { data[istride[1] * (length[1] / 2)].imag(0.0); } if(length[0] % 2 == 0 && length[1] % 2 == 0) { data[istride[0] * (length[0] / 2) + istride[1] * (length[1] / 2)].imag(0.0); } for(unsigned int i = 1; i < (length[0] + 1) / 2; ++i) { data[istride[0] * (length[0] - i)] = std::conj(data[istride[0] * i]); } if(length[1] % 2 == 0) { for(unsigned int i = 1; i < (length[0] + 1) / 2; ++i) { data[istride[0] * (length[0] - i) + istride[1] * (length[1] / 2)] = std::conj(data[istride[0] * i + istride[1] * (length[1] / 2)]); } } } } template static void impose_hermitian_symmetry_planar_2D(std::vector& vals, const std::vector& length, const std::vector& istride, const Tsize idist, const Tsize nbatch) { for(unsigned int ibatch = 0; ibatch < nbatch; ++ibatch) { auto data_real = ((Tfloat*)vals[0].data()) + ibatch * idist; auto data_imag = ((Tfloat*)vals[1].data()) + ibatch * idist; data_imag[0] = 0.0; if(length[0] % 2 == 0) { data_imag[istride[0] * (length[0] / 2)] = 0.0; } if(length[1] % 2 == 0) { data_imag[istride[1] * (length[1] / 2)] = 0.0; } if(length[0] % 2 == 0 && length[1] % 2 == 0) { data_imag[istride[0] * (length[0] / 2) + istride[1] * (length[1] / 2)] = 0.0; } for(unsigned int i = 1; i < (length[0] + 1) / 2; ++i) { data_real[istride[0] * (length[0] - i)] = data_real[istride[0] * i]; data_imag[istride[0] * (length[0] - i)] = -data_imag[istride[0] * i]; } if(length[1] % 2 == 0) { for(unsigned int i = 1; i < (length[0] + 1) / 2; ++i) { data_real[istride[0] * (length[0] - i) + istride[1] * (length[1] / 2)] = data_real[istride[0] * i + istride[1] * (length[1] / 2)]; data_imag[istride[0] * (length[0] - i) + istride[1] * (length[1] / 2)] = -data_imag[istride[0] * i + istride[1] * (length[1] / 2)]; } } } } template static void impose_hermitian_symmetry_interleaved_3D(std::vector& vals, const std::vector& length, const std::vector& istride, const Tsize idist, const Tsize nbatch) { for(unsigned int ibatch = 0; ibatch < nbatch; ++ibatch) { auto data = ((std::complex*)vals[0].data()) + ibatch * idist; data[0].imag(0.0); if(length[0] % 2 == 0) { data[istride[0] * (length[0] / 2)].imag(0.0); } if(length[1] % 2 == 0) { data[istride[1] * (length[1] / 2)].imag(0.0); } if(length[2] % 2 == 0) { data[istride[2] * (length[2] / 2)].imag(0.0); } if(length[0] % 2 == 0 && length[1] % 2 == 0) { data[istride[0] * (length[0] / 2) + istride[1] * (length[1] / 2)].imag(0.0); } if(length[0] % 2 == 0 && length[2] % 2 == 0) { data[istride[0] * (length[0] / 2) + istride[2] * (length[2] / 2)].imag(0.0); } if(length[1] % 2 == 0 && length[2] % 2 == 0) { data[istride[1] * (length[1] / 2) + istride[2] * (length[2] / 2)].imag(0.0); } if(length[0] % 2 == 0 && length[1] % 2 == 0 && length[2] % 2 == 0) { data[istride[0] * (length[0] / 2) + istride[1] * (length[1] / 2) + istride[2] * (length[2] / 2)] .imag(0.0); } // y-axis: for(unsigned int j = 1; j < (length[1] + 1) / 2; ++j) { data[istride[1] * (length[1] - j)] = std::conj(data[istride[1] * j]); } if(length[0] % 2 == 0) { // y-axis at x-nyquist for(unsigned int j = 1; j < (length[1] + 1) / 2; ++j) { data[istride[0] * (length[0] / 2) + istride[1] * (length[1] - j)] = std::conj(data[istride[0] * (length[0] / 2) + istride[1] * j]); } } // x-axis: for(unsigned int i = 1; i < (length[0] + 1) / 2; ++i) { data[istride[0] * (length[0] - i)] = std::conj(data[istride[0] * i]); } if(length[1] % 2 == 0) { // x-axis at y-nyquist for(unsigned int i = 1; i < (length[0] + 1) / 2; ++i) { data[istride[0] * (length[0] - i) + istride[1] * (length[1] / 2)] = std::conj(data[istride[0] * i + istride[1] * (length[1] / 2)]); } } // x-y plane: for(unsigned int i = 1; i < (length[0] + 1) / 2; ++i) { for(unsigned int j = 1; j < length[1]; ++j) { data[istride[0] * (length[0] - i) + istride[1] * (length[1] - j)] = std::conj(data[istride[0] * i + istride[1] * j]); } } if(length[2] % 2 == 0) { // x-axis at z-nyquist for(unsigned int i = 1; i < (length[0] + 1) / 2; ++i) { data[istride[0] * (length[0] - i) + istride[2] * (length[2] / 2)] = std::conj(data[istride[0] * i + istride[2] * (length[2] / 2)]); } if(length[1] % 2 == 0) { // x-axis at yz-nyquist for(unsigned int i = 1; i < (length[0] + 1) / 2; ++i) { data[istride[0] * (length[0] - i) + istride[2] * (length[2] / 2)] = std::conj(data[istride[0] * i + istride[2] * (length[2] / 2)]); } } // y-axis: at z-nyquist for(unsigned int j = 1; j < (length[1] + 1) / 2; ++j) { data[istride[1] * (length[1] - j) + istride[2] * (length[2] / 2)] = std::conj(data[istride[1] * j + istride[2] * (length[2] / 2)]); } if(length[0] % 2 == 0) { // y-axis: at xz-nyquist for(unsigned int j = 1; j < (length[1] + 1) / 2; ++j) { data[istride[0] * (length[0] / 2) + istride[1] * (length[1] - j) + istride[2] * (length[2] / 2)] = std::conj(data[istride[0] * (length[0] / 2) + istride[1] * j + istride[2] * (length[2] / 2)]); } } // x-y plane: at z-nyquist for(unsigned int i = 1; i < (length[0] + 1) / 2; ++i) { for(unsigned int j = 1; j < length[1]; ++j) { data[istride[0] * (length[0] - i) + istride[1] * (length[1] - j) + istride[2] * (length[2] / 2)] = std::conj( data[istride[0] * i + istride[1] * j + istride[2] * (length[2] / 2)]); } } } } } template static void impose_hermitian_symmetry_planar_3D(std::vector& vals, const std::vector& length, const std::vector& istride, const Tsize idist, const Tsize nbatch) { for(unsigned int ibatch = 0; ibatch < nbatch; ++ibatch) { auto data_real = ((Tfloat*)vals[0].data()) + ibatch * idist; auto data_imag = ((Tfloat*)vals[1].data()) + ibatch * idist; data_imag[0] = 0.0; if(length[0] % 2 == 0) { data_imag[istride[0] * (length[0] / 2)] = 0.0; } if(length[1] % 2 == 0) { data_imag[istride[1] * (length[1] / 2)] = 0.0; } if(length[2] % 2 == 0) { data_imag[istride[2] * (length[2] / 2)] = 0.0; } if(length[0] % 2 == 0 && length[1] % 2 == 0) { data_imag[istride[0] * (length[0] / 2) + istride[1] * (length[1] / 2)] = 0.0; } if(length[0] % 2 == 0 && length[2] % 2 == 0) { data_imag[istride[0] * (length[0] / 2) + istride[2] * (length[2] / 2)] = 0.0; } if(length[1] % 2 == 0 && length[2] % 2 == 0) { data_imag[istride[1] * (length[1] / 2) + istride[2] * (length[2] / 2)] = 0.0; } if(length[0] % 2 == 0 && length[1] % 2 == 0 && length[2] % 2 == 0) { data_imag[istride[0] * (length[0] / 2) + istride[1] * (length[1] / 2) + istride[2] * (length[2] / 2)] = 0.0; } // y-axis: for(unsigned int j = 1; j < (length[1] + 1) / 2; ++j) { data_real[istride[1] * (length[1] - j)] = data_real[istride[1] * j]; data_imag[istride[1] * (length[1] - j)] = -data_imag[istride[1] * j]; } if(length[0] % 2 == 0) { // y-axis at x-nyquist for(unsigned int j = 1; j < (length[1] + 1) / 2; ++j) { data_real[istride[0] * (length[0] / 2) + istride[1] * (length[1] - j)] = data_real[istride[0] * (length[0] / 2) + istride[1] * j]; data_imag[istride[0] * (length[0] / 2) + istride[1] * (length[1] - j)] = -data_imag[istride[0] * (length[0] / 2) + istride[1] * j]; } } // x-axis: for(unsigned int i = 1; i < (length[0] + 1) / 2; ++i) { data_real[istride[0] * (length[0] - i)] = data_real[istride[0] * i]; data_imag[istride[0] * (length[0] - i)] = -data_imag[istride[0] * i]; } if(length[1] % 2 == 0) { // x-axis at y-nyquist for(unsigned int i = 1; i < (length[0] + 1) / 2; ++i) { data_real[istride[0] * (length[0] - i) + istride[1] * (length[1] / 2)] = data_real[istride[0] * i + istride[1] * (length[1] / 2)]; data_imag[istride[0] * (length[0] - i) + istride[1] * (length[1] / 2)] = -data_imag[istride[0] * i + istride[1] * (length[1] / 2)]; } } // x-y plane: for(unsigned int i = 1; i < (length[0] + 1) / 2; ++i) { for(unsigned int j = 1; j < length[1]; ++j) { data_real[istride[0] * (length[0] - i) + istride[1] * (length[1] - j)] = data_real[istride[0] * i + istride[1] * j]; data_imag[istride[0] * (length[0] - i) + istride[1] * (length[1] - j)] = -data_imag[istride[0] * i + istride[1] * j]; } } if(length[2] % 2 == 0) { // x-axis at z-nyquist for(unsigned int i = 1; i < (length[0] + 1) / 2; ++i) { data_real[istride[0] * (length[0] - i) + istride[2] * (length[2] / 2)] = data_real[istride[0] * i + istride[2] * (length[2] / 2)]; data_imag[istride[0] * (length[0] - i) + istride[2] * (length[2] / 2)] = -data_imag[istride[0] * i + istride[2] * (length[2] / 2)]; } if(length[1] % 2 == 0) { // x-axis at yz-nyquist for(unsigned int i = 1; i < (length[0] + 1) / 2; ++i) { data_real[istride[0] * (length[0] - i) + istride[2] * (length[2] / 2)] = data_real[istride[0] * i + istride[2] * (length[2] / 2)]; data_imag[istride[0] * (length[0] - i) + istride[2] * (length[2] / 2)] = -data_imag[istride[0] * i + istride[2] * (length[2] / 2)]; } } // y-axis: at z-nyquist for(unsigned int j = 1; j < (length[1] + 1) / 2; ++j) { data_real[istride[1] * (length[1] - j) + istride[2] * (length[2] / 2)] = data_real[istride[1] * j + istride[2] * (length[2] / 2)]; data_imag[istride[1] * (length[1] - j) + istride[2] * (length[2] / 2)] = -data_imag[istride[1] * j + istride[2] * (length[2] / 2)]; } if(length[0] % 2 == 0) { // y-axis: at xz-nyquist for(unsigned int j = 1; j < (length[1] + 1) / 2; ++j) { data_real[istride[0] * (length[0] / 2) + istride[1] * (length[1] - j) + istride[2] * (length[2] / 2)] = data_real[istride[0] * (length[0] / 2) + istride[1] * j + istride[2] * (length[2] / 2)]; data_imag[istride[0] * (length[0] / 2) + istride[1] * (length[1] - j) + istride[2] * (length[2] / 2)] = -data_imag[istride[0] * (length[0] / 2) + istride[1] * j + istride[2] * (length[2] / 2)]; } } // x-y plane: at z-nyquist for(unsigned int i = 1; i < (length[0] + 1) / 2; ++i) { for(unsigned int j = 1; j < length[1]; ++j) { data_real[istride[0] * (length[0] - i) + istride[1] * (length[1] - j) + istride[2] * (length[2] / 2)] = data_real[istride[0] * i + istride[1] * j + istride[2] * (length[2] / 2)]; data_imag[istride[0] * (length[0] - i) + istride[1] * (length[1] - j) + istride[2] * (length[2] / 2)] = -data_imag[istride[0] * i + istride[1] * j + istride[2] * (length[2] / 2)]; } } } } } template static void generate_random_interleaved_data(std::vector& input, const Tint1& whole_length, const Tint1& whole_stride, const size_t idist, const size_t nbatch) { auto idata = (std::complex*)input[0].data(); size_t i_base = 0; auto partitions = partition_rowmajor(whole_length); for(unsigned int b = 0; b < nbatch; b++, i_base += idist) { #pragma omp parallel for num_threads(partitions.size()) for(size_t part = 0; part < partitions.size(); ++part) { auto index = partitions[part].first; const auto length = partitions[part].second; std::mt19937 gen(compute_index(index, whole_stride, i_base)); do { const auto i = compute_index(index, whole_stride, i_base); const Tfloat x = (Tfloat)gen() / (Tfloat)gen.max(); const Tfloat y = (Tfloat)gen() / (Tfloat)gen.max(); const std::complex val(x, y); idata[i] = val; } while(increment_rowmajor(index, length)); } } } template static void generate_interleaved_data(std::vector& input, const Tint1& whole_length, const Tint1& whole_stride, const size_t idist, const size_t nbatch) { auto idata = (std::complex*)input[0].data(); size_t i_base = 0; auto partitions = partition_rowmajor(whole_length); auto unit_stride = make_unit_stride(whole_length); const Tfloat inv_scale = 1.0 / static_cast(count_iters(whole_length) - 1); for(unsigned int b = 0; b < nbatch; b++, i_base += idist) { #pragma omp parallel for num_threads(partitions.size()) for(size_t part = 0; part < partitions.size(); ++part) { auto index = partitions[part].first; const auto length = partitions[part].second; do { const auto val_xy = -0.5 + static_cast(compute_index(index, unit_stride, 0)) * inv_scale; const std::complex val(val_xy, val_xy); const auto i = compute_index(index, whole_stride, i_base); idata[i] = val; } while(increment_rowmajor(index, length)); } } } template static void generate_random_planar_data(std::vector& input, const Tint1& whole_length, const Tint1& whole_stride, const size_t idist, const size_t nbatch) { auto ireal = (Tfloat*)input[0].data(); auto iimag = (Tfloat*)input[1].data(); size_t i_base = 0; auto partitions = partition_rowmajor(whole_length); for(unsigned int b = 0; b < nbatch; b++, i_base += idist) { #pragma omp parallel for num_threads(partitions.size()) for(size_t part = 0; part < partitions.size(); ++part) { auto index = partitions[part].first; const auto length = partitions[part].second; std::mt19937 gen(compute_index(index, whole_stride, i_base)); do { const auto i = compute_index(index, whole_stride, i_base); const std::complex val((Tfloat)gen() / (Tfloat)gen.max(), (Tfloat)gen() / (Tfloat)gen.max()); ireal[i] = val.real(); iimag[i] = val.imag(); } while(increment_rowmajor(index, length)); } } } template static void generate_planar_data(std::vector& input, const Tint1& whole_length, const Tint1& whole_stride, const size_t idist, const size_t nbatch) { auto ireal = (Tfloat*)input[0].data(); auto iimag = (Tfloat*)input[1].data(); size_t i_base = 0; auto partitions = partition_rowmajor(whole_length); auto unit_stride = make_unit_stride(whole_length); const Tfloat inv_scale = 1.0 / static_cast(count_iters(whole_length) - 1); for(unsigned int b = 0; b < nbatch; b++, i_base += idist) { #pragma omp parallel for num_threads(partitions.size()) for(size_t part = 0; part < partitions.size(); ++part) { auto index = partitions[part].first; const auto length = partitions[part].second; do { const auto val_xy = -0.5 + static_cast(compute_index(index, unit_stride, 0)) * inv_scale; const auto i = compute_index(index, whole_stride, i_base); ireal[i] = val_xy; iimag[i] = val_xy; } while(increment_rowmajor(index, length)); } } } template static void generate_random_real_data(std::vector& input, const Tint1& whole_length, const Tint1& whole_stride, const size_t idist, const size_t nbatch) { auto idata = (Tfloat*)input[0].data(); size_t i_base = 0; auto partitions = partition_rowmajor(whole_length); for(unsigned int b = 0; b < nbatch; b++, i_base += idist) { #pragma omp parallel for num_threads(partitions.size()) for(size_t part = 0; part < partitions.size(); ++part) { auto index = partitions[part].first; const auto length = partitions[part].second; std::mt19937 gen(compute_index(index, whole_stride, i_base)); do { const auto i = compute_index(index, whole_stride, i_base); const Tfloat val = (Tfloat)gen() / (Tfloat)gen.max(); idata[i] = val; } while(increment_rowmajor(index, length)); } } } template static void generate_real_data(std::vector& input, const Tint1& whole_length, const Tint1& whole_stride, const size_t idist, const size_t nbatch) { auto idata = (Tfloat*)input[0].data(); size_t i_base = 0; auto partitions = partition_rowmajor(whole_length); auto unit_stride = make_unit_stride(whole_length); const Tfloat inv_scale = 1.0 / static_cast(count_iters(whole_length) - 1); for(unsigned int b = 0; b < nbatch; b++, i_base += idist) { #pragma omp parallel for num_threads(partitions.size()) for(size_t part = 0; part < partitions.size(); ++part) { auto index = partitions[part].first; const auto length = partitions[part].second; do { const auto i = compute_index(index, whole_stride, i_base); idata[i] = -0.5 + static_cast(compute_index(index, unit_stride, 0)) * inv_scale; } while(increment_rowmajor(index, length)); } } } template static void impose_hermitian_symmetry_interleaved(std::vector& vals, const std::vector& length, const std::vector& istride, const Tsize idist, const Tsize nbatch) { switch(length.size()) { case 1: impose_hermitian_symmetry_interleaved_1D(vals, length, istride, idist, nbatch); break; case 2: impose_hermitian_symmetry_interleaved_2D(vals, length, istride, idist, nbatch); break; case 3: impose_hermitian_symmetry_interleaved_3D(vals, length, istride, idist, nbatch); break; default: throw std::runtime_error("Invalid dimension for impose_hermitian_symmetry"); } } template static void impose_hermitian_symmetry_planar(std::vector& vals, const std::vector& length, const std::vector& istride, const Tsize idist, const Tsize nbatch) { switch(length.size()) { case 1: impose_hermitian_symmetry_planar_1D(vals, length, istride, idist, nbatch); break; case 2: impose_hermitian_symmetry_planar_2D(vals, length, istride, idist, nbatch); break; case 3: impose_hermitian_symmetry_planar_3D(vals, length, istride, idist, nbatch); break; default: throw std::runtime_error("Invalid dimension for impose_hermitian_symmetry"); } } #endif // DATA_GEN_HOST_H upstream/shared/increment.h0000664000175000017500000000731314637212246014774 0ustar kaolkaol// Copyright (C) 2021 - 2022 Advanced Micro Devices, Inc. All rights reserved. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. #ifndef ROCFFT_INCREMENT_H #define ROCFFT_INCREMENT_H #include #include #include // Helper functions to iterate over a buffer in row-major order. // Indexes may be given as either a tuple or vector of sizes. They // return true if the index was successfully incremented to move to // the next element in the buffer. template static bool increment_base(T1& index, const T2& length) { static_assert(std::is_integral::value, "Integral required."); static_assert(std::is_integral::value, "Integral required."); if(index < length - 1) { ++index; return true; } index = 0; return false; } // Increment the index (row-major) for looping over 1, 2, and 3 dimensions length. template static bool increment_rowmajor(T1& index, const T2& length) { static_assert(std::is_integral::value, "Integral required."); static_assert(std::is_integral::value, "Integral required."); return increment_base(index, length); } template static bool increment_rowmajor(std::tuple& index, const std::tuple& length) { if(increment_base(std::get<1>(index), std::get<1>(length))) // we incremented ok, nothing further to do return true; // otherwise, we rolled over return increment_base(std::get<0>(index), std::get<0>(length)); } template static bool increment_rowmajor(std::tuple& index, const std::tuple& length) { if(increment_base(std::get<2>(index), std::get<2>(length))) // we incremented ok, nothing further to do return true; if(increment_base(std::get<1>(index), std::get<1>(length))) // we incremented ok, nothing further to do return true; // otherwise, we rolled over return increment_base(std::get<0>(index), std::get<0>(length)); } // Increment row-major index over arbitrary dimension length template bool increment_rowmajor(std::vector& index, const std::vector& length) { for(int idim = length.size(); idim-- > 0;) { if(index[idim] < length[idim]) { if((++index[idim]) == length[idim]) { index[idim] = 0; continue; } // we know we were able to increment something and didn't hit the end return true; } } // End the loop when we get back to the start: return !std::all_of(index.begin(), index.end(), [](int i) { return i == 0; }); } #endif upstream/shared/array_predicate.h0000664000175000017500000000366314637212246016152 0ustar kaolkaol// Copyright (C) 2021 - 2022 Advanced Micro Devices, Inc. All rights reserved. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. #ifndef ROCFFT_ARRAY_PREDICATE_H #define ROCFFT_ARRAY_PREDICATE_H #include "rocfft/rocfft.h" namespace { bool array_type_is_complex(rocfft_array_type type) { return type == rocfft_array_type_complex_interleaved || type == rocfft_array_type_complex_planar || type == rocfft_array_type_hermitian_interleaved || type == rocfft_array_type_hermitian_planar; } bool array_type_is_interleaved(rocfft_array_type type) { return type == rocfft_array_type_complex_interleaved || type == rocfft_array_type_hermitian_interleaved; } bool array_type_is_planar(rocfft_array_type type) { return type == rocfft_array_type_complex_planar || type == rocfft_array_type_hermitian_planar; } } #endif upstream/shared/precision_type.h0000664000175000017500000000457614637212246016054 0ustar kaolkaol// Copyright (C) 2023 Advanced Micro Devices, Inc. All rights reserved. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. #ifndef ROCFFT_PRECISION_TYPE_H #define ROCFFT_PRECISION_TYPE_H #include "array_predicate.h" #include "rocfft/rocfft.h" static size_t real_type_size(rocfft_precision precision) { switch(precision) { case rocfft_precision_half: return 2; case rocfft_precision_single: return 4; case rocfft_precision_double: return 8; } } static size_t complex_type_size(rocfft_precision precision) { return real_type_size(precision) * 2; } static const char* precision_name(rocfft_precision precision) { switch(precision) { case rocfft_precision_half: return "half"; case rocfft_precision_single: return "single"; case rocfft_precision_double: return "double"; } } static size_t element_size(rocfft_precision precision, rocfft_array_type array_type) { return array_type_is_complex(array_type) ? complex_type_size(precision) : real_type_size(precision); } // offset a pointer by a number of elements, given the elements' // precision and type (complex or not) static void* ptr_offset(void* p, size_t elems, rocfft_precision precision, rocfft_array_type type) { return static_cast(p) + elems * element_size(precision, type); } #endif upstream/shared/hip_object_wrapper.h0000664000175000017500000000503714637212246016657 0ustar kaolkaol/****************************************************************************** * Copyright (C) 2023 Advanced Micro Devices, Inc. All rights reserved. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. *******************************************************************************/ #ifndef ROCFFT_HIP_OBJ_WRAPPER_H #define ROCFFT_HIP_OBJ_WRAPPER_H #include "rocfft_hip.h" // RAII wrapper around HIP objects template struct hip_object_wrapper_t { hip_object_wrapper_t() : obj(nullptr) { } void alloc() { if(obj == nullptr && TCreate(&obj) != hipSuccess) throw std::runtime_error("hip create failure"); } void free() { if(obj) { (void)TDestroy(obj); obj = nullptr; } } operator const T&() const { return obj; } operator T&() { return obj; } operator bool() const { return obj != nullptr; } ~hip_object_wrapper_t() { free(); } hip_object_wrapper_t(const hip_object_wrapper_t&) = delete; hip_object_wrapper_t& operator=(const hip_object_wrapper_t&) = delete; hip_object_wrapper_t(hip_object_wrapper_t&& other) : obj(other.obj) { other.obj = nullptr; } private: T obj; }; typedef hip_object_wrapper_t hipStream_wrapper_t; typedef hip_object_wrapper_t hipEvent_wrapper_t; #endif // ROCFFT_HIP_OBJ_WRAPPER_H upstream/.githooks/0000775000175000017500000000000014637212246013272 5ustar kaolkaolupstream/.githooks/install0000775000175000017500000000022214637212246014662 0ustar kaolkaol#!/usr/bin/env bash cd $(git rev-parse --git-dir) cd hooks echo "Installing hooks..." ln -s ../../.githooks/pre-commit pre-commit echo "Done!" upstream/.githooks/pre-commit0000775000175000017500000000176714637212246015307 0ustar kaolkaol#!/bin/sh # # This pre-commit hook checks if any versions of clang-format # are installed, and if so, uses the installed version to format # the staged changes. base=/opt/rocm/llvm/bin/clang-format format="" # Redirect output to stderr. exec 1>&2 # check if clang-format is installed type "$base" >/dev/null 2>&1 && format="$base" # no versions of clang-format are installed if [ -z "$format" ] then echo "$base is not installed. Pre-commit hook will not be executed." exit 0 fi # Do everything from top - level cd $(git rev-parse --show-toplevel) if git rev-parse --verify HEAD >/dev/null 2>&1 then against=HEAD else # Initial commit: diff against an empty tree object against=4b825dc642cb6eb9a060e54bf8d69288fbee4904 fi # do the formatting for file in $(git diff-index --cached --name-only $against | grep -E '\.h$|\.hpp$|\.cpp$|\.cl$|\.h\.in$|\.hpp\.in$|\.cpp\.in$') do if [ -e "$file" ] then echo "$format $file" "$format" -i -style=file "$file" fi done upstream/docs/0000775000175000017500000000000014637212425012314 5ustar kaolkaolupstream/docs/.sphinx/0000775000175000017500000000000014637212425013703 5ustar kaolkaolupstream/docs/.sphinx/requirements.txt0000664000175000017500000000575114637212425017177 0ustar kaolkaol# # This file is autogenerated by pip-compile with Python 3.8 # by the following command: # # pip-compile requirements.in # accessible-pygments==0.0.3 # via pydata-sphinx-theme alabaster==0.7.13 # via sphinx babel==2.12.1 # via # pydata-sphinx-theme # sphinx beautifulsoup4==4.11.2 # via pydata-sphinx-theme breathe==4.34.0 # via rocm-docs-core certifi==2022.12.7 # via requests cffi==1.15.1 # via # cryptography # pynacl charset-normalizer==3.1.0 # via requests click==8.1.3 # via sphinx-external-toc cryptography==40.0.2 # via pyjwt deprecated==1.2.13 # via pygithub docutils==0.19 # via # breathe # myst-parser # pydata-sphinx-theme # sphinx fastjsonschema==2.17.1 # via rocm-docs-core gitdb==4.0.10 # via gitpython gitpython==3.1.31 # via rocm-docs-core idna==3.4 # via requests imagesize==1.4.1 # via sphinx importlib-metadata==6.0.0 # via sphinx importlib-resources==5.12.0 # via rocm-docs-core jinja2==3.1.2 # via # myst-parser # sphinx markdown-it-py==2.2.0 # via # mdit-py-plugins # myst-parser markupsafe==2.1.2 # via jinja2 mdit-py-plugins==0.3.5 # via myst-parser mdurl==0.1.2 # via markdown-it-py myst-parser==1.0.0 # via rocm-docs-core packaging==23.0 # via # pydata-sphinx-theme # sphinx pycparser==2.21 # via cffi pydata-sphinx-theme==0.13.3 # via # rocm-docs-core # sphinx-book-theme pygithub==1.58.1 # via rocm-docs-core pygments==2.14.0 # via # accessible-pygments # pydata-sphinx-theme # sphinx pyjwt[crypto]==2.6.0 # via pygithub pynacl==1.5.0 # via pygithub pytz==2023.3 # via babel pyyaml==6.0 # via # myst-parser # rocm-docs-core # sphinx-external-toc requests==2.28.2 # via # pygithub # sphinx rocm-docs-core>=0.20.0 # via -r requirements.in smmap==5.0.0 # via gitdb snowballstemmer==2.2.0 # via sphinx soupsieve==2.4 # via beautifulsoup4 sphinx==5.3.0 # via # breathe # myst-parser # pydata-sphinx-theme # rocm-docs-core # sphinx-book-theme # sphinx-copybutton # sphinx-design # sphinx-external-toc # sphinx-notfound-page sphinx-book-theme==1.0.1 # via rocm-docs-core sphinx-copybutton==0.5.1 # via rocm-docs-core sphinx-design==0.4.1 # via rocm-docs-core sphinx-external-toc==0.3.1 # via rocm-docs-core sphinx-notfound-page==0.8.3 # via rocm-docs-core sphinxcontrib-applehelp==1.0.4 # via sphinx sphinxcontrib-devhelp==1.0.2 # via sphinx sphinxcontrib-htmlhelp==2.0.1 # via sphinx sphinxcontrib-jsmath==1.0.1 # via sphinx sphinxcontrib-qthelp==1.0.3 # via sphinx sphinxcontrib-serializinghtml==1.1.5 # via sphinx typing-extensions==4.5.0 # via pydata-sphinx-theme urllib3==1.26.15 # via requests wrapt==1.15.0 # via deprecated zipp==3.15.0 # via # importlib-metadata # importlib-resources upstream/docs/.sphinx/_toc.yml.in0000664000175000017500000000034414637212425015760 0ustar kaolkaolroot: index defaults: numbered: False subtrees: - caption: Design entries: - file: design/design - caption: API entries: - file: api - file: allapi - caption: License entries: - file: license upstream/docs/.sphinx/requirements.in0000664000175000017500000000002714637212425016755 0ustar kaolkaolrocm-docs-core>=0.20.0 upstream/docs/real.rst0000664000175000017500000001477114637212425014003 0ustar kaolkaolReal data --------- When real data is subject to DFT, the resulting complex output data follows a special property. About half of the output is redundant because they are complex conjugates of the other half. This is called the Hermitian redundancy. So, for space and performance considerations, it is only necessary to store the non-redundant part of the data. Most FFT libraries use this property to offer specific storage layouts for FFTs involving real data. rocFFT provides three enumeration values for :cpp:enum:`rocfft_array_type` to deal with real data FFTs: * REAL (:cpp:enumerator:`rocfft_array_type_real`) * HERMITIAN_INTERLEAVED (:cpp:enumerator:`rocfft_array_type_hermitian_interleaved`) * HERMITIAN_PLANAR (:cpp:enumerator:`rocfft_array_type_hermitian_planar`) The REAL (:cpp:enumerator:`rocfft_array_type_real`) enum specifies that the data is purely real. This can be used to feed real input or get back real output. The HERMITIAN_INTERLEAVED (:cpp:enumerator:`rocfft_array_type_hermitian_interleaved`) and HERMITIAN_PLANAR (:cpp:enumerator:`rocfft_array_type_hermitian_planar`) enums are similar to the corresponding full complex enums in the way they store real and imaginary components, but store only about half of the complex output. Client applications can do just a forward transform and analyze the output or they can process the output and do a backward transform to get back real data. This is illustrated in the following figure. .. figure:: ./data/images/realfft_fwdinv.jpg **Forward and Backward Real FFTs** .. note:: Real backward FFTs require that the input data be Hermitian-symmetric, as would naturally happen in the output of a real forward FFT. rocFFT will produce undefined results if this requirement is not met. Let us consider a 1D real FFT of length :math:`N`. The full output looks as shown in following figure. .. figure:: ./data/images/realfft_1dlen.jpg **1D Real FFT of Length N** Here, C* denotes the complex conjugate. Since the values at indices greater than :math:`N/2` can be deduced from the first half of the array, rocFFT stores data only up to the index :math:`N/2`. This means that the output contains only :math:`1 + N/2` complex elements, where the division :math:`N/2` is rounded down. Examples for even and odd lengths are given below. Example for :math:`N = 8` is shown in following figure. .. figure:: ./data/images/realfft_ex_n8.jpg **Example for N = 8** Example for :math:`N = 7` is shown in following figure. .. figure:: ./data/images/realfft_ex_n7.jpg **Example for N = 7** For length 8, only :math:`(1 + 8/2) = 5` of the output complex numbers are stored, with the index ranging from 0 through 4. Similarly for length 7, only :math:`(1 + 7/2) = 4` of the output complex numbers are stored, with the index ranging from 0 through 3. For 2D and 3D FFTs, the FFT length along the innermost dimension is used to compute the :math:`(1 + N/2)` value. This is because the FFT along the innermost dimension is computed first and is logically a real-to-hermitian transform. The FFTs along other dimensions are computed next, and they are simply 'complex-to-complex' transforms. For example, assuming :math:`Lengths[2]` is used to set up a 2D real FFT, let :math:`N1 = Lengths[1]`, and :math:`N0 = Lengths[0]`. The output FFT has :math:`N1*(1 + N0/2)` complex elements. Similarly, for a 3D FFT with :math:`Lengths[3]` and :math:`N2 = Lengths[2]`, :math:`N1 = Lengths[1]`, and :math:`N0 = Lengths[0]`, the output has :math:`N2*N1*(1 + N0/2)` complex elements. Supported array type combinations --------------------------------- Not In-place transforms: * Forward: REAL to HERMITIAN_INTERLEAVED * Forward: REAL to HERMITIAN_PLANAR * Backward: HERMITIAN_INTERLEAVED to REAL * Backward: HERMITIAN_PLANAR to REAL In-place transforms: * Forward: REAL to HERMITIAN_INTERLEAVED * Backward: HERMITIAN_INTERLEAVED to REAL Setting strides --------------- The library currently requires the user to explicitly set input and output strides for real transforms for non simple cases. See the following examples to understand what values to use for input and output strides under different scenarios. These examples show typical usages, but the user can allocate the buffers and choose data layout according to their need. Examples -------- The following figures and examples explain in detail the real FFT features of this library. Here is a schematic that illustrates the forward 1D FFT (real to hermitian). .. figure:: ./data/images/realfft_expl_01.jpg **1D FFT - Real to Hermitian** Below is a schematic that shows an example of not in-place transform with even :math:`N` and how strides and distances are set. .. figure:: ./data/images/realfft_expl_02.jpg **1D FFT - Real to Hermitian, Example 1** Below is a schematic that shows an example of in-place transform with even :math:`N` and how strides and distances are set. Notice that even though we are dealing with only 1 buffer (in-place), the output strides/distance can take different values compared to input strides/distance. .. figure:: ./data/images/realfft_expl_03.jpg **1D FFT - Real to Hermitian, Example 2** Below is a schematic that shows an example of in-place transform with odd :math:`N` and how strides and distances are set. Notice that even though we are dealing with only 1 buffer (in-place), the output strides/distance can take different values compared to input strides/distance. .. figure:: ./data/images/realfft_expl_04.jpg **1D FFT - Real to Hermitian, Example 3** And here is a schematic that illustrates the backward 1D FFT (hermitian to real). .. figure:: ./data/images/realfft_expl_05.jpg **1D FFT - Hermitian to Real** Below is a schematic that shows an example of in-place transform with even :math:`N` and how strides and distances are set. Notice that even though we are dealing with only 1 buffer (in-place), the output strides/distance can take different values compared to input strides/distance. .. figure:: ./data/images/realfft_expl_06.jpg **1D FFT - Hermitian to Real, Example** And here is a schematic that illustrates the in-place forward 2D FFT (real to hermitian) . .. figure:: ./data/images/realfft_expl_07.jpg **2D FFT - Real to Hermitian In Place** Below is a schematic that shows an example of in-place 2D transform and how strides and distances are set. Notice that even though we are dealing with only 1 buffer (in-place), the output strides/distance can take different values compared to input strides/distance. .. figure:: ./data/images/realfft_expl_08.jpg **2D FFT - Real to Hermitian, Example** upstream/docs/design/0000775000175000017500000000000014637212425013565 5ustar kaolkaolupstream/docs/design/buffer_assignment.rst0000664000175000017500000004413514637212425020027 0ustar kaolkaolBuffer assignment design document for rocFFT ============================================ Copyright and Disclaimer ------------------------ DISCLAIMER The information contained herein is for informational purposes only, and is subject to change without notice. While every precaution has been taken in the preparation of this document, it may contain technical inaccuracies, omissions and typographical errors, and AMD is under no obligation to update or otherwise correct this information. Advanced Micro Devices, Inc. makes no representations or warranties with respect to the accuracy or completeness of the contents of this document, and assumes no liability of any kind, including the implied warranties of noninfringement, merchantability or fitness for particular purposes, with respect to the operation or use of AMD hardware, software or other products described herein. No license, including implied or arising by estoppel, to any intellectual property rights is granted by this document. Terms and limitations applicable to the purchase or use of AMD’s products are as set forth in a signed agreement between the parties or in AMD's Standard Terms and Conditions of Sale. AMD is a trademark of Advanced Micro Devices, Inc. Other product names used in this publication are for identification purposes only and may be trademarks of their respective companies. Copyright (C) 2021 - 2022 Advanced Micro Devices, Inc. All rights reserved. Summary ------- Buffer assignment in rocFFT is the work of coordinating the input and output buffers of each step in a rocFFT plan. Observations ------------ Some observations can be made about the FFT planning and buffer assignment process: 1. Buffer assignment begins after the plan structure is decided. This means all of the node schemes, as well as input lengths and input/output strides are known. Note that at this time, output lengths are not directly known. 2. The first child of any non-leaf node must read from its parent's input, and the last child must write to its parent's output. 3. The input of any other child node must be the same as the output of its preceding sibling. There is one exception: a CS_KERNEL_CHIRP node is only used to populate the Bluestein temporary buffer, and can essentially be ignored during buffer processing as it does not actually read input. 4. The top-level node in the tree must read from the user-defined input buffer, and write to the user-defined output buffer. These buffers will be the same for in-place transforms. 5. When deciding buffer assignments for a node, only the output buffer for nodes besides the last requires actual decision making. The input buffer follows either from the top-level input, a preceding sibling, or a parent node's input. The last node's output must be the user-defined output. 6. During buffer assignment, some number of buffers are available. At minimum, we have the user input and output buffers (which may be the same for in-place transforms) whose sizes are defined by the user. Zero or more temporary buffers may be needed, whose sizes are dynamic and can be as big as necessary for the transform to succeed. 7. Some choices of output buffers are clearly invalid. For example: * Transpose nodes must always be out-of-place (i.e. input buffer cannot equal output buffer). * Some internal kernels only support interleaved formats as their input or output. For example, the input of a copy-kernel (like COPY_CMPLX_TO_R or COPY_CMPLX_TO_HERM) must be interleaved. * Internal temp buffers are allocated contigously, so they can be used on both planar and interlevead formats. This is not always true for user-provided buffers. An obvious example of this planar data: users typically create these using two buffers. * A node cannot write data to a buffer if the buffer is too small for that data. This really only applies to user input/output buffers, as temp buffers are always made large enough. Solution -------- We implement a decision function that determines whether a buffer assignment is valid based on the observations above. Buffer assignment should do an exhaustive search through the space of possible buffer assignments for the tree, calling the decision function for each potential choice. If we arrive at the end of the tree and all assignments are valid, then the buffer assignment operation is complete. Returning the first valid buffer assignment found is a simple solution. However, not all valid buffer assignments are equal in terms of memory usage and/or performance: some buffer assignments allow more kernel fusions and/or use more inplace kernels. This implies that we should keep all valid assignment candidates in a list and subsequently return the "best" one. The first pass of buffer assignment shall attempt to assign buffers starting with just the user input buffer (and output buffer, if distinct) and a Bluestein temp buffer (if the plan requires it), but without any other temp buffers. If that first pass is not successful, we retry with one temp buffer added to the list of available buffers. If that is still not successful, we retry with a second temp buffer. Implementation -------------- A Structure Storing A Try ^^^^^^^^^^^^^^^^^^^^^^^^^^ We store our current assignment try in a tree-like structure. We don't assign to the tree-node directly since there could be many valid assignment paths for one plan. Once we determine the best assignment path from this tree, we fill the assignment back to the real tree-node. .. code-block:: cpp struct PlacementTrace { TreeNode* referedNode; Buffer inBuffer, outBuffer; ArrayType inArrayType, outArrayType; // each branch stands for a possible try on the next node vector branches; // a parent pointer to backtracing PlacementTrace* parent; // propagate these values from the root to leaves size_t numFusedNodes; size_t numInplace; size_t numTypwSwithching; } Exhaustive Search ^^^^^^^^^^^^^^^^^ All possible assignments on each node are attempted. There are several limitations on each node that allow us to reject many illegal assignments and prevent the assignment tree from growing exponentially. For example, SBRC and transpose kernels can only be done using out-of-place buffers. The exhaustive search is implemented in pseudocode like: .. code-block:: cpp // ------------------------------------------------------------------------------------ // Recursive function enumrates all the possible assignments // Returns a sub-tree, starting from execSeq[curSeqID], with input startBuf & startAType // ------------------------------------------------------------------------------------ Function: void Enumerate(PlacementTrace* parent, ExecPlan, curSeqID, startBuf, startAType) // for terminal condition: - if curSeqID is the last nodes - if the end buffer and array-type fit the root-plan setting - calculate the number of eligible kernel-fusions. - add this candidate to the winnerCandidates list. - finish this path, return // not terminal condition: // add a single assignment on current node and append to parent's branches - if current node->isPlacementAllowed(inplace) // add a branch which uses inplace (if allowed) on this node and test validity - if ValidOutBuffer(execPlan, *curNode, startBuf, startType) - append an assignIP = PlacementTrace(curNode, startBuf, startBuf, startType, startType, parent) - call Enumerate(IPAssign, execPlan, curSeqID + 1, startBuf, startType); - if current node->isPlacementAllowed(out-of-place) // add branches which use out-of-place (if allowed) on this node and test validity - for each testOutputBuf in the availableBuffers set, (where testOutputBuf != startBuf) - if ValidOutBuffer(execPlan, *curNode, testOutputBuf, testOutType) - append an assignOP = PlacementTrace(curNode, startBuf, testOutputBuf, startType, testOutType, parent) - call Enumerate(OPAssign, execPlan, curSeqID + 1, testOutputBuf, testOutType); // -------------------------------------------------------- // Decision maker: choose the best one from all candidates // This function is a sorting function, pick the first one // -------------------------------------------------------- Function: void ValidPathExists(ExecPlan) - if winnerCandidates is empty, simply return false - using std::sort, sort by: // the one can fuse more kernels is better - lhs->numFusedNodes > rhs->numFusedNodes ? // if tie, compare inplace kernels, more is better - lhs->numInplace > rhs->numInplace ? // if tie, compare the times of switching-array-type, less is better - lhs->numTypeSwitching < rhs->numTypeSwitching ? - pick the first one, and do the Backtracking() - fill-in the assignment back to the real tree-nodes // --------------------------------------------------------- // Top-level function that assigns buffers on the root plan // --------------------------------------------------------- Function: void AssignBuffers(ExecPlan) - add rootPlan in/out buffer to availableBuffers set - Note: For C2C out-of-place, we can't add USER_IN to the set to prevent it from being modified. - add rootPlan in/out array-type to availableArrayTypes set - add OB_TEMP_BLUESTEIN to availableBuffers set, if plan uses Bluestein - initialize a winnerCandidates list to save all valid results. - initialize a dummyRoot of PlacementTrace as tree root, this dummyRoot pretends it's a parent of the first node (in execSeq). So dummyRoot.outBuf = rootPlan->obIn, and dummyRoot.oType = rootPlan->inArrayType // The 1st round try - call Enumerate(&dummyRoot, execPlan, 0, dummyRoot.outBuf, dummyRoot.oType) here 0 is curSeqID, which means starting from the first leafNode - call ValidPathExists() to pick the best solution - if successful, return // The 2nd round try - add OB_TEMP to availableBuffers - call Enumerate(&dummyRoot, execPlan, 0, dummyRoot.outBuf, dummyRoot.oType) here 0 is curSeqID, which means starting from the first leafNode - call ValidPathExists() to pick the best solution - if successful, return // The last round try - add OB_TEMP_CMPLX_FOR_REAL to availableBuffers - call Enumerate(&dummyRoot, execPlan, 0, dummyRoot.outBuf, dummyRoot.oType) here 0 is curSeqID, which means starting from the first leafNode - call ValidPathExists() to pick the best solution - if successful, return // Failed - if not found, throw exception. Decision Function and Output Lengths ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Much of the remaining complexity lies in the ValidOutBuffer() decision function mentioned above. Output lengths often differ from input lengths on a node. For example, R2C/C2R transforms change the data length from the input, and transpose kernels swap dimension lengths between input and output. Tree nodes need to store their output length explicitly so that the decision function does not need to guess at what lengths any node will output. This information is also helpful to log, so humans reading the plan don't need to guess either. As the exhaustive search proceeds, it likely needs to call the decision function multiple times with identical inputs. This is because it might need to decide validity of two plans that might only have tiny buffer assignment differences. The results of the function are cached to reduce extra work during the search. Fusions ^^^^^^^ Kernel-fusion is essential for improving performance. Unfortunately fusion depends heavily on buffer assignment. Two (or more) kernels can be fused into one kernel only when the resulting buffer assignment remains valid. To maximise kernel fusion, we also implement a FuseShim framework. A FuseShim class is a container/shell indicating that there is a potentially-fusable kernel-fusion. Each FuseShim defines its own requirements to fulfill the fusion, including the expected buffer assignment. During the buffer assignment process, we can use the test function to get the final number of the achievable kernel fusions. This number plays the most important role when making the final decision: we always pick the one which can fuse the most kernels. Padding ^^^^^^^ We have cases where reading/writing along certain strides is bad for performance (e.g. power-of-2). While we are unable to adjust strides for user-provided input and output buffers, we can potentially pad temp buffers to avoid bad strides. Once a plan candidate is constructed and buffers are assigned (including any kernel fusion), a padding pass can adjust the output strides of any node that writes to a temp buffer with bad strides. The padding pass must also consider the input lengths and strides of subsequent nodes that continue to use the same temp buffer, and adjust them accordingly. The writing and reading nodes might also decompose the problem differently, so the logic needs to be aware that a change to one dimension's stride on the write side may affect multiple dimensions' strides on the reading side, and vice-versa. Padding example &&&&&&&&&&&&&&& For example, consider this excerpt of a large plan: .. code-block:: scheme: CS_KERNEL_TRANSPOSE length: 4096 262144 outputLength: 262144 4096 iStrides: 1 4096 oStrides: 1 262144 OB_USER_OUT -> OB_TEMP scheme: CS_KERNEL_STOCKHAM_BLOCK_CC length: 512 512 4096 outputLength: 512 512 4096 iStrides: 512 1 262144 oStrides: 512 1 262144 OB_TEMP -> OB_TEMP scheme: CS_KERNEL_STOCKHAM_BLOCK_RC length: 512 512 4096 outputLength: 512 512 4096 iStrides: 1 512 262144 oStrides: 1 512 262144 OB_TEMP -> OB_USER_OUT The first kernel writes 262144 elements on the fastest dimension, and the higher dimension of 4096 elements is written along large power-of-2 strides, making it a good candidate for padding. The following two kernels decompose the 262144 length to 512x512 along their fastest dimensions. Padded output of the first kernel needs to modify the following strides using the same buffer, until the data leaves that temp buffer: .. code-block:: scheme: CS_KERNEL_TRANSPOSE length: 4096 262144 outputLength: 262144 4096 iStrides: 1 4096 oStrides: 1 262208 OB_USER_OUT -> OB_TEMP scheme: CS_KERNEL_STOCKHAM_BLOCK_CC length: 512 512 4096 outputLength: 512 512 4096 iStrides: 512 1 262208 oStrides: 512 1 262208 OB_TEMP -> OB_TEMP scheme: CS_KERNEL_STOCKHAM_BLOCK_RC length: 512 512 4096 outputLength: 512 512 4096 iStrides: 1 512 262208 oStrides: 1 512 262144 OB_TEMP -> OB_USER_OUT The second kernel is in-place, and would need iStrides == oStrides. The padding pass would need to continue through the execution plan to keep the third kernel's input strides consistent with the second's output. The output of the third kernel is a user buffer, so we cannot change its padding. When to pad &&&&&&&&&&& The exact criteria for when to add padding to a temp buffer (and how much) are an implementation detail, but ad-hoc planning we've done in the past has padded strides if higher dimension data longer than a threshold is written along sufficiently large powers of two. The decision logic around padding is centralized in one place in this design, making it more feasible to have per-architecture decisions around padding, should they become necessary. Choosing a winner ^^^^^^^^^^^^^^^^^ The exhaustive search is a depth-first-search that produces a list of valid plans, each of which would produce correct results. The list is sorted to decide which option is best, and the best plan is ultimately given to the user for execution. The sort criteria are: 1. Number of fused kernels (more is better, to minimize kernel launches and global memory reads/writes) 2. Number of buffers used (fewer is better, to minimize temporary memory usage) 3. Number of padded reads/writes (more is better, to maximize use of padding once we've accepted the memory cost) 4. Number of in-place operations (more is better) 5. Number of type changes (e.g. planar -> interleaved, or vice-versa) in the plan (fewer is better, as a tiebreaker) Future Work ----------- Strides ^^^^^^^ Currently, rocFFT does not guarantee that strides on user buffers are respected if temporary data is written to those buffers. With this implementation, it would be simpler to begin enforcing such a guarantee. Enforcing Read-only Input ^^^^^^^^^^^^^^^^^^^^^^^^^ rocFFT may currently overwrite user input buffers for out-of-place real-transforms (not C2C-transform). Although we've documented this behaviour and it is common practice in other libraries, it might still be unintuitive for some users. If we ever wanted to start guaranteeing that user input is left unmodified, this buffer assignment implementation would make that work trivial - only the decision function needs to be made aware of this policy change, and buffer assignment will work fine. However, we may need to introduce yet another temp buffer, since we'd be taking away a potential work space from existing plans. Flexibility Between Minimizing Memory or Maximizing Fusions ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ We can't always expect there is a perfect assignment that maximises kernel fusions while also minimising temporary buffers. In some cases, these two goals are contradictory: if we choose an assignment using minimal buffers, we may loose the oppurtunity to fuse more kernels. On the other hand, if we are allowed to use more memory, we have more buffers available for out-of-place kernel-fusions. With this implementation, it is possible to introduce an optimization strategy option to users. For example, if the memory usage is the main concern of users, we can return the assignment with least buffer usage. Otherwise, we return the result which maximizes the kernel fusions regardless of the memory consumption. Make C Buffer as Temp2 Buffer ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ There is no reason to limit the "C" buffer to real-transforms only. We can make the C buffer as another generic temporary buffer throughout; this can also avoid any confusion about the purpose of C and T. upstream/docs/design/codegen.rst0000664000175000017500000002412114637212425015723 0ustar kaolkaolCode Generator Design Document for rocFFT ========================================= Copyright and Disclaimer --------- DISCLAIMER The information contained herein is for informational purposes only, and is subject to change without notice. While every precaution has been taken in the preparation of this document, it may contain technical inaccuracies, omissions and typographical errors, and AMD is under no obligation to update or otherwise correct this information. Advanced Micro Devices, Inc. makes no representations or warranties with respect to the accuracy or completeness of the contents of this document, and assumes no liability of any kind, including the implied warranties of noninfringement, merchantability or fitness for particular purposes, with respect to the operation or use of AMD hardware, software or other products described herein. No license, including implied or arising by estoppel, to any intellectual property rights is granted by this document. Terms and limitations applicable to the purchase or use of AMD’s products are as set forth in a signed agreement between the parties or in AMD's Standard Terms and Conditions of Sale. AMD is a trademark of Advanced Micro Devices, Inc. Other product names used in this publication are for identification purposes only and may be trademarks of their respective companies. Copyright (C) 2021 - 2022 Advanced Micro Devices, Inc. All rights reserved. Proposal -------- Create a new code generator for rocFFT. Rationale --------- The current code generator: * dates from clFFT * is based on string concatenation Ideally, a new code generator: * based on an abstract-syntax-tree (AST) * generates faster, more robust kernels ASTs allow generated code to be transformed and manipulated before being emitted. A concrete example of this for FFT kernels would be: automatically translating a kernel from interleaved to planar format. How the generator is designed and implemented is crucial for both conciseness and ease-of-use. Required kernels (scope) ------------------------ For rocFFT, we need/want to generate: * Host functions to launch the FFT kernels * Tiled (row/column) + strided + batched Stockham kernels for arbitrary factorisations * May want to extend to Cooley-Tukey kernels as well Kernels need to handle all combinations of: * single/double precision (and be extendable to half-float and bfloat) * in-place/out-of-place * planar/interleaved * real/complex * small/large twiddle tables * unit/non-unit stride * transposed output, including with twiddle multiplies for large 1D * fusing with pre and post-processing kernels (eg real even-length) Ideally any configuration/runtime parameters required by the kernels would be defined in a single place to avoid repetition between rocFFT and the generator. We have flexibility in handling these combinations at compile-time or run-time. For example, multiple kernels could be generated for single/double precision, but unit/non-unit stride could be handled at runtime. Fundamentally, all multidimensional and batched FFTs can be written in terms of 1D transforms (with affine indexing). As such, an FFT is broken down into: * A *host* function that is aware of dimensions, strides, batches, and tiling. This function would be responsible for determining how the problem will be broken down into GPU thread blocks. * A *global* function that is aware of GPU thread blocks, dimensions, strides, batches, and tiling. This function would be responsible for determining offsets and strides for the device function, and declaring LDS memory buffers. * A *device* function that is passed offsets and strides, and is aware of GPU threads. The device function would perform a (short) 1D transform. A device function may be called so that a thread block is actually transforming multiple batches. As such, indexes (the spatial index in the FFT) should be computed as: .. code-block:: c int fft_index = threadIdx.x % width; Tiling ^^^^^^ Launching device kernels in a way that traverses memory in tiles will be handled at the host/global level. Kernels need to support reading/writing in columns/rows. These are the block CC/RC/CR flavours (where C and R refer to column and row) of the existing kernels. Strides and batches ^^^^^^^^^^^^^^^^^^^ Host ~~~~ Host/global functions should support arbitrary dimensions, lengths, strides, offsets, and batches. Users should be allowed to store their arrays arbitrarily. For an :math:`N` dimensional dataset, the flat index :math:`a` corresponding to indices :math:`(i_1,\ldots,i_N,i_b)`, where :math:`i_b` is the batch index, is given by .. math:: a(i_1,\ldots,i_N,i_b) = s_b i_b + \sum_{d=1}^N s_d i_d where :math:`s_d` is the stride along dimension :math:`d`. To support these strides, the device function to compute the FFT along dimension :math:`D` would be passed: .. code-block:: c int offset = 0; offset += batch_index * batch_stride; for (int d=0; d < N; ++d) if (d != D) offset += spatial_index[d] * strides[d]; int stride = strides[D]; For example, in three dimensions, to compute the FFT along the y-dimension given x and z indices ``i`` and ``k`` for batch ``b``, the device function would be passed: .. code-block:: c int offset = 0; offset += b * batch_stride; offset += i * strides[0]; offset += k * strides[2]; int stride = strides[1]; Device ~~~~~~ Device functions should support arbitrary offsets and strides. Array indexes in device functions should be computed as, eg: .. code-block:: c int fft_index = threadIdx.x % width; int array_index = offset + fft_index * stride; Large twiddle tables ^^^^^^^^^^^^^^^^^^^^ Large 1D transforms are decomposed into multiple transforms. To reduce the size of twiddle tables, rotations can be decomposed into multiple stages as well. For example, the rotation through :math:`2\pi \cdot 280 / 256^2` can be decomposed into :math:`2\pi \cdot 1 / 256 + 2\pi 24 / 256^2`. The resulting twiddle table contains 512 entries instead of 65536 entries. Generated kernels should support these "large twiddle tables". Launching ^^^^^^^^^ For a specific transform length, the generator is free to choose among several algorithms and related tuning parameters. These choices may influence how the kernel is launched. The generator will create both the kernel and the accompanying struct, which gives indications of how the kernel may be used in both rocFFT and other applications. the generator will populate a function pool with structs of the form .. code-block:: c++ struct ROCFFTKernel { void *device_function = nullptr; std::vector factors; int transforms_per_block = 0; int workgroup_size = 0; // ... }; This moves the responsibility of figuring how a kernel should be launched to the generator. Currently kernels are launched with: * dimension * number of blocks (batches) * number of threads (threads per batch; kernel parameter) * stream * twiddle table * length(s) * strides * batch count * in/out buffers Implementation -------------- The code generator will by implemented in Python using only standard modules. The AST will be represented as a tree structure, with nodes in the tree representing operations, such as assignment, addition, or a block containing multiple operations. Nodes will be represented as objects (eg, ``Add``) extending the base class ``BaseNode``. Operands will be stored in a simple list called ``args``: .. code-block:: python class BaseNode: args: List[Any] To facilitate building ASTs, the base node will have a constructor that simply stores its arguments as operands: .. code-block:: python class BaseNode: args: List[Any] def __init__(self, *args, **kwargs): self.args = list(args) To facilitate rewriting ASTs, node object's constructors should accept a simple list of argument/operands. This, for example, allows a depth-first tree re-write to be implemented trivially as: .. code-block:: python def depth_first(x, f): '''Depth first traversal of the AST in 'x'. Each node is transformed by 'f(x)'.''' if isinstance(x, BaseNode): y = type(x)(*[ depth_first(a, f) for a in x.args ]) return f(y) return f(x) To emit code, each node must implement ``__str__``. For example: .. code-block:: python class Add(BaseNode): def __str__(self): return ' + '.join([ str(x) for x in self.args ]) Stockham tiling implementation ------------------------------ To support tiling, the *global* function is responsible for loading data from global memory into LDS memory in a tiled manner. Once in LDS memory, a singly strided *device* function performs an interleaved, in-place FFT entirely within LDS. Polymorphism will be used to abstract tiling strategies. Different tiling strategies should extend the ``StockhamTiling`` object and overload the ``load_from_global`` and ``store_to_global`` methods. For example: .. code-block:: python tiling = StockhamTilingRR() scheme = StockhamDeviceKernelUWide() body = StatementList() body += tiling.compute_offsets(...) body += tiling.load_from_global(out=lds, in=global_buffer) body += scheme.fft(lds) body += tiling.store_to_global(out=global_buffer, in=lds) Different tiling strategies may require new template parameters and/or function arguments. Tiling strategies can manipulate these through the * ``add_templates``, * ``add_global_arguments``, * ``add_device_arguments``, and * ``add_device_call_arguments`` methods. Each of these methods is passed a ``TemplateList`` or ``ArgumentList`` argument, and should return a new template/argument list with any extra parameters added. Large twiddle tables -------------------- Device kernels may need to apply additional twiddles during their execution. These extra twiddle tables are implemented similarly to tiling. Different twiddle table strategies should extend the ``StockhamLargeTwiddles`` object and overload the ``load`` and ``multiply`` methods. Twiddle tables may also require additional templates and arguments. See :ref:`Stockham tiling implementation`. upstream/docs/design/bluestein.rst0000664000175000017500000003723714637212425016325 0ustar kaolkaolBluestein Design Document ========================= Copyright and Disclaimer ------------------------ DISCLAIMER The information contained herein is for informational purposes only, and is subject to change without notice. While every precaution has been taken in the preparation of this document, it may contain technical inaccuracies, omissions and typographical errors, and AMD is under no obligation to update or otherwise correct this information. Advanced Micro Devices, Inc. makes no representations or warranties with respect to the accuracy or completeness of the contents of this document, and assumes no liability of any kind, including the implied warranties of noninfringement, merchantability or fitness for particular purposes, with respect to the operation or use of AMD hardware, software or other products described herein. No license, including implied or arising by estoppel, to any intellectual property rights is granted by this document. Terms and limitations applicable to the purchase or use of AMD’s products are as set forth in a signed agreement between the parties or in AMD's Standard Terms and Conditions of Sale. AMD is a trademark of Advanced Micro Devices, Inc. Other product names used in this publication are for identification purposes only and may be trademarks of their respective companies. Copyright (c) 2023 Advanced Micro Devices, Inc. All rights reserved. Summary ------- This document revisits the Bluestein algorithm for prime-length discrete Fourier transforms (DFTs), and presents its implementation in the rocFFT library. An optimization of the Bluestein algorithm for large length DFTs is introduced. This optimization provide several benefits, including significantly improved performance and the ability to reuse the design to perform fast convolutions without any major design modifications. Background and Notation ----------------------- Let :math:`\mathbf{X} = \mathcal{F}\left\{ \mathbf{x} \right\}` denote the DFT of :math:`\mathbf{x}`, which maps an :math:`N`-length input sequence :math:`\mathbf{x} = \begin{bmatrix} x_0 & \cdots & x_{N-1} \end{bmatrix}` into an :math:`N`-length output sequence :math:`\mathbf{X} = \begin{bmatrix} X_0 & \cdots & X_{N-1} \end{bmatrix}` with .. math:: X_k = \sum_{n=0}^{N-1}{x_n e^{-\frac{2 \pi \jmath}{N}nk}}, \qquad k = 0, \ \ldots, \ N-1. Conversely, let :math:`\mathbf{x} = \mathcal{F}^{-1}\left\{ \mathbf{X} \right\}` denote the inverse DFT, which maps the sequence :math:`\mathbf{X}` into sequence :math:`\mathbf{x}` as follows .. math:: x_k = \frac{1}{N}\sum_{n=0}^{N-1}{X_n e^{\frac{2 \pi \jmath}{N}nk}}, \qquad k = 0, \ \ldots, \ N-1. Bluestein Algorithm ------------------- In Bluestein's algorithm, the following identity is considered for the DFT computation .. math:: nk = \frac{-(k-n)^2}{2} + \frac{n^2}{2} + \frac{k^2}{2}. For example, substituting this identity into the DFT equation, the DFT can then be expressed as .. math:: X_k = e^{-\frac{\pi \jmath}{N}k^2} \sum_{n=0}^{N-1}{\left( x_n e^{-\frac{\pi \jmath}{N}n^2} \right) e^{\frac{\pi \jmath}{N} (k-n)^2}{}}, \qquad k = 0, \ \ldots, \ N-1. +++++ Chirp +++++ Bluestein's algorithm is frequently used to compute the DFT, but it can also be used to compute the more general z-transform. This transform is similar to the DFT equation with the difference that the term :math:`e^{-\frac{2\pi \jmath}{N}}` is replaced by :math:`z`, where :math:`z` is an arbitrary complex number. Let :math:`\mathbf{c} = \begin{bmatrix} c_0 & \cdots & c_{N-1} \end{bmatrix}` denote an :math:`N` length sequence of the form .. math:: c_n = e^{\frac{\pi \jmath}{N}n^2}, \qquad n = 0, \ \ldots, \ N-1. The sequence :math:`\mathbf{c}`, which is present in Bluestein DFT equation, is also known as chirp because it defines a complex sinusoid of linearly increasing frequency. Bluestein's algorithm is also known as the chirp z-transform for this reason. +++++++++++ Convolution +++++++++++ Now let :math:`\left(\mathbf{a} \ast \mathbf{b}\right)_k` for :math:`k = 0, \ \ldots, \ M-1` denote the convolution of two :math:`M`-length input sequences :math:`\mathbf{a} = \begin{bmatrix} a_0 & \cdots a_{M-1} \end{bmatrix}` and :math:`\mathbf{b} = \begin{bmatrix} b_0 & \cdots b_{M-1} \end{bmatrix}` with .. math:: \left(\mathbf{a} \ast \mathbf{b} \right)_k = \sum_{m=0}^{M-1}a_m b_{k-m}, \qquad k = 0, \ \ldots, \ M-1. The DFT in the Bluestein DFT equation can be expressed in terms of the convolution sum in the above equation as .. math:: X_k = b_k^{-1} \sum_{m=0}^{M-1}{a_m b_{k-m}}, \qquad k = 0, \ \ldots, \ M-1, with :math:`M=N`, :math:`a_m = x_m / c_m`, and :math:`b_m = c_m` for :math:`m = 0, \ \ldots, \ M-1`. From the convolution theorem we know that, under suitable conditions, the convolution sum in convolution definition equation can be evaluated by computing the point-wise product of the DFTs of :math:`\mathbf{a}` and :math:`\mathbf{b}` and taking the inverse DFT of the product .. math:: \left(\mathbf{a} \ast \mathbf{b} \right) = \mathcal{F}^{-1}\left\{ \mathcal{F}\left\{ \mathbf{a} \right\} \cdot \mathcal{F}\left\{ \mathbf{b} \right\} \right\}. Note, however, that Bluestein's DFT equation in terms of the convolution sum cannot be used to directly evaluate the DFT equation under the values of :math:`M`, :math:`a_m` and :math:`b_m` provided. ++++++++++++ Zero padding ++++++++++++ Consider instead that the DFT in the Bluestein DFT convolution equation is evaluated with .. math:: M \geq 2N-1 and the sequences :math:`\mathbf{a}` and :math:`\mathbf{b}` are zero-padded as follows .. math:: a_m = \begin{cases} x_n / c_n& \text{for $n = 0, \ \ldots, \ N-1$},\\ 0 & \text{otherwise} \end{cases} and .. math:: b_m = \begin{cases} c_n& \qquad \text{for $n = 0, \ \ldots, \ N-1$ \ and $n = M - N + 1, \ \ldots, \ M - 1$},\\ 0 & \qquad \text{otherwise.} \end{cases} In Bluestein's algorithm, the above conditions ensure that the convolution theorem holds and, therefore, the Bluestein's DFT equation can be properly employed for the DFT computation. +++++++++++++++++ DFT via Bluestein +++++++++++++++++ Based on the two conditions for the sequences :math:`\mathbf{a}` and :math:`\mathbf{b}` obtained above, and the convolution theorem, the DFT can be computed as follows in Bluestein's algorithm .. math:: X_k = b_k^{-1} \mathcal{F}^{-1}\left\{ \mathcal{F}\left\{ \mathbf{a} \right\} \cdot \mathcal{F}\left\{ \mathbf{b} \right\} \right\}, \qquad k = 0, \ \ldots, \ N-1. There are quite a few operations involved in this computation. More specifically, computation of the chirp sequence, two :math:`N`-length plus one :math:`M`-length point-wise multiplications, zero-padding of two :math:`M`-length sequences, and two forward DFTs of length :math:`M` plus an inverse DFT also of length :math:`M`. The main reason for using Bluestein's algorithm is that it applies for the DFT computation of any input length :math:`N`, including prime lengths. When a fast Fourier transform (FFT) algorithm is used to compute the DFT, such as Stockham or Cooley-Tukey, it provides optimized length support via a given radix or combination of radices, e.g., :math:`N = 2, \ 3, \ 5, \ 25 \times 2, \ 16 \times 9`, and so on. Considering that the DFTs via Bluestein can be carried out with any length satisfying :math:`M \geq 2N-1`, a suitably chosen value of :math:`M` can be used to compute the convolution via an FFT with existing radix support. However, it should be mentioned that the Bluestein DFT computation is much slower than directly computing the DFT equation via an FFT with a supported length, even though both computations posses the same complexity of :math:`O(N \log N)`. Implementation -------------- An illustration of the steps required for Bluestein's algorithm is given in the figure below. .. figure:: images/bluestein_fig1.png Diagram of computations involved in Bluestein's algorithm A few observations can be made from the block diagram. First, it can be seen that there are no direct dependencies between the two branches that compute :math:`\mathcal{F}\left\{ \mathbf{a} \right\}` and :math:`\mathcal{F}\left\{ \mathbf{b} \right\}` and, therefore, parallelization may be leveraged to speed-up the computations and perform the two sequence of operations independently. Second, it can further be seen that the chirp sequence is used multiple times throughout the diagram. Re-utilizing the computed chirp sequence across the operations where possible may also be advantageous. Third, there are quite a few number of operations in the diagram, and it is, therefore, often preferable to put together these operations into as little as possible device kernels, due to the overhead of kernel launch. +++++++++++++++++++++++++++ Device Kernel Configuration +++++++++++++++++++++++++++ Important factors to consider when designing an efficient implementation of Bluestein's algorithm are (1) the length of the DFT to be performed, (2) the size of available shared memory for the compute device at hand, and (3) the latency for launching device kernels. For instance, when the DFT length is small, all the operations in Bluestein's algorithm may be performed in a single device kernel, if data can fit into shared memory. This minimizes kernel launching overhead and provides the best performance. In the case where the DFT length is large and the entire data does not fit into shared memory, a hierarchical approach is utilized where the large FFT is decomposed into smaller FFT device kernels that fit into shared memory for improved performance. In this large length DFT scenario, it is important to minimize the number of device kernels utilized in the implementation for reduced kernel launch overhead. The default implementation for Bluestein's algorithm when applied to large length DFTs is illustrated in the diagram below. .. figure:: images/bluestein_fig2.png Default device kernel configuration for Bluestein's algorithm and large length DFTs As can be seen from the diagram, Bluestein's algorithm is performed with (at least) six kernels in a single device stream. The chirp sequence is computed in a single chirp kernel, and the sequence is re-utilized at later stages via a temporary device buffer. The two forward DFTs are joined together in one fft device node. This is possible because the padded sequences :math:`\mathbf{a}` and :math:`\mathbf{b}` are contiguous in the temporary device buffer used in the implementation, thus allowing for a single fft node to perform the two fft operations. The inverse FFT operation requires a separate ifft device node. Similarly, the three point-wise multiplications are carried out with separate kernels, pad\_mul, fft\_mul, and res\_mul. Note that the fft (or ifft) nodes are usually split into at least two device kernels for large length DFTs. For example, a large 1D input data vector is viewed as a matrix (with same number of elements as the large vector), and the first FFT device kernel operates on rows of the data matrix while the second device kernel operates on the columns of the data matrix. In this scenario, a total of 8 device kernels are used to perform Bluestein's algorithm. ++++++++++++++++++++++++++++++++++++++++++ Optimizing Bluestein for large length DFTs ++++++++++++++++++++++++++++++++++++++++++ The default implementation of Bluestein's algorithm for large length DFTs can be optimized by following the design principles: #. Use the convolution as a building block for the implementation. #. Minimize the number of device kernels by fusing FFT read and write operations with Bluestein operations. #. Move computation of the chirp sequence from the FFT execution phase to the plan creation phase in rocFFT. The convolution building block is shown in the diagram below. .. figure:: images/bluestein_fig3.png Proposed configuration of device kernels for fast convolution In the building block, two independent FFT device nodes are used to carry out the forward DFTs. The point-wise multiplication of the two forward DFTs is fused with the read operation of the iFFT device node. Arranging the convolution in this configuration has two advantages. The independence of the two forward FFT nodes means that parallelism may be leveraged, since the two foward FFT nodes may be executed concurrently if required. Fusing the point-wise multiplication of the two foward DFTs means that a separate kernel for performing the point-wise multiplication is no longer required, thus reducing device kernel launch latency. A typical use case of the rocFFT library is to create an FFT plan device handle once, and perform FFTs on multiple input data using this same plan handle. As shown in the diagram of Bluestein's algorithm, the chirp sequence :math:`\mathbf{c}` is independent from the input sequence :math:`\mathbf{x}`. Since the execution phase of rocFFT depends only on the input sequence, it is advantageous to precompute :math:`\mathbf{c}` at the plan creation phase of the library. That way, it is not always required to compute :math:`\mathbf{c}` when an FFT is executed, thus reducing the overal amount of computations. Based upon the three design principles above, an optimized implementation of Bluestein's algorithm is described in the diagram below. .. figure:: images/bluestein_fig4.png Proposed configuration of device kernels for Bluestein's algorithm As can be seen from the diagram, the implementation of Bluestein's algorithm is quite similar to the fast convolution implementation. The main difference between the two implementations is that the foward/inverse DFT stages have additional fused operations in them. Compared to the default Bluestein implementation, at least three device nodes are used in the optimization. When using the row/column FFT decomposition for large lengths, this brings to a total of 6 device kernels in the optimization, a significant redution in the number of kernels compared to the default configuration. The read operation of the first DFT stage is fused with chirp + point-wise multiplication + padding. The read operation of the second DFT stage is fused with the chirp + padding. Similarly, the point-wise multiplication of the two forward DFTs is fused with the read operation of the inverse DFT node, and the chirp + point-wise multiplication is fused with its write operation. Since the chirp sequence is computed at the plan level, the chirp operations are performed by simply loading the computed chirp table into device registers. Parallelization of the first two FFT nodes can be employed in the optimized implementation, however, preliminary tests have shown that in practice not much performance is gained by executing the two nodes simultaneously. The main reason for this is due to the fact that a synchronization step is required after the two forward DFT stages. This is denoted by the thin solid rectangle in the diagram. Another factor that needs to be taken into account is that in practice the amount of computation performed on the second FFT node is usually much smaller than the first FFT node. A typical use case of the rocFFT library is to perform batched FFTs. In this scenario, the amount of computation in the two forward FFT nodes is unbalanced since multiple FFTs are performed on the first node while only a single FFT is performed on the second node. This unbalance between the independent nodes makes the benefits of parallelization less pronounced. One last technical aspect of the optimization is the need to have separate transform and contiguous data indices across the multiple FFT nodes. Since the FFT nodes decompose a large length FFT into a column and a row FFT, the device kernels need to keep track of a global transform index to properly perform the fused read/write Bluestein operations. A similar concept is required for the data index, as the temporary buffers utilized for the computations are accessed in a contiguous fashion for minimal storage requirements. upstream/docs/design/design.rst0000664000175000017500000000025514637212425015572 0ustar kaolkaol================ Design Documents ================ .. toctree:: :maxdepth: 3 :caption: Contents: codegen runtime_compilation buffer_assignment bluestein upstream/docs/design/runtime_compilation.rst0000664000175000017500000003540714637212425020411 0ustar kaolkaolRuntime Compilation Design Document for rocFFT ============================================== Copyright and Disclaimer ------------------------ DISCLAIMER The information contained herein is for informational purposes only, and is subject to change without notice. While every precaution has been taken in the preparation of this document, it may contain technical inaccuracies, omissions and typographical errors, and AMD is under no obligation to update or otherwise correct this information. Advanced Micro Devices, Inc. makes no representations or warranties with respect to the accuracy or completeness of the contents of this document, and assumes no liability of any kind, including the implied warranties of noninfringement, merchantability or fitness for particular purposes, with respect to the operation or use of AMD hardware, software or other products described herein. No license, including implied or arising by estoppel, to any intellectual property rights is granted by this document. Terms and limitations applicable to the purchase or use of AMD’s products are as set forth in a signed agreement between the parties or in AMD's Standard Terms and Conditions of Sale. AMD is a trademark of Advanced Micro Devices, Inc. Other product names used in this publication are for identification purposes only and may be trademarks of their respective companies. Copyright (c) 2022 - present Advanced Micro Devices, Inc. All rights reserved. Summary ------- This document describes runtime compilation (RTC) as it is used in rocFFT. Runtime compilation helps reduce binary size and build times for the library, and can allow for optimizations that are not practical versus ahead-of-time compiled kernels. Problem ------- Stockham FFT kernels make up the vast majority of the rocFFT library. Kernels handling specific problem sizes are chosen as part of the rocFFT build process, and are compiled for all of the variants that might be required at runtime. The count of variants for each problem size has a number of stacking multipliers applied to it. Any given problem size needs variants for: * Each supported GPU architecture; * Six interleaved/planar and in-place/out-of-place variants; * Forward and inverse transforms; * At least two precisions (single/double); * Unit- and non-unit- strides; * With and without callback support. Runtime compilation has advantages over pre-compiling all of the above variants for all problem sizes. These include: * Handling of new problem sizes does not require rebuilding the library. * Build times are faster. * The library binary is smaller. This in turn means: installation is faster; and difficulties arising from limited memory addressing in shared objects (the default memory model for shared objects built with ``-fPIC`` only allows for 2 GiB binaries, resulting in `build breaks`_) are reduced. .. _build breaks: https://www.ibm.com/support/pages/intel-compiler-error-relocation-truncated-fit-rx8664pc32 Solution -------- HIP provides runtime compilation facilities, in the hiprtc.h header. The code generator is embedded into the library itself. During FFT planning, we can run the code generator to produce suitable FFT source code for the specific problem being solved. Then, we can runtime-compile that source into GPU kernels that we launch at FFT execution time. Empirical testing of runtime compilation on our FFT kernels shows that compilation times for single kernels range between 0.5 and 2 seconds on modern hardware, with more complicated kernels taking more time. Kernel execution time is identical to the ahead-of-time compiled version. Implementation -------------- Embedding and running the generator ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ A generator implemented in C++ can be built into the library like any other C++ code that makes up the library. During plan building, we can execute the generator code to produce a string of source code for the required problem sizes. The generator needs sufficient input to have it produce exactly the variant that is required, e.g. length-336, inverse, out-of-place, interleaved-to-planar, double precision, etc. Compilation ^^^^^^^^^^^ Compilation should be done during plan building, and the generated kernels can be attached directly to the ``TreeNode`` for that step of the FFT. If the kernels are available on the ``TreeNode``, we will have less overhead at execution time, since we don't need to do any work to find the right kernel to run. Caching kernels at runtime ^^^^^^^^^^^^^^^^^^^^^^^^^^ If a process needs to create multiple plans that would compile the same FFT kernel variant, it's nice to reuse kernels we've already compiled. Reusing an already-compiled kernel would save compilation time on subsequent runs. Compiled kernels may be persisted to disk for maximal reuse. However, rocFFT may be used in distributed systems where the filesystem is shared among multiple compute nodes, and having multiple nodes all contend for the same shared file is problematic for performance. By default, kernels are only cached in memory, to prevent this contention. The cache location may still be overridden at runtime using mechanisms described below. Cache keys :::::::::: The cache keys need to be chosen carefully to ensure that an obsolete kernel is not reused when a new version really **should** be recompiled. The kernel function name shall be the main key field in the cache. The function name shall encode all of the parameters by which kernel functions could differ, including: * scheme (e.g. whether this is a standard Stockham kernel, or a variant that does different read/write tiling) * length (typically 1D length, may be 2D or more for single kernels that handle higher dimension transforms) * placement (in-place or out-of-place) * direction (forward or backward) * input and output formats (planar, interleaved, real) * precision (single or double) * stride type (unit stride or non-unit stride) * large 1D twiddle type and base (for kernels that need to integrate a twiddle multiplication step) * callback type (run callbacks or don't run callbacks) Encoding all of these parameters into the kernel name is necessary anyways, so that logs and profilers will tell users and developers exactly which kernel is running, even if it's been runtime-compiled. Using just the kernel name as the main key is also helpful because the caching code needn't be aware of all the possible parameters that kernels could differ by. New parameters can be added at anytime, and as long as the kernel names are updated accordingly, the cache will just work. The cache will also need to store other key fields to ensure that a kernel is compiled if any of these changes: * GPU architecture * HIP runtime version * Kernel generator version Practically, these key field choices will ensure that users are always running the latest kernels that rocFFT provides and which are appropriate for the hardware present. User control of cache ::::::::::::::::::::: Distributed workflows will want additional control over the cache. For example, a workload that distributes FFT computation over a large number of MPI nodes will want to ensure that the kernels are built once centrally rather than by each node. MPI nodes might also have no access to disk (either shared with other nodes or local to each node). rocFFT needs to expose APIs to: * Serialize the current cache to a library-allocated buffer * Free the library-allocated serialization buffer * Deserialize a buffer into a cache (which might need to be in-memory for diskless nodes) The example MPI computation described above would be able to build plans on the rank 0 node to populate the cache once. Then, it can use these new APIs along with MPI APIs to distribute the cache to each work node. Backing store implementation :::::::::::::::::::::::::::: The cache may be written to disk, and if so it must be robust in the face of concurrent access, crashes during library operation, and so on. We really would like the cache to have ACID properties of database systems. The easiest way to achieve this is to use SQLite to manage the storage. It's easily embeddable in our library (or is readily available as its own library), and provides all of the properties we'd want for the storage backend. It also provides APIs to serialize a database, as required for the distributed workflows described above. Pre-built kernels ::::::::::::::::: Even if rocFFT is prepared to runtime-compile any FFT kernel, we can still pre-compile kernels by populating a cache at library build time and shipping the cache with the library. Cache location ~~~~~~~~~~~~~~ The main challenge here is installing this pre-built cache in a place that the library will be able to find. The easiest solution here, as employed by `other math libraries` is to look for this the cache file relative to the shared library itself. .. _other math libraries: https://github.com/ROCmSoftwarePlatform/rocBLAS/blob/d8e00e169ccc7ca21211705643e85545e98e455a/library/src/tensile_host.cpp#L521 Environment variables can override the locations of caches used by rocFFT. During normal operation, we would expect one read-only cache shipped with the library and one modifiable cache updated as the user runs transforms that use new kernels. We support two environment variables for these two locations: * ROCFFT_RTC_SYS_CACHE_PATH - the pre-built read-only system-level cache. * ROCFFT_RTC_CACHE_PATH - the read-write user-level cache. Note that if the library is linked statically, we will not be able to find any files relative to the library. The ROCFFT_RTC_SYS_CACHE_PATH environment variable will then be required for rocFFT to find the system-level cache, but rocFFT will still update the user-level cache and have correct behaviour without a system-level cache. Populating the cache ~~~~~~~~~~~~~~~~~~~~ Populating this shipped cache is done via a helper executable that is built and run during the rocFFT build. A separate helper executable (which is not itself shipped with rocFFT) is necessary so that it can share rocFFT's generator and RTC code, without requiring rocFFT to expose extra symbols just for this task. This helper should work at the kernel level, e.g. build Stockham kernels for all desired combinations of: * supported architectures (gfx908, gfx90a, gfx1030, etc.) * precisions * problem sizes * array formats * etc. The criteria for which kernels to pre-build can be arbitrary. Less common choices will be runtime-compiled, and runtime compilation is still a fallback in case a pre-built kernel is not available for whatever reason. An inferior option would be for the helper to work at the plan level (i.e. use rocFFT to build a set of plans and save the resulting RTC kernels). However, creating plans involves doing a lot of other unnecessary work, like generating twiddle tables and deciding on buffer assignment. Impact on tests ::::::::::::::: Accuracy tests are maximally affected in terms of runtime by this change, since they run a huge number of problem sizes in the context of a single process. That means the costs of generating and compiling a large variety of kernel variants will be the most painful here, once more problem sizes are handled by the new generator. An increase in test runtime is an unfortunate side effect of runtime compilation. This cost is made more acceptable because the compile time of the library has already been reduced prior to running the tests. A possible solution here might be to do a parallel traversal of the test cases, building rocFFT plans for each of them (but not actually executing plans). This would runtime-compile the whole suite's kernels in parallel, which would save a lot of time. Interaction with callbacks ^^^^^^^^^^^^^^^^^^^^^^^^^^ Callback-enabled FFTs require a different kernel variant to be generated, but the decision of whether to actually run with a callback is made by the user after the plan is constructed. To solve this, we generate both a callback and non-callback variant where necessary during plan creation. Parallel compilation ^^^^^^^^^^^^^^^^^^^^ Because of the potential need for callback-enabled kernels, most plans will be generated faster if kernels can be compiled in parallel. Unfortunately, hipRTC has process-wide locks in it that prevent useful multithreading of compilation. Instead, we can spawn a helper process for subsequent compilations if a compilation is already in-progress in the original process. This helper would need to be shipped with the library, in a location that's knowable by the library. If we fail to find or spawn that helper, compilation must fall back to compiling in-process. Code organization ----------------- The whole of rocFFT runtime compilation can be broken down into separate subsystems: 1. Generating source to be compiled, further subdivided into generators for each type of kernel (Stockham, transpose, Bluestein, etc). Input specifications of the desired kernel include problem size, precision, result placement, and so on. Files to implement this are named: * rtc_stockham_gen.cpp * rtc_transpose_gen.cpp * etc. 2. Compiling source code into object code, which can be further subdivided: a. Compiling code in the current process b. Compiling code in a subprocess The files to implement these are named: rtc_compile.cpp rtc_subprocess.cpp 3. Reading/writing the cache of compiled object code. The file to implement this is named: rtc_cache.cpp 4. Compiling and launching the correct kernel for a TreeNode in an FFT plan. This subsystem would need to derive the correct input specifications for the generator, given the data in the TreeNode. It would also need to derive the correct launch arguments to pass to the kernel. Files to implement this are named: * rtc_stockham_kernel.cpp * rtc_transpose_kernel.cpp * etc. These files are named rtc_*_kernel.cpp because they implement subclasses of the generic RTCKernel type. In this list, 1 and 2 are independent. 2b depends on 2a. 3 depends on 1 and 2. 4 depends on 3. 2a requires the hipRTC library, 3 requires the SQLite library, and 4 requires the full HIP runtime library (amdhip64). Build-time processes that populate a cache to ship with the library depend on 3. The helper process to support parallel compilation depends on 2a. It's important to avoid using the full HIP runtime at build time - Windows build environments in particular may not have the sufficient libraries or infrastructure to successfully load the full runtime, but they are able to load hipRTC. Future work ----------- Moving away from chosen problem sizes ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Once the infrastructure is in place, we could consider enabling runtime compilation for all FFT sizes, not just those that are chosen ahead of time. The generator is already able to auto-factorize arbitrary sizes, though we haven't yet tested the limits of this ability. upstream/docs/design/images/0000775000175000017500000000000014637212246015033 5ustar kaolkaolupstream/docs/design/images/bluestein_fig3.png0000775000175000017500000014030714637212246020453 0ustar kaolkaol‰PNG  IHDRj_¾2V#&iCCPkCGColorSpaceAdobeRGB1998(‘c``RH,(Èa``ÈÍ+) rwRˆˆŒR`ÎÀÊ Á ÀÀÈ œ˜\\ààÃ0|»T—uAfaÊã\)©ÅÉ@úg'•000fÙÊå% v-’” f/±‹€²·€Øéö °ûXMH3ýÈæK³™@vñ¥CØ 6Ô^tLÉOJUù^ÃÐÒÒB“D?%©% Ú9¿ ²(3=£DÁR© žyÉz: FFÆ  p‡¨þOF±31@ˆÍ‘``ð_ÊÀÀò!fÒËÀ°@‡*BLÍA@ŸaßœäÒ¢2¨1ŒL@; ñ zJMp&Éç8eXIfMM*‡i j _7@&@IDATxìÝ ücÿÿñO›V¥¢P¢”¥$”Èš=kvÙ©û¶Ç}#ÜȾ»-‰ÜnٵٲE‘"*´i§(BI{ü½çþÍ÷ÎùÎr–9Û÷¼®ÇãtÎÌ\sÍ5Ï9Ë·ù\K¥?ÿJFB@@@@@@È™@圉!€ € € € € € €8jy# € € € € € € €9 P›cp‡ € € € € € €¨å=€ € € € € € €äX€@mŽÁ9 € € € € € € @ –÷ € € € € € € cµ9çp € € € € € € €ZÞ € € € € € € €@ŽÔæœÃ!€ € € € € € €jy € € € € € € €9 P›cp‡ € € € € € €¨å=€ € € € € € €äX€@mŽÁ9 € € € € € € @ –÷ € € € € € € cµ9çp € € € € € € €ZÞ € € € € € € €@ŽÔæœÃ!€ € € € € € €jy € € € € € € €9 P›cp‡ € € € € € €¨å=€ € € € € € €äX€@mŽÁ9 € € € € € € @ –÷ € € € € € € cµ9çp € € € € € € €ZÞ € € € € € € €@ŽÔæœÃ!€ € € € € € €jy € € € € € € €9 P›cp‡ € € € € € €¨å=€ € € € € € €äX€@mŽÁ9 € € € € € € @ –÷ € € € € € € cµ9çp € € € € € € €ZÞ € € € € € € €@ŽÔæœÃ!€ € € € € € €jy € € € € € € €9 P›cp‡ € € € € € €¨å=€ € € € € € €äX€@mŽÁ9 € € € € € € @ –÷ € € € € € € cµ9çp € € € € € € €ZÞ € € € € € € €@ŽÔæœÃ!€ € € € € € €jy € € € € € € €9 P›cp‡ € € € € € €¨å=€ € € € € € €äX€@mŽÁ9 € € € € € € @ –÷ € € € € € € cµ9çp € € € € € € €ZÞ € € € € € € €@ŽÔæœÃ!€ € € € € € €jy € € € € € € €9 P›cp‡ € € € € € €¨å=€ € € € € € €äX€@mŽÁ9 € € € € € € @ –÷ € € € € € € cµ9çp € € € € € € €ZÞ € € € € € € €@ŽÔæœÃ!€ € € € € € €jy € € € € € € €9 P›cp‡ € € € € € €¨å=€ € € € € € €äX€@mŽÁ9 € € € € € € @ –÷ € € € € € € cµ9çp € € € € € € €ZÞ € € € € € € €@ŽÔæœÃ!€ € € € € € €jy € € € € € € €9 P›cp‡ € € € € € €¨å=€ € € € € € €äX jŽÇá@@@@¼¬]»Ö&NœèøÀ ÄΞ=»¢ŸjÊç7wî\›3gNÙcÒ¤I6nÜ8[¶lYÊeËß|ó}ôÑG6oÞ<Óùë¡×úü) ]½zukܸ±m¾ùæÎs³fͬK—.No↠ËiRO@@" P[$Šj"€ € €Tl÷ßßN=õÔœd:uœawÝuWÓc=ö°vØ!gÇÏÅî¹ç»üòË\.ŽWlÇhݺµ­Y³¦ØªV} ½é¦›ìÙgŸu7ø" 87zbW®\ÙvÙe;ðÀíˆ#ްN:¹›yF@@ mµiÓ±# € € €Å+°bÅ ;v¬ópÏb¯½ö²¾}ûÚ1Çcnï]w[1=¯[·Î.ºè"{üñÇ‹©ÚÔ5 êIý¯ýËþóŸÿhí޶îüÆ÷Þ{¯ýöÛoV¥J•°ÝØŽ € €@ Ú@6"€ € € Î;Ûý÷ßoK–,±É“'›zØ*˜šË¤!aõhÚ´©=ñÄNïÁ\?ŠcýüóÏÖ³gÏ =ŸjN*ãïÿ»ÉKC5Ê~ýõרŠ.WŽÆ ³?þØ.»ì2ghár™"^¡ÏÐÁlšÖ+©—¬'¨'ù¶ÛnëXè³§ÇâÅ‹½vqÖixrí›NʇC:õd@@ܨÍ3GA@@@ P@¢>}ú”åùú믭mÛ¶ö矖­K|¡} d›l²‰3÷¬¶/_¾Üy(ø¦!\?ûì3§' ÊÓ¼µÉ¤ï¾ûÎ9ä»îºëìÚk¯M;(•̱¢Ì£óíÞ½»i˜ÛØÔµkW{ôÑG`ÜÌ™3³GyÄV®\éÌI›·”^0 ìt˜ìرcÜ¿e3xñË/¿ØàÁƒFšVIs½öë×/ƒRÃw6mšÓ3Ü/HÛ­[7»ûî»á¿½JS]Õ»|äÈ‘å6ës™êüÎùr(WyV € €”Ú‚ºT@@@ÿ ì´ÓNÖ²eËrAÇXŸ­¶ÚÊ„L&­ZµÊ^yåS€RC‡%õüëß¿¿}ùå—öâ‹/¦˜ +?êí ¬)ø6þü¸¢kÖ¬i¯½öšÕ®]ÛY/×»îºË®¾új[¸p¡µk×..©.4nÜØ™«XÁîL“¨7ëÀíÕW_µÕ«WÇ©@m6“z¥÷èÑ÷‡ðUW]e±Aj¯ºl³Í66bÄ{úé§žÇ ´ºiçwv_>çÛ!°rlD@(ôÆi)ˆªS @@@*¶@³fÍ";A,O>ùd3fŒ|կɤ—_~Ùn¸á†d²æ-‚Ê'žxb¹ ­*´Ï>û”ic+¸é¦›¤ùëuݺuÖ¤·xÞyçÙþûïoC† )¤U‰ g3)ïöÞME&½ÚonZ°pá¤ËĮù¢ëÕ«ç¬ë)ëîtžÙvpëÀ3T ýþQ1Î’³@LŽ:ê(;å”S2-†ýs,@ 6Çà@ ÿ+V¬° –öù¯!5@ ô¶Øb‹Ò;iÎ( žÎ<óL»ï¾ûk5fÌ[»v­m´ÑFùr½Qß‘ G hÈîlö¦•ÀÏ?ÿì ñ믿ÚðáÃíˆ#ŽðÍã·aàÀ¦G*)Ÿ©Ô“¼ PøÖû…¨!ùhÑ¢E¾«ÀñÓ P›» € € € PÑ4âHX Vó½~úé§¶×^{¥|ú‹/¶×^{ÍÔãqþüù¦yo·Ûn;Û~ûím‡v°V­ZYÍš5S.W;¬[·.­ýRÝIsŸ~ñÅ6gΛ={¶-]ºÔš4ibº)Ö²eKÛyç-ŠÆG2:t¨c}íµ×ZëÖ­Sªêï¿ÿno¾ù¦3Tu‡ìüóÏOiÿL3 /6p¦Ç®\9x–¯sÏ=×Ú·oo¹;› :ëó4jÔ(ëÑ£‡üñqt“&M²7ÞxÃô¬a›»téb'žxb\ž …l~^c›«Ï”Žùã?:ú÷Þ{Ï®»î:ç»'¶.£Gvz,.X°À~ùå§uóæÍ­M›6ŽqµjÕb³Ç½Öwã‹/¾hÓ§O·o¿ýÖÖ¬YcuêÔ±víÚY×®]­mÛ¶öÞŒ+@(µ%s©9Q@/Ýиÿþû½6±r °Ç{87ësp( " @£³fÍ Ì©¡S ÔN™2Å àôRo\¿¤ í5×\cW\q…U¯^Ý/[^Ö+€s÷Ýw;ÃC+Àì—ÔÓXsþ*¸Ú A¿låÖ«G°zK}ðÁN€Vfn:äCBµªÓÔ©S “‚wo¿ý¶)p¤¤ r®µnÝóñ¬à¿Îß/)© ™®§z‘KðlÆ ¦÷…zµk˜r½'Ý ÚæjuÝu½GðÑG%¨ÍÕç5ÛŸ)¼>_~ù¥ÓhaĈ6nÜ8“•’¼ÔHDé믿¶Þ½{Ûøñãe¯5jd7Üpƒçgé©§ž²«®ºÊ–,Y⵫³N 8þûßÿZ·nÝ|ó°d>ûì3kÚ´i²Ùɇ\@(?üð ~–ûôÔVìëËÙ!€„è†`¶‡_ ©›(i ž&% ÃÉ#€yPš°@­zÃ&“ÔãO½Ö~øá²àHÐ~«V­rœ z¨!]²sá6ÌÔÎ/)@£í j)ˆµråJÓ±ô\µjU§×ªß¾Ÿ|ò‰õíÛ70Oì¾ DkAƒ‚2~çðÒK/9A7õ¾ÓCAV7€[žßkŸÔ“rÆŒÎþZ–w¡$õ’ôKšX6šWXs"»Ï l+ÀiàT½³Ã’Ž{öÙgÛõ×_o'œp‚óØ}÷Ý3>vâq3qÐMWîõþÐuÖ5›‹YïoÍM÷Ö[o%V%´·z®>¯ÙúL鄟þyçûKÔ+]Ø à©ö2dˆsÎ9¦^èAIå\pÁNoÙ›o¾ÙÉ*ï>}ú$5$ö÷ßop€=ðÀN9AÇba›m¶÷1ÂØŽ@ ¤Ò8°„XŠêT ÔÕ墲 € € •€nJ+XSQ“æ­]»vE==Î+K;vt‚hAÅÍêî§`ÌÑGm?üðƒ»*ég )¬¡\_yå§ ¿gΜiW^y¥)P”¬ÑÃ+Õ«WÏkµýôÓOÖ¯_?4hÓ+Ï3SÀJ}¿œvÚiN0vË-·,—S½n5 kºIÎÛn»-Ýݳ¶ßÂ… ‡êñé—4,ì)§œâ¹ù’K.1}we’4üt²IõUÏZ=t“s¿ýö³ý÷ßßyh(îtS>ø =óÌ3IWA Ž9æÏ ­ ©U«–oY¹ø¼fû3¥“;ýôÓ!‡}O4fƒU¨†§©¤[n¹Å:è gèìž={Ú矞ôîjŒq饗:ßo¹z;銑@ò*@ 6¯ü@@ò% a5×dEMêɧޑ$RÐ\«a),P«jguVÙлnyêtÓM7™z.ª'Ú»ï¾ë,¯_¿ÞÍ÷|ÞyçYçÎËõRoÈË.»Ìéµê·o\A š«Ô+=òÈ#öÄOxmJzæ¯=ãŒ3œž¯IïT¤5Äìã?î [­ë“NÚd“M2Ò긚‹uÏ=÷t†¹M¥z_«§³J ¤)h«y]Õ²R¥J¡ÅåËá?þpÞkš“Ö/ùj³ýyuëShŸ)w¨h·~©<vØaNo|Íc›jÒwŸ€ôïß?Õ]É €T€@m½°œ € € €© øõ0-'hˆÝ:ÃˆÆæ×ëÍ7ßÜÞÿ}‹–VóÜj®ÐãŽ;ÎsØ_õ8դÇ+NC3ë8Q$¿@mbÙêA§`]“&Mœág§M›æ ³ºhѢĬqËš+UCEo½õÖqëUÍ•ª!™5ÌsªÁÍSO=Õ™·V"õºœ8qb\ù¹^PoIÖ3IÍ›7Ïd÷¸}ïºë.'È·2Å]›'Ÿ|Òy¨nšÃT ¼zH»EGé Þžêá«òc=fêAî—l K^Ú\|^ýêõgJÇQOs5ÐÐÇê)«áÍ“Iõë×w>£.yÙ²e¡»$AêþêqN 6”™  €”ŒÚ’¹Ôœ( € € €ÁêÕ–ÔkÐ+)@¢¡ˆ½’†´ Òºy4<² o¾ù¦»*îYóË~õÕWÖ¦M›²õ­[·v‚‚ºTõUDsáú%õŒÔ0Æ^iï½÷öZ·N=Ó½ê§ùTœ|ñÅãò'.hnÑÄ@íGQ–MÃ<«h*i§v2=”4G¦ÎrÊWÒuÐ\¯ TêõË/¿X•‹/¾¸ÜöwܱܺtWÈó¢‹.²‡z(Ý"âöÓ\³ºÞ·ß~»ýç?ÿ±#<2n»»¥C‡L%y5kÖ,£á²µ¹ú¼º6±ÏÙøL©| Eì¦nݺÙÁì.z>«aÊ­·Þê|Ž57²¾Û^{í5SCˆ )naºÞÚÿÜsÏuæ7Nv}'¨t¦ó1»õà@Š[€@mq_?j € € €@d5kÖ -kÓM7õÌsýõ×›×P  FôêÕËs­<餓<¡î“'OŽ ÔªŽF56½ð ¾Jû‚ .ˆÍžÒk™½’ÄêÁ÷ÙgŸ™zâù¥)S¦˜†JõKnÀÕo{Øú*Uª8Að|j«U«fÏ=÷\YU°¿ãŽ;Ê–c_´k×.åyAc÷Oöµæxmܸq`?Ù²Ü|êI©Æz¯«q@âpÈÙr¨^½ºµlÙ24P« šCUsM«×÷€Êæ¬M ÔæêóêÚÅ>gû3¥cÅ6îˆ=¶ûºF¦¡¢cIèz곪©vÙe—Àù©Ýý;uêäé¼´¿FØu×]}÷W#õ´ßf›mÊöå €”®@åÒ=uÎ@@@bV¬X»èùÚkè×ï¾û®\ðÔÝYȦM›º‹åž»wï^n]ì õ>ËuÒܤ§všÓÛ7¶—^b=˜ ÂTCè¥ÄZP^¿mQ”áWv1¯W/X ­ù‘£Jê5©kþðÃGUdRå„]ãcŽ9Æ&Mšd‡~¸ Þwß}mèС¦ž¥ú Æ6ÈÇç5—Ÿ)†y©ll6ö"¨1zº%õ¦ ÒÆæUwÍ”–,Y´™m € PBô¨-¡‹Í©"€ € €@°@ÕªU!DƒsÖÖ5kÖX>Y…¥@m¢ðê›X¶W Vs±nذ!1«³¬y]ƒR£Fœžh~C*«Gj®“8~AœÄºrÈ!‰«â–Ì Jê«^Ç 5Ý´ÑF¥»k…ßïä“O¶c=Öž}öY»ï¾ûL=´£Hꑪ²5üm.RÐ5V/wõîÖ{)6©×ç{ï½»ÊyÏk.?S:É /m×ÙAIÃk.Y¿”ÌþFÙ/ýþûï~›X €%&@ ¶Ä.8§‹ € €þ –l±Åþ p˪U«Ôàu)Ö*%3/£WàÕ+äxåw·éY $4h`š3Ó+-Z´ÈkuÁ¬SoMÍu¹lÙ2Ï:)X–‡Ð ËŸ¸=ÓýË«hË:øÌ3ÏtŸ|ò‰ 6Ì Âe2\´Þ¯7ß|³ïÏQ]ã.]ºX2ï3·N…þyÍÅgJ”8¯tbÞL÷×o7 @ZÞ € € € àÌ™3'Tbï½÷.—gúôéåÖ¹+† bcÇŽ5Í-[»vmçY½o¨p~AZ·ŒB{VPvâĉÎü´š£vùòå¾U èøîȆ¬h¸Z=n½õV›7ož1Ây|ðÁ¦¹CSIo¾ùfεAõJfnéØý ñóZhŸ©°&±ž^¯ÃöϤ½×ñX‡ €Å+@ ¶x¯5G@@@ RqãÆ–§^fmÚ´)—'(кråJ›1cF¹}Ši…ÎO½ß}÷]=z´Íœ9Óü†jN<¯°!Xó³œ;æÍ›[Ÿ>}œÇo¿ýf£Fr†>|¸ïPÞ±µ›5k–3dµFc(¦TŸ×BÿLe:¤u¦ûÓû‰º"€ €@fj3óco@@@*Œ@X ¶GžçúÓO?y®Ïd¥z¢*(6l&ÇÚwýúõNOËGyÄÞyç´ç%P¤\8Û6ÞxcëÙ³§óX°`ýío³¡C‡VpõêÕ6þ|Ûf›móÚÆ|}^‹é34Ôt¡]Oêƒ €Å-@ ¶¸¯µG@@@ õ]¸pa`Y dy¥uëÖy­.[w '”½N|¡€¬zŸÕ¯_ßynÔ¨‘µmÛÖZ·níÌ_›˜?ËãÇ·³Î:˦Nê{8Í­ÛµkW;ðÀíßÿþ·3Œ®Wfå#—ÀV[me¯¼òŠõíÛ×|ðÁÀÊë3SlÚ||^ùL¾Øˆ €%,ÀÿJøâsê € € €¸a©.]ºØ~ûíçf{®W¯ž©w¡WÒ°°O?ý´KÏRÍ;ªžÃ~sHî´ÓNNoËcŽ9Æ6lèœò믿î¨ÍtX\?W/kÖ™}úé§Ö¯_?ÓÛ§žzª]xá…i±¨Gåí·ßîôªV¯Y¿T§N¿M»>ן×BûLì…¡b € P’Å5‰FI^"N@@@ì üúë¯öä“Oä¶ÛnóÝ4£žsçÎõÝ·6¨w¤‚{~AÚ«¯¾Ú¾øâ ;çœsÊ‚´aõß°aCX–Àí?ÿüsàv6Æ L›6Í™Oø“O>±É“'ÇoLq©fÍšvÜqÇîåë3ØÆ\~^ ñ3U`—ƒê € €@‰ ¨-ñ7§ € € €À-·Üb+V¬ð…PïÑN:ùnßl³Í|·iÃØ±c·ÊÆxÀüæïTâ›o¾ÙªU«–Ru×®]›RþÄÌÉjÕ{”ô?X‹ü1c–Í7ßÜ· mkÒ¤‰ïöBÝËÏk!~¦ õºP/@(Mµ¥yÝ9k@@@#FØwÞé«Ñ¢E {ì±Ç|·kî»î¸}РAÛ eãèÑ£}«rôÑG›†ÃM5­Y³&Õ]âò/Z´(nÙkaÒ¤I^«KrÝï¿ÿ^vÞ~A÷² I¼˜>}ºo®ã?Þ2ÚÚ·ð,nÈåçµ?SY¤¥h@@ eµ)“± € € €C`Ê”)vÚi§ÙŸþéyBšËräÈ‘¡ÃüvîÜÙswåÇl&Lp òYÕ‰'úÖ-Ý€\2ÚZµjùWó{ Ÿ¬k¨á~‹%ÅöxÍFcË"P«9oýÒI'ä·)t}l=C3Gœ!WŸ×|~¦"&£8@@ kj³FKÁ € € €®ÀÀáŒ5?­Wª^½º½øâ‹¶Ã;xmŽ[×­[7Ûh£âÖ%.¨÷áÒ¥KWÌòo¿ýfëׯ÷­Ï;ï¼ã¹í¹çž³>øÀs›VúÍw»CË–-cã^+Ø8jÔ¨¸uî‚‚} &s wŸtŸý‚ù©–§9K³™b‡Šþæ›oì­·ÞJûpÇ7½Ò6Ûl8¸×>±ë²í{¬Ä×¹ú¼æó3•xΩ.Gõ~Oõ¸äG@Ò P[zל3F@@(:¢N~ø¡qÄvÎ9çØªU«<‹×¼›ÊwÀxnO\Ù¨Q#;á„WÇ-ÏŸ?ßöÛo?›9sfÜz¯‡ f>ø ×ærëV¯^]n»"(øêæÑs:u‡6~÷ÝwíÛo¿-ÛEsú^{íµN ´l¥Ç ?ãØ¬­ZµŠ],÷ºW¯^åæùUp¸}ûö6uêÔrùcWÙÄæÓë ¼Éô vËkذ¡û²Ü³‚˯¾új¹õQ­˜3gNYQºö={ö´ñãÇ—­Kö…Ê9ýôÓ={›«wõ]wÝZT”A×&ÕÞ¹¹ú¼æó3ä¥ ö~ÛV~Ø›#ÓýÃÊg; €@Õâ©*5E@@@ ´bƒN^gžL¯/ 5‡é¸qãì©§ž²/¿üÒ«¨²u{íµ—½ôÒK¶ùæ›—­KæÅe—]fÏ>ûlè0½»í¶›]qÅN x‹-¶ˆ+Z½!UGhçÎëlSþ=÷Ü3._â‚_¯`åS@5™T£F S€ú»ï¾óÌ®`˜æöìÑ£‡Í›7Ï4œóºuë<óÆ®\²dIì¢çëí¶ÛÎs½»Rç·Ï>ûØV[må+èÖ¿'»>ñµ§×_½ÝtÓMN NÁf=/^l‹-ŠË¾É&›X›6mâÖ%.|þùç4éèÑ£@Výúõw-·¬!ˆýµÊ¬ã <¸Ü~A+ôÓym¹å–¾Ù¸¼í¶ÛB¿ ,0=b“‚Ë nΘ1#vuÙkõ`VOàfÍš•­óz¡ëÔóTÃëØ ‡¥°¥‚ÞwÜqG¹b´U/át“ÞG^P¿õÖ[í™gž±SO=Õ4 ·Þ¯±I½¸e¨÷å!Cb7ŽÖgä’K.‰[ç·•ƒ>úû%Íÿ¬<7öËRn}®>¯ùúL©|PRÕ³Ï>Û7‹>·AIåŸ{î¹AY·i(õ ãîÌF@¨Pj+Ôåäd@@@*‚À믿n\pAh€U½ºFŽ™Ñ)W«VÍé=xÍ5ׄó¤28Mfh[ K;qâÄÀ"Ï?ÿ|Ûxããò|öÙgöÊ+¯˜z¼)(4?¬vœ>}º)X¤Þ°zVyêÁê$ÑðÍ ì&› }â‰'œÀ]bÙ-C=_wÚi'gÞN:ywûí·wʸýöÛÝÝ’zVÐôý÷ß·3Ï<Ó7P«¤Î½{÷îÖ´iSÛÿýíàƒvÊW°Is¸ª×¯ÊQ°Ï/©õnV9 wèÐÁw¸ëwÞÙ¶Þzk§¡€_y‰ë/½ôÒŒ‚´*ïûï¿÷Î[Û°VC=Ô AÁk O¬ýˆî¶R¥JÖ·o_»ûî»UTR)}†>úè#§nºNk×®õ=¦ßzOwîÜÙ®[·®3¼¹–ƒR.>¯¹üLÝxã¦^ùjœà7§´ë1hÐ §·>›mÛ¶5}×<ùä“6yòd§Ç¼ß¼Ðîþ/¼ð‚Óë÷Ýw75(Ñ÷uXcw_=?ÿüóÎ÷—öoР]yå•Îu‹ÍÃk@( Jµ6ü³4N•³D@àj©ïΦy§4¼ ò#ТE ç&•†¾ô»ÁŸšqÔR:t¨©ÇblRƒ:(vUÁ¿Ö°¶^7¤=ôPÛc= ¾þT°¼€zê¦}¶“‚–ÆW½T‹*) ×¯_?Ï“©CAMd‡G~衇ìâ‹/N¥¨ry;vìèÙ{tÆ NPsÊ”)åöI\Ѻuk§~;ì°ƒU¯^=0æîÛ¥KßÀ²z´* ª€a2iÛm·µ÷Þ{Ϲvê…ªS2IÁÆûî»ÏÉzõÕWÛ€’Ù­\ ý«ëã—ž{î¹Ðù{Ý}õ}5bÄÓܯ™$õ"×¾ r*`UÒ58p iXðTSºêù«Àé&ý5ÄxXÊöç5—Ÿ)*l÷³P5éÚµ«37·_¾ õcÆŒ±½÷Þ»,‹’(`žlÒèÉôVO¶<òU<ÍUîööçýRñ®/g„@&ÂýEÿ‡Ð(-¤âÈì/àâ:Wj‹ € € €@Á D=w¡zÌ*Ø©Þ}Ç{¬=úè£N#ï½÷ÞHƒ´ÂU°MA"Ý4r{n&‹®žiZVR Mš¤U9©Îëul¿2ªT©bê)ÖÈA½5ܬxVWÁ®d’ßqµ¯zçj(kõ,U=‚’®¥z»öZµje÷ÝTß’Ü #Ý, ;—wÜÑ >d¤UµêÕ«çTÕ3XóÞžuÖYNoÅ$«—Mõ9ì°Ãœ²†vo€ÆeJb!ªV²×6ÛŸ×|~¦’¸‹Cù$Ô¨zvZ¯GÕªUá]¼Ò°­ùJúÛK=?5¤ðܹsméÒ¥NoGÕO½BÕ³WìÔ÷fÍš¡UU™ 0É&ö!†4x˜ëáõ¬¡{k×®í{^5¤±ê¬¡“çÍ›g †jXY ‘ªžM±IC0kõès:­Ó°´êù®g‚’ ªjøh i=mÚ4çøê=¥@~7e¤aVåç&÷5®Ž©‡ê ½7tÜÕ«W—½Oô~pRrÒ0±Êëè¾–qìûLå©Ç¢ÊÓyh(尤ೆyU`{êÔ©Ž…òÚwß}÷µ«®ºªÜðÖae¦²]×ÿÃ?tz*kþáØ‡†|Ö¹ª>šÃXÃ!«W³z\+Xeð-U ™­^Ö}ÖõÒg:ñ½í~¨«;‚P*VʛϫÊÍÅgJŸU}þc­Ü×ú¼¨²‘“ÞÃî{YóGëû@Ÿ¹«‡¼ÚWÏòv÷—¹öw? *CÛ‡=Ö±4ç±öw¿ô¬2cëà~?è³­rHø УÖO†õ @Úâ¨-þkÈ €¤(@ 6E0²#EµYÄ¥èPµ¡Dd@@(µp¨*@ ¶@/L Õbèã°ÈŠ € € € € € €D!@ 6 EÊ@@@@@@@R P›Y@@@@@@@(ÔF¡H € € € € € € €@ jSÀ"+ € € € € € € …Ú()@@@@@@HA€@m XdE@@@@@@¢ P…"e € € € € € € €)¨M‹¬ € € € € € € €@j£P¤ @@@@@@@ µ)`‘@@@@@@ˆB€@mŠ” € € € € € €¤ @ 6,²"€ € € € € € €Q¨B‘2@@@@@@@Ô¦€EV@@@@@@@ µQ(R € € € € € € ‚Ú°ÈŠ € € € € € €D!@ 6 EÊ@@@@@@@R P›Y@@@@@@@(ÔF¡H € € € € € € €@ jSÀ"+ € € € € € € …Ú()@@@@@@HA€@m XdE@@@@@@¢ P…"e € € € € € € €)¨M‹¬ € € € € € € €@j£P¤ @@@@@@@ ª)ä%+E)0yòdûðÃí¨£Ž²fÍšå9Pi@ ¢ ¬]»Ö&Nœèõl”™veØ@@@ðX²d‰½ñÆÖ¤IëÞ½»GVÅ D}¿'êòbëÊk’ P›ŒyŠJà÷ß·?þØFŒaÇ·ùóç;õ¯R¥Š]xá…Eu.T¨è+W®´k¯½Ö~øa[½zuÜé8Ðz÷î·.™…l”™ÌqɃ € € €„ üñÇöå—_:ÁYÝ¿?~¼i]×®]‹.P»téR{ê©§löìÙV¹reÛvÛmíÌ3Ï´úõë‡1¤¼=êû=Q——ò ±ÿ'@ 6·ÂÂ… /XõªQ£†é¹zõê¶ÑFÙŸþi«V­Š{4nÜØÚµk—q -ZdS§NuŽ©ãºÇ¬V­š­_¿ÞÖ¬Yã<Ô2Ä}­žJ-Z´ÈøØù,@ç2sæL›3gŽÍ;×fÍšåü¨Mš4É9ï|Öc#€„ |ðÁN VÌG•²QfTu£@@@(-Ý·Öýk÷¡{×ãÆ³eË–=ÄØ±c­gÏž¦Á±éî»ïv:Qí²Ë.±«3zõýž¨ËËè䨹äÔFøxÿý÷íÔSOMºÄZµjÙ„ l§vJz¯Œ'žx¢3Æk“ïºØUW]廽6¼õÖ[vä‘GCUóZÇW^yÅÔ:hŸ}ö±­·Þ:¯uáà €€+pÏ=÷Øå—_î´u×eúœ23­û#€ € € €@é ´nÝÚé}ú”Ù×_mmÛ¶ >ûì³¶÷Þ{Û\P¶o*/Ž:꨸ìê©ôøãÇ­9rdÑMDw 6â¾ûî+Û¢›ê‰e+ð Í}¬ ½æÐcĈ¦IÜI €@! è{ª{÷îöÍ7ßÄU«k×®ö裚~C5︚<òÈ#Îíjd”¢.ó—_~±Áƒ;®4‡ŒRÆ ­_¿~AÕ` € € € à) )Ý´xñbëØ±£s/×]W¬Ï 4¬úúõëÎU™|6F}¿'êòTmî!ù\ûìS¤Ý¨ßÁ ßÂ(ËÔô±›bë¡×úO @@@ˆB nݺQ“÷2vÞygëÝ»·=ñÄžu¹ôÒK­yóæžÛüVFy¿GLjº<î!ù]9Ög*@ 6SÁýÕ[(Ù¤–4Çwœ½ýöÛ–Éî5jÔˆ;dârÜÆ ¶ s-•@­zaׯ_ß "( 9Û·ooÕªUsz¨¨­`onN"P/Õ &xÖ^ ‰ÒIQ–™Øà%±>jEXF@@@t¦yJ·Ü|ì÷ðÃÛf›m挎¶nÝ:§ º?ÿü#­i¤¢¼ß£ÊD]÷òñ.+c¨ÍòuN5H:zôh»è¢‹œùúÒ­Zâ1—Ó-·ö+…žÃîu¸óÎ;Ý—<#P!.¹ä;õÔS!`*Ä qŽ€æÎöKéÝe™a-Y5jäW}Ö#€@ÆŒcz¨AZ­Zµ²pŠD@@ swß}×>ÿüsëÓ§•Ò½×Ìå(¡"ݿ֗·Þz«Ýpà 6mÚ4«R¥Ší°ÃiwB‹ò~ÞiQ—Ç=$>¿Ù¨œ­‚)÷éôŒUOÈL‚p‰ÇL\æÚ €…(ðþûï;su÷êÕË4©bÌœ9Ó÷D4×v:)Ê27ÞxãÀ*У6‡D.ðÛo¿9­¯[µješ;:¬Årä @@@’X¶l™]yå•¶ÝvÛÙàÁƒ!V“Ø,THlÛµkgmÚ´I;H+˜(ï÷d£<î!UÈ·oAœÚ_†d‡6Ðý+¯¼’ãÚq8@ ¿þù§ 2ÄùÎgœa3fÌÈo…8zÆ ºD¢,3¨Ç^íÚµéÑõÅ£<’X´h‘sÎ9Îï1®]»6É=Ɇ € €@î¾ýö[Óý õ"|òÉ'Íþ5w5àHø ôïßß.»ì²²‡b?ýô“ÿyÞåýJÔåq)Ïo |xµ9¾¸ ¾&3‰¶‚ôÓO?Íq 9 õë×;-RwÜqG;þøã}ç8ÍM©A˜ÀŠ+²¤¼=Ê2ƒ†¨¢7mÊ—†ˆ\`Μ9vÞyçÙ¶Ûnk÷Üs-_¾<òcP  € €™ |óÍ7vÖYgYË–-íˆ<@”iýØ¿4yä»ûî»ËwÜq‡s¿­P5¢¼ß£sŒº<î!ê;§øëŵ9¾†š\{øðáÖ¹sçÐ/Š•+WÚGaŸ|ò‰m½õÖ9®iüá4ìœæ]P]~øáûùçŸí—_~qÆoذ¡5hÐÀéñеkWkÒ¤IüÎ-}ÿý÷öæ›oÚ¬Y³œaQ5¤BÓ¦MMœC9ÄêׯÑ‘¼‹Y¼x±½öÚkαçÏŸo ¦kx“í·ßÞi5§!újÖ¬é½3k@ -?þøÃ^|ñEç±Ç{Xß¾}í¸ã޳jÕª¥U;å^ ­‰£,Só§ø¥°!müöc=D/°páB»ôÒKíºë®³ÓO?Ý™ —1@IDATLƒ‘@@($ 8÷.þùÏÚ™gži_|±¼-¤:fZ—üѹ¿ýÞ{ï9Ÿ'þ]>zôhgnPYèþq½zõœŽK·G÷tV¯^íÜš>}º©·òš5k¬N:ΰººïܶm[«\9š¾gšvkèСNG©k¯½ÖZ·nÍï¿ÿîÜ«~ýõ×­C‡vþùç§´E̬ë¥÷ň#lË-·4}RIQÞïÑq£.{H©\Mò¦"@ 6­ˆòêå¿ÿý¯}ôÑ¡ó((zØa‡ÙG}da“UGT½¸b&Mšd·Ýv› Lv¨ õñ·¿ýÍùc$Ù¡žãš° i °gžyÆwØ;Íëë_ý5aï̧L™âÿå—_ö=¾Ž¢ í5×\cW\q…Eqޙל¨XãÇ·“O>Ù®Eà ©¥ªH@JG@-¢zè!{øá‡m¿ýö³³Ï>Ûù›š¿½Jç=À™"€ €Å  ‘`î»ï>»ÿþûíÀ´Þ½{Û‘GiêxRlIU¾üòK'(©Ü¸qãLz”4 š¨ýúë¯óÔý¿Ô¨Q#»á†<ƒšO=õ”]uÕU¶dÉ¿Ým‹-¶pî«wëÖÍ7ßý_b„ öÁ8ZÝóu“:…jå0uêT'­ÎjwÞygÙr6^¨Œ†ãð Òª'°þ¨P/_ ¡;õ öKê%uÞyçÙ;ï¼ã—¥Üú{î¹Çvß}wÏhÍÕ«o­Ãæ4(Wp’+ÔLÃg$i5¿°åŸ}ö™3Ä…zúõpÖyk^[d_@ÿxõÕWÿਅª¾«Ô“Ï`öíc ^§jA»ÓN;…ic÷ózݾ}{guÔeª5³~{5¿yPÖ«NîºM6Ù„ ­‹Á3& Þj™¯©FZ¶lé —ÿá‡ú6,,°êS@@ÐTo7Þx£óÿg ¬ÆÎcÇŽ &¯Ðx4ôqªAZ÷4Ýß®»îêyØÍã÷¬^¸ƒ òÛÌú"ˆú~OÔå‰{HEðFª U¤Gmž/¤æÓRÏK Ó–Ì0§¿E‹Yé©©@‡†ýé§Ÿ € Pá¢ü»U=5ý›º×êþݪ‘ ·Ûn»œÛ©'©:ꨤ‚°šž(™¤º«“†K^¶lYè.î\¦nÆT÷Wãk¯ûÈnyn~Ýs×4u 䥒4Lµþß ûUÏ<óŒMœ81•Ý+d^Íå«ÑAe¢{Þ™LEõýž¨ËÓäR…|äI¨-€ËÒ¸qcç‹M„°qüuSÃ0Ž=Ú:tèiíŸ|òÉÀý ùMx¯^°êa«‡WÒ—š~؈õKW_}µ3çdâö*Uª8=r÷Þ{ïÄMÎ ]ˆõŒ† QÞt“þQë5¯¤!.cƒ´n m¬y46¿WR H½zÏKçÿ¯ÓJ™ EòÿKâñjù¦¡ÔõЈuëÖuæßféx§¨–ôAõVõ:Õ²4×·_ÒEjìã•Üïü¨ËT½4´”~—ôúå—_ö:|Ù:à˜Ô—”~ ²ãJ©æÜ(:t¨é¡¤F’}ûö55¬ÈI7Õø\Uä+̹!€ €@EPLzôPêܹ³3«î ç*i4C7éžëÁì.z>«Q¤F\Ôý€Ê•+;½_{í5g>ÞdætÕÿ͵ÿ¹çž›Òþš3V.:¦WÒhknÒÈŒ{î¹§»˜Ô³F ÓCIsèªA¹îó–rÒT3z(éÿSzohætRÔ÷{4¢RÔ÷¤¸‡”ΕeŸtÔ¦£–…}Úµkç´ÌQV‚’n¸è‡füøñã𕑸Mcû«Õ–_R 1¨—†>í´Ó|µ*W½^uC¾fÍšå£á„ Pn½Vyä‘æÞ°÷Ìð×Jý¡@³Z:…ùù•qýõ×;­·ëǾW¯^‰«Ë– ö Ô*“Dj˸<_¨Ç²ææ •¶€Z8&;J²Rj\¢?ÄÕ B½¨vÛm7gÈrµ²#E/ ï÷ĹÔ_xáßÿÈè»Uó ¥l”ùÜsÏ•R tü~ÿôÛœîNeàEJü¤ÄU!3ëï\ÍÿujÚ´©ó;àö¨Ýb‹-œC¸Û¨W(åéÆŸ«B¹Ô@*’€:Û,Z´(òSRƒfýÍêþݪ>Jù±/ìžf5Ló–ÆA5º™†5VG£]vÙ%ð^­»§NÊ,Ýý5º¡†Fö»×«Fâš¾o›m¶)Û×ï…põÛ¶^‰Ô‰§Ôµ‰NrM7P›û=Qß“ªV­šq)ñª³œ µÙPM³Ì£Ž:Ê™Sëšk® -Aó§ª%ÐG}É‘FDSï\¿¤iñ’ Ô†‡_ù±ë£(#¶¼Šð“Šp9‡B P[W!¦þ÷믿6Í–Ô‚G¶ëÇN=Z3Ijá”’¹¹¥9û4äÔ©S}‹3fL¹@­z¶¹Ãy$34X¶ÓÛo¿í9ϬŽÛ¤I“ÀÃk>=ýQå׺kÊ”)û³2Ðw…F$Pw}g¨¥# @ ôÔâ^¿â¼Y³f¥À#€ €…€‚³ú»U÷vÃî=æó„ü¦Ás뤩ü‚’j“Ù?(P6 [7Ý'RãàL†³p•Íçõë×;S9%{ ¯óÕÜÀê–lÒHˆ~©LüêÆzŠI ³è^1iÕUÏž=Û>ùä“ÐZkhõbMìÖºcB†?ü0aMübÆ ãWø,uíÚ50Pûé§Ÿ–ÛsÈ!¾?’[n¹e¹üÙXñÞ{ïùöÇ’‚äêÁ§Ã^)àx‡u”š€>{‡rˆyæ™N ËŠ>Ï`©]_ÎHV@#ŸœqÆÎASu$[ù@@ȆÀÖ[oíÜÃ8å”S¬E‹Ù8DäeªsJP »£sJ™î¿jÕª âã¶…K\f…L÷÷(2åU?þ¸]xá…)ï»Ã 7Ü`z$›Ö­[—q'±dE>JU€@m^yÍÿꫯZÇŽíÛo¿ ­á£>jš,ûòË/Íë—!l¾Ædµê]”~úé§r›çÎ[n»"WÚéÓ§»‡,÷¬@òرc¹uk×®íü‚´å cd, ï»³Ï>ÛN?ýt jÕ—ñ(@ `ÔXGÓ†ôîÝÛ<ð@§u|ÁV–Š!€ €”¬€z}ôÑÎß­šN½:K)…u€ ³Ûß«ÇhX™Å¼ý—_~)æêSwð Pë“ïÕ > 6ÌöÞ{o[¹rehu®ºê*§%–~øÓI?ÿüsànê1šL ËçuœyóæùVžïŽ)n ´ÊÆŒ)–HvˆR@­u#¾oß¾N/ÚBhÅåùQ €@r›m¶™i¬ .¸  ‡ˆKîlÈ… € PQ7nl矾ó(åFæ›l²IF—8Óý3:8;#€9 P›#èt³Ë.»Øþó;öØc}ç?uËUë! ›¡¹fÃZ¹û¸Ï+V¬0 aE ¬._¾¼ÜaæÏŸ_n»â×_u_fõÙ«§o¦ÔÐmÚ´q‚J™–Åþ”ª€Zžªçìe—]fÛo¿}©2pÞ €@É è7@ {õêeaã•< € €yØqÇ­_¿~v 'ówšÑÐ>ooEŒE$@ ¶À/Ö1Çc7Þx£]{íµ¡5UÏÏ#Ž8Â694sL†Õ«WÇ,y¿TOØd†!®_¿¾wÿ·¶V­Zå¶ÍáúÝwߕ˟ajýqå—t³P­»tîzÖðÏmÛ¶µÖ­[3~¿ëÐwÅ9çœcW\qEÊOBŠf3 €@ ´k×ή¹æ§áb© WD—‰ª"€ €%/°Ûn»Ù?þñgz‚“%ÿvHI€@mJ\ùÉüÏþÓ¦Njš+5,ýðÃÖ£G°lqÛëÖ­·ìµà5d±W¾š5kz­.[çÕã¶^½zæ,ÎU 6¨º)øôÓOÓ ®ì*òì hÎA iyÝu×™† "!€”¦@«V­ìÖ[oµž={–&g € Pꨡ¿[<òÈ¢¨/•ôð»7í;k5ÂPÇŽ“®ÀqÇg‰#VžwÞyNCØd ©R¥J²Yɇi ¨M.×» 4ÈfÏžm&L=t²AU· áÞkAsá&[¦×ÐÆîqôì¨mÑ¢…-^¼86[Ùëß~ûÍ4‡móæÍËÖeã…zÂúÕAÃJÏ;—aW³O™ÄhD€^xÁ¶Ûn»˜µ¼D(%ý­øÐC9vÔx‡„ € €@! lºé¦öØcÙYge² ñ ¥V§dï}§Vjô¹u<•ûä^ÓÆ¨Ql÷îÝ£¯%"€@Ú•ÓÞ“s*P£F :thÖ†Ýl³ÍÏgéÒ¥ÛÝa?j-[¶t³–=o»í¶e¯½^<òÈ#^«Ë­ûóÏ?Óžk7ìüÇŽ[îx¬@h4Ì;AÚhM) (6N:Ù…^ÈôÅvá¨/ € Pb]ºtq¦l"H[1.|Ø=meP'§Š¡ÀY €@¾ÔæK>ãn±Å6lØ0§÷k»î²ûî»nÿì³Ï·»Ãºú#&1…jŸxâ ß¡‘cËš6mšýþûﱫ’~½ë®»æUf € € € € €@ÅX´hQè Mš4)4@tÔ¦£–Â>ëÖ­‹Ë½~ýú¸åT41ýàÁƒ-êIé÷Úk¯ÀªŒ92p»»1lNÙtµ?ýô“ÝsÏ=î!|Ÿ}ôQßma:wî˜åã?NjØéÀB؈ PT´–-ªËEe@@@ðÐÔ~éÍ7ß´ 6øm¶)S¦˜: ‘ðà’Ÿ ë“ P›ŒRy'"_µjU¥ýo×c=Ön»í¶ŒË‰-àÐC þ*ûÅ_Äîâùúõ×_÷\¯•tçÐÍâ®råà·â5×\¬½ÿþûMtS·nÝLsõ¥ã?ÞÂz íÏ6@âX¸paqU˜Ú"€ € € P@šª®P’×”|nÝÔQhÔ¨Qîbܳp't’ýñÇqë³±PH^Ù8¿Š\&÷*òÕÍþ¹GDzü „ÄÀlârºW\q…wÞyIížÌüöÛoo‡rH`y/½ôRàö¯¿þÚfÍšå›§_¿~žÛtìO<Ñs[ìÊK/½ÔÎ=÷\SïÞeË–Ù’%KL­öÞ{oûÛßþ›ÕóubÐ<6S£Fì„Nˆ]Uîõüùóm¿ýö³™3g–Û–¸B?ܦúÁLÜ”·ådÞy«F +ôÝ›î(Q–Ù°aC_{ýgìÕW_õÝÎ@@@*²@Ðÿ¿uÞkÖ¬ <ý°íaåþׯTöoÕªU`q½zõ²±cÇÆåùàƒ¬}ûö6uêÔ¸õ‰ ©Ô#(o˜Wâqó½t.AÛüê´O:÷¢.{H~WŽõ™ TÍ´ö˜3gN\†¹sçÆ-g²ðÐCÙ‚ ì7Þ,&Ù/±«¯¾Ú)Ë/ wÇwX÷îÝM½O½Ò<àµÚYwØa‡Ù¾ûîë»ý†n°^xÁÂêúøã›é¤_ý5p·Ë.»Ìž}öÙÐa.4ü´åçœsŽiÞàØ¤‰çŸzê)'@ë^kåßsÏ=c³eýõ?üPî^ëÊeb ±@ÐwïŠ+Ò:Z”en³Í6u8å”S좋.²víÚ9‘Ô@höìÙ¦ß÷:uêîËF@@@ŠY èÿß:¯åË—žÞo¿ý¸=¬üÀÿÚ¨Î<ɦí¶Û.0«ê²Ï>ûØV[må±ü1p ÏŠ³@@@"x÷ÝwkýôSÏüúò1b„Õ¯_ßs{ìJÝ×¹,Z´(vuèëš5kÚ Aƒ¬K—.vï½÷úæW=Œíܹ³P½à‚ ¬Y³fqù5®¾Ä“êR½'Nœ·âÂùçŸoo¼qâêH–õã?tèP'ø¬`¼®…è<ÕÛ+1©S‹-lÿý÷7µ kРU¯^½ìyT­ÊWB¢Ë ¼ÀgŸ}f¯¼òЩŬzœjˆ  4}út§¡Ž»¨Á޾/õýtöÙg—í–2Ë ÿëÅÎ;ïìü'6¬ŠÍ“øZCñkè# € € €MàÆo4ÝVð1,ª{²º¹ÓN;YÛ¶mM÷BŸ|òIÓ½ñyóæùÎûêš©S“îi«3Œ:¼xݯuóz=?ÿüóÎýí¯{W^y¥Õ­[×+«h½ä’KìöÛo÷Üî·R=lÕ)æÌ3Ï´3fxfÓ4xº·¡Ñ(›6mêÜ=øàƒ¼ F¿õÖ[NÌ@åu¤Q9ê¤rîСCèt}žÊÒJÝ£Ñôˆj”¯8‡W‡®ØCxই wØagZCw{Ô÷{¢.Ï­gì3÷b5x¥@¥¿†¹-œ½£<³<”õÌ3ÏØ©§žšÒ‘57jЗs²…éKQÈ¥K—–Û¥yóæ¾AÜr™ÿZ±aÃÓMèûï¿ßksÒë8à2dˆMv'µÒÐʱ-–‚öU UÁJý*0žJïUý˜¨qbÒ¢æÓUªLR§Nœ€EâðÈ™”»¯ÞkzÏE•è­U«VTÅQ- ù´Ý¹IN?ýtgÈò‚®p‘TNCò_|ñÅÕV ™b[—f£ÌÄ >÷ÜsvÒI'%®ö\>ôÐCH•+3(‰'P+ÕˆH}ý^¦ÚX+ñ qú;ò‹/¾ˆ[·ÑFÙA·®ÐV­ZåyMßY{ì±G¡WŸú!€ €„hþTÝgURCc¾²•jÔ¨:÷¬×±`T°¬k×®=-½öu×%Þ¯UCp¿À«»Oìs˜z´ê>²îA'“ÔÑJ£fª³•l+LêÛ·¯Ýwß}NVuÒ0`@2»•ËsôÑG;÷—ËmHrÅæ›o^.öpçw:#B&YD\¶LîÑ(˜ÛÙ+“²ÜJÅÞCŠº<÷‰Ï…xI=×÷Úk/§ªj¬pÛm·%V›åà.c„¨qãÆ––ZQꤞ«ê]š˜tã&•¤!•õC¢¡ Ôk6Õ¤/õŒ5jTJAZG“I“&9-”*UªxhG_îúqUÒp É&õõ›ü[7ßÕ²J_pn˧dËU˯>}ú˜zWkèliUõèUXµÓqôƒþx¡Z(éѺu벇–5D‡z­©§³Zwm¶Ùf΋ʧ›’$@ ý Ó”XFâr:凕q '8­nõû”4¢„þcL6H‰m € € €¥(àþßÛ}NÕ@ÃûݯMµ,¿üR÷žÕI)ì€zOj¤0ÝsUJ·ƒKº~çÊú¨ãQžKeÅ–û:£Ø¼É”Á=¤X1^G%@Ú¨$ÿ¯ Õ £hj}¤gÀtSW=U\ÓPÄ žê¡ua™§RE 5¡–KNrÚ´iγ‚·aóCåh˜ ý0iŒ~õÚÕœ‚:/ õª^»j-¥À®Q$µlzùå—MÃC¨‡í’%Kœ¡y5샾5'mbpQuÕ:³üõ,{õÕðÀ æ®]»Ö¹.‰ÃûÕY=ïÔrjô_s)êúêü5ü‡‚½ Œê|õÐ ü=zxËýÊf=äG€µÙs—­¾wõ]ûиôŸ ä¡ïb¿‡¾ãk×®WÁl”w€ÿ[ÐÖ4lÒ„ LózëwZ pÔ°E¿ošÇ=[ÃÙ{Õ§TÖÑ£¶T®taž'=j óºP+@@x\ö¨Õ}eýÿ=öÿôîkÝÕh„º§­{¬ºÏ­{®zÞrË-ÿϯ\¹Ò¹—«ûâî½q=ë¾€»¿{Veh=´=ñ~­Ž¥á†µ¿îõºÕ'¶:¦þ¯ÿ_ªœd’¦¿9rdÙ=tõÆÕ}n•¡{¼‰ÓÄiZ9 ù«ûÍî½gKç ãË@¯unõêÕ37è§û  Sy]ÇØgÝCQÌ ÑDå)8¬{…”ÂîÑèºÄž‹ûѵOœã5¬¬Tï!E]^{!ÝC¢GmЕ*Žmj‹ã:QK@ô‡CGJQd @ 6›fÍšÕ-k’V˜6mZ+vKŸz ¨í–S  @`ð \sÍ5)~ P¤À]wÝ•^|ñÅôä“O¦1cÆÙ”º  @€±ÀV[m5ˆïέ @ ýl¿[vÇ @€(Wà¶ÛnËìx-·u­ @€ @€­(`Dm+>}"@€BFŽ™ößÿBÛP9=XrÉ%{v³ 0Ž€6^>øàÖ{Ý%@€èO-·Ü2 >¼?» m€ÀFm4z©‹]˜÷IéºÓ{ @€hŽ@¬…¾öÚkg•-±ÄéÕW_MC†øÎlstÕB€ @€®€©î³Ós @€€Àõ×_?¿—o½õVŠõj @€ @€€ Ö¿ @€(píµ×vª½ëûN½!@€ @€ÚFÀÔÇmó¨Ý( @€e ¼üòËi…VHüñü¦W^yå4uêÔùïm @€ @€í)`Dm{>wwM€ @€@ W]uU§6šœ6mZºÿþûKh] @€ @€@+ j[ùéè @€Z`üøñUû?nܸªûí$@€ @€ÚGÀÔÇíó¬Ý) @€% ̘1#­´ÒJiÞ¼y­Ž5*MŸ>=-¸ ïÎVàØA€ @€ÚDÀ_ÚäA»M @€ÊˆÑ´ÕBÚèE„¸&L(·CZ#@€ @€ZJ@PÛRCg @€,_|qÝ[¹è¢‹êw @€Ü¦>ÜÏ×Ý @€ Г&MJcÇŽ­ÛòðáÃÓ /¼FŽY÷<  @€ @€Á)`Díà|®îŠ @€~¸ð ¶>kÖ¬4nܸ†ç9 @€œFÔÎçê® @€è'wÞy'5*Åk£²Á¤x ÑiŽ @€ @€ƒPÀˆÚAøPÝ @€ý'pÉ%—t+¤>øàƒiâĉý×Y- @€ @€ý& ¨í7z  @€ 0ØæÍ›—Î:ë¬ÝÖ™gžÙ£óL€ @€ SŽçè. @€h›o¾9m·Ýv=êÉ!CÒ´iÓÒg>ó™]çd @€ @`` Q;°ŸŸÞ @€ ÐB§vZ{3wîÜtÆgôø: @€ @€[ÀˆÚýüôž @€˜4iR;vl¯z³Øb‹e£j—^zé^]ï" @€ @`à Q;ðž™ @€ Ђ'tR¯{õî»ï&kÕöšÏ… @€ @`@ Q; ›N @€ ÐJ?üpZýõÓ¼yózÝ­¥–Z*M:59²×u¸ @€8FÔœg¥§ @€´¨ÀQGÕ§6në7ÞH§œrJ‹Þ¡n @€ @€Í0¢¶Ù¢ê#@€ @ ­n¿ýö´Í6Û4垇 –ž~úé4jÔ¨¦Ô§ @€ @ u†´n×ôŒ @€­/0nܸô¥/}©nGcjä˜yĈiÍ5׬{îøñãÓá‡^÷  @€ @€/`DíÀ†î€ @€Xxá…ÓܹsÓÆoœ&MšÔâ½Õ= @€ @  kÔ–¡¬  @€ @€ @€äµ9 › @€ @€ @€(C@P[†²6 @€ @€ @€ Ôæ0l @€ @€ @€ @  AmÊÚ @€ @€ @€ @€@N@P›Ã°I€ @€ @€ @€2µe(kƒ @€ @€ @€9AmÃ& @€ @€ @€ÊÔ–¡¬  @€ @€ @€äµ9 › @€ @€ @€(C@P[†²6 @€ @€ @€ Ôæ0l @€ @€ @€ @  AmÊÚ @€ @€ @€ @€@N@P›Ã°I€ @€ @€ @€2µe(kƒ @€ @€ @€9AmÃ& @€ @€ @€ÊÔ–¡¬  @€ @€ @€äµ9 › @€ @€ @€(C@P[†²6 @€ @€ @€ Ôæ0l @€ @€ @€ @  AmÊÚ @€ @€ @€ @€@N@P›Ã°I€ @€ @€ @€2µe(kƒ @€ @€ @€9AmÃ& @€ @€ @€ÊÔ–¡¬  @€ @€ @€äµ9 › @€ @€ @€(C@P[†²6 @€ @€ @€ Ôæ0l @€ @€ @€ @  AmÊÚ @€ @€ @€ @€@N@P›Ã°I€ @€ @€ @€2µe(kƒ @€ @€ @€9AmÃ& @€ @€ @€ÊÔ–¡¬  @€ @€ @€äµ9 › @€ @€ @€(C@P[†²6 @€ @€ @€ Ôæ0l @€ @€ @€ @  AmÊÚ @€ @€ @€ @€@N@P›Ã°I€ @€ @€ @€2µe(kƒ @€ @€ @€9AmÃ& @€ @€ @€ÊÔ–¡¬  @€ @€ @€äµ9 › @€ @€ @€(C@P[†²6 @€ @€ @€ Ôæ0l @€ @€ @€ @  AmÊÚ @€ @€ @€ @€@N@P›Ã°I€ @€ @€ @€2µe(kƒ @€ @€ @€9AmÃ& @€ @€ @€ÊÔ–¡¬  @€ @€ @€äµ9 › @€ @€ @€(C@P[†²6 @€ @€ @€ Ôæ0l @€ @€ @€ @  AmÊÚ @€ @€ @€ @€@N@P›Ã°I€ @€ @€ @€2µe(kƒ @€ @€ @€9AmÃ& @€ @€ @€ÊÔ–¡¬  @€ @€ @€äµ9 › @€ @€ @€(C@P[†²6 @€ @€ @€ Ôæ0l @€ @€ @€ @  AmÊÚ @€ @€ @€ @€@N@P›Ã°I€ @€ @€ @€2µe(kƒ @€ @€ @€9AmÃ& @€ @€ @€ÊÔ–¡¬  @€ @€ @€äµ9 › @€ @€ @€(C@P[†²6 @€ @€ @€ Ôæ0l @€ @€ @€ @  AmÊÚ @€ @€ @€ @€@N@P›Ã°I€ @€ @€ @€2µe(kƒ @€ @€ @€9AmÃ& @€ @€ @€ÊÔ–¡¬  @€ @€ @€äµ9 › @€ @€ @€(C@P[†²6 @€ @€ @€ Ôæ0l @€ @€ @€ @  AmÊÚ @€ @€ @€ @€@N@P›Ã°I€ @€ @€ @€2µe(kƒ @€ @€ @€9AmÃ& @€ @€ @€ÊÔ–¡¬  @€ @€ @€äµ9 › @€ @€ @€(C@P[†²6 @€ @€ @€ Ôæ0l @€ @€ @€ @  AmÊÚ @€ @€ @€ @€@N@P›Ã°I€ @€ @€ @€2µe(kƒ @€ @€ @€9AmÃ& @€ @€ @€ÊÔ–¡¬  @€ @€ @€äµ9 › @€ @€ @€(C@P[†²6 @€ @€ @€ Ôæ0l @€ @€ @€ @  AmÊÚ @€ @€ @€ @€@N@P›Ã°I€ @€ @€ @€2µe(kƒ @€ @€ @€9AmÃ& @€ @€ @€ÊÔ–¡¬  @€ @€ @€äµ9 › @€ @€ @€(C@P[†²6 @€ @€ @€ Ôæ0l @€ @€ @€ @  AmÊÚ @€ @€ @€ @€@N@P›Ã°I€ @€ @€ @€2µe(kƒ @€ @€ @€9AmÃ& @€ @€ @€ÊÔ–¡¬  @€ @€ @€äµ9 › @€ @€ @€(C@P[†²6 @€ @€ @€ Ôæ0l @€ @€ @€ @  AmÊÚ @€ @€ @€ @€@N@P›Ã°I€ @€ @€ @€2µe(kƒ @€ @€ @€9AmÃ& @€ @€ @€iÝ€f?IDATÊÔ–¡¬  @€ @€ @€äµ9 › @€ @€ @€(C@P[†²6 @€ @€ @€ Ôæ0l @€ @€ @€ @  AmÊÚ @€ @€ @€ @€@N@P›Ã°I€ @€ @€ @€2µe(kƒ @€ @€ @€9AmÃ& @€ @€ @€ÊÔ–¡¬  @€ @€ @€äµ9 › @€ @€ @€(C@P[†²6 @€ @€ @€ Ôæ0l @€ @€ @€ @  AmÊÚ @€ @€ @€ @€@N@P›Ã°I€ @€ @€ @€2µe(kƒ @€ @€ @€9AmÃ& @€ @€ @€ÊÔ–¡¬  @€ @€ @€äµ9 › @€ @€ @€(C@P[†²6 @€ @€ @€ Ôæ0l @€ @€ @€ @  AmÊÚ @€ @€ @€ @€@N@P›Ã°I€ @€ @€ @€2†”ш6 @€@+ L:5;¶•º¤/Ú^`¹å–K“'On{ @€ @€í# ¨mŸgíN  @à¯sçÎM/½ôZH`ÁMôÒBCW @€ @€JÔ–€¬ h]Å_<-»ì²­ÛA=#0ȦM›–âË  @€ @€vÔ¶Ûw¿ ÐI`—]vI_|q§}Þ PžÀèÑ£ÓsÏ=W^ƒZ"@€ @€ Ð"æ˜k‘¡ @€ @€ @€´€ ¶}žµ;%@€ @€ @€ @ Eµ-ò tƒ @€ @€ @€öԶϳv§ @€ @€ @€´ˆ€ ¶E„n @€ @€ @€ Ð>‚ÚöyÖî” @€ @€ @€ԶȃР @€ @€ @€ÚG@PÛ>ÏÚ @€ @€ @€ Ð"‚ÚyºA€ @€ @€ @€@ûjÛçY»S @€ @€ @€ZD@PÛ"B7 @€ @€ @€hAmû‚ÚöyÖî” @€ @€ @€ԶȃР @€ @€ @€ÚG@PÛ>ÏÚ @€ @€ @€ Ð"‚ÚyºA€ @€ @€ @€@ûjÛçY»S @€ @€ @€ZD@PÛ"B7 @€ @€ @€hAmû‚ÚöyÖî” @€ @€ @€ԶȃР @€ @€ @€ÚG@PÛ>ÏÚ @€ @€ @€ Ð"‚ÚyºA€ @€ @€ @€@ûjÛçY»S @€ @€ @€ZD@PÛ"B7 @€ @€ @€hAmûúhŠ‘@‹.ºhöºÈ"‹¤¡C‡¦yóæ¥Ù³gwúYn¹åÒºë®Û ÖƇgΜ™üñ¬Íh·£Í…^8Í;7}ðÁÙOŒ,êØŽ‘J£Gn\y Ÿ÷òÔSO¥çž{.=ÿüóé™gžI“&MJ?üpvß-Üu]#@€Oîºë®,ˆ}öÙg›æQDM뜊 @€ @€ Ôæ0úºyÇw¤}öÙ§ÛÕ ><Ýwß}éóŸÿ|·¯©vâ{ì‘î¾ûîj‡jî;ùä“Ó‘GYóø@8pË-·¤¯ýë¡«¥ö1F’Ep? .¸`ZsÍ5Ó˜1cÒK,Qj?4F€z§Ÿ~zúñœ>þøãz§õèXuö¨N&@€ @€ @€@µ=Àjö©¨íºë®éþûïO‹-¶X¯«÷Ýw{}­ ‡À”)SÒñÇŸzè¡l„qµàcùå—Ok­µVn|ðÁ)F\+([àÃ?LñôÛßþ¶iMQgÓ:§" @€ @€ÔÔÖ€éÍîM6Ù$yæ™éå—_N“'ON1¶QˆÛ˜.¿üòÞ4™]sÜqÇeSOŸ>=Ý{ï½ÙÔ¿]+‹é¿ò•¯¤õ×_?Å”Ë[o½u×SÜû˜¾ùûßÿ~zûí·Ó /¼n½õÖlŠéw#}èp„§œrJúå/™Mk]¯ª_|1ÅÏwÞ™­yê©§‘\Ì1š.ðú믧o|ãÙçP³*/¢Îø²Ëµ×^›­w~øá‡§ø¢‹B€ @€ @€f j›(ºÚj«¥C=t~=öXZgu†‡W\qEÚl³ÍÒA4ÿÚžlì´ÓNNÿÎw¾S1Ré†nHÛl³M§óú›µ×^;ýêW¿šñGõ®óÂ'Ÿ|2í¶Ûné‘GÉî.¦9ÞqÇÓÞ{ïMuSkÇšÉ<ð@ºæšk²©;ž~úéÌjË-·L\pAZuÕU;y%@€@!ñe¢ø=Ÿ?ù²Å[¤óÎ;/ÅïÐXwü /Lçž{nŠY'âKFõJ³ë|ã7Ò%—\’}é*Ö>²Ì2ˤ£Ž:ª^7#@€ @€ @€@¯˜÷IéÕ•.ê–@¬ÚõÒÕ.:thºçž{ÒFmTípöÝxãY`×qQL«£NX`Ž]ƒòuΜ9uÿ¨Î9ç¤ï}ï{ƒâÞgÏžý[‰/D‰ÀõüóÏÏ‚Žj7ÇþûïŸÆ_qx•UVI&LH+­´RÅ1; Vgžy&Ũü(ûî»oºøâ‹³mÿSŒ@„žñ95mÚ´N 6,½òÊ+iĈö¿úê«iÆŒiÝu×í´?ÿ¦YuÆÿ Š0â3ôꫯNï¿ÿ~¾™ô›ßü&pÀöyÓ\Ñ£GgÓö¯°Â iæÌ™Í­\mZH –˜;wnÚxãÓ¤I“Z¨gºB€ @€ô—À‚ýÕp»´ÛÝð+BÆX¯6þ8Ý×ÒµÍW\qЇ´aaw£ÑW}µm•ëô£¥Žv¿ýöK·ÜrKÍ6ú£kÇ—N<ñÄŠ[˜:ujÚ~ûíSL£¬ @ Ù1ð{ìQÒF;_þò—+BÚØÿ©O}ªnHÛÌ:cùX >#»†´Ñ—X.@!@€ @€ @€@‚Ú"TsuÆh¡î–˜Âñ›ßüföMûî^Sí¼E]´Óî®ï;doÚá^cÄWLe«­¶Ê¦ Ý)?ûÙϪŽÚþóŸÿœÎ8ãŒîTáôH >³î»ï¾ª×ĉzSšYçG}T· ‚Úº< @€ @€ ÐAmðºsiOƒÃ;ï¼3|ðÁÝ©ºæ9]Ûìú¾æ…ƒàÀ`ŸÞ9Q¬)ÛQŽ;Ín¿ž|òÉUÏý1ŸB ¿üûë/ùbÛiýk•j#Xk›ßßÌ:GŽ™¯ºb{Ùe—­Øg @€ @€š! ¨m†b:† RçhõC±Þi§Vý`7övm³ëûnTᔈµ'Nœ8¿w1¢v¯½öÊÖö›¿³ÁF\Óv-¯¿þzŠ/ (úK`»í¶KgŸ}¶i¸ûëÔîSO=U³æÏ~ö³5Õ;ÐÌ:_|ñzM™ú¸®Žƒ @€ @€ôE@PÛ½^\ÛÝ5T8âˆtÕUWõ¢— f'žx"½ñÆóo1ÖiŒu9æ˜ùûº³1vìØª§M˜0¡ê~; ”!ðÊ+¯¤C=4­½öÚiüøñ)þ}+_àwÞiúM4³ÎXûV1bD¶Æw­ãö @€ @€ @ /‚Ú¾èõâÚ_WYe•†WÆÈÉ}öÙ'Ýÿý ÏuBûLž<¹êÍÞtÓM=š¶x̘1Uë™9sfÕýv(SàÙgŸM{î¹gØ^tÑEFØ–‰_@[ï¾ûnÓkmfõ–°>mÓ  @€ @€È ô|^ÞÜÅ6{.ðéO:]wÝui“M6IþÐòÈ#Yû¿ÿýïk¶õGHSÿä'?iÊ}Wës¬-»ä’KVÜçn»íÖ£6ãKÕŠ¬šŠ}ý-0}úôtØa‡¥c=6í»ï¾éCÉFÛöw¿´O€ @€ @€ú" ¨í‹^®‘²ÿò/ÿ’Ž<òȆµüùÏNßüæ7³°´»#&VÚà„×^{-í¿ÿþéÚk¯mpfåá)S¦¤ƒ:(ýò—¿ÌÖOp±7eîܹéÔSOM?ÿùÏÓ|P·Š8÷öÛo¯{NOFè{ÜqÇ¥_ÿú×ÙhÕF×Ïž=; ’.¾øâtæ™g¦í·ß¾Ñ%=>¾ð §sÏ=7Yaë ,Ô'tRêŠÉÕJŒV´ªÀ{ï½—ý÷ÿMn¶ÙfÙgT|6Æ:¢Jk ÄïÚk•믿>;#dßÿýŸkñY¯ñû®ÚôÿÍ®sêÔ©µº—Í_¬Š/*½ýöÛó_c‰øâÀ‚ ZA¢&ž @€ @€4Ô6$*î„#Ž8"=öØcéÒK/mØÈ­·Þš"‹®èò§?ý)íÓûV+ÆtÃÇϦCŽ)w«•3f¤­·Þ: V¾ýíoW;¥æ¾X‹5¦<é–û£ÄtÓ1â9F4÷´Äúš;ì°CŠõˆ£Žf—Ýwß=s}òÉ'³i¦»³æq×>Üwß}]weïµUYìlA{î¹'ÅÏ÷¿ÿý#ÊcMÛ-·ÜRpÖ"Ï*¦­Žßq¾ì¿ã§Zé:¿ÙuÆï¨ï~÷»é†n¨Ö|¶ï/ùKÚ{ォÿÑ~Ô/3]TíŒ @€ @€ HCAúù±ýæ7¿ÉÖ«íN7Î;ï¼tÚi§uçÔ^Ÿ#Ÿþîïþ.Õ ic$pŒ¶Q¾öÅš¦12¸V‰QRx`ºí¶ÛjR±ÿôÓOOn¸aÕ6ÖêýÆ7¾‘…Ö†ÆÓf—qãÆeO×6ÖŽ üÈ¢é[o„sÜw¬k[DùÔ§>•6ÝtÓÔ›ö­·ÞJ1s×#ÃÂ]!0b¤ã\¶Ùf›ì‹ 1íúÝwßÝ­Qðé>J_cÔé>ÿùÏ7 iÝÓzë­—Òì:c}ñøÝ_8ªÒÖë_LAßËÔë“c @€ @€ <#jûù™Å®1òrã7®;=dG7cªäÑ£G2R3¦Þe—]Ò«¯¾ÚÑ\§×¯~õ«é?þã?ÒСCçï_j©¥ÒQG•bdÒÙgŸ=~ãã?ÎF»=ñÄ)Æz%ÂÏN8¡â”pŠ)…÷ØcNÇ^~ùåô‹_ü"sÎ9ö÷öÍù矟… ]¯_~ùåÓwÜ‘býÝŽAi¬7S¯VU!v×]w]Ç%-ñÓxÆÔ¢]K¬ý¹Új«uÝ]Êû´#ŒQÚ[ ÖÔÝU‰/WÄ´ãñŸ5±¾w|¹d«­¶J]Ggv·NçõLà™gžIñ9ÚŒÒÔ6»Îø_¤éKéÍ—dúÒ^;]#§ƒU ¾(¢ @€ @€ò‚Ú¼F?m/·ÜrY˜Á_¬ÁX¯DèÓ0Þyçi£6ªwj]tÑEéÁ¬y]„ù6bŒ‚¶µ¦Ô?ŒŸuÖYÙz³ùëòÛG}t:ùä“ó»²í…Z(‘kRv-Ë.»lÇ”§1õiŒàím‰‘Â1UgµS\æCÚŽsbjãm·Ý6Ý|óÍ»:½Æú‹1úø _øB§ýýõ&ÂÐSN9¥¢ùE]4 ¼+”´#Âþâ͈Ϝø7?ñŸ—1êv‹-¶Hó7Ó¬fÔÓE`íµ×ÎBÐÝcÔi|™'Öú®U>ûÙÏf_ö©v¼ã3¿ÙuF¿b ùø7ÛÕfÈ÷çCɿͶc4®Ò|øÿ8cÆŒi~Åj$@€ @€ Т‚Úy0ë®»nºì²Ë²­¾m?kÖ¬l”ؤI“Rü‘»%F„žzê©5«Š ±Þú¥1ð?ýÓ?Õ j£âõܰaÃ*ډ鄫…´qâ׿þõÔñûŠ ÿº#¦CŽ yŸ}öIüjÕqüñǧ7Þx£âpL ¼×^{UìïØq­ 6Ήõv[%¨=üðëŽÜþéOšV\qÅŽ[òJ`P Äç[¬;ýÈ#¤‰'fŸ'_þò—Õ=¶ÊÍÄç{×µÔ¯¼òÊì +ÕúŸ­tPµCó÷Qçøñãç×_Щõû/~7Ç—Œ @€ @€! ¨-Bµ—uî´ÓN餓NJÇsLÃbŠÏX£õÞ{ïmÊ:y·ß~{zî¹çj¶Ah£ý©6ò¨ãº±!ÉÖ[oݱkþkµQž>øàŽÍº¯ÿøÿ˜µÿæ›oÖ=¯ÚÁÿýßÿ­:΋µë…˜1J¯^yôÑGë.íX„ÉÕ¦$Ým·ÝRµýY"4[`ú³ Únk¯½6½þúëMëIü›Šu—cÝíøït“M6™?+@L§« ÐZñßì~ûí×ZÒô×RÜŠ*  @€ @€> jûØìËcúßÇ{,Å:¢JL©!Û 7ÜbDk_JL¥\¯|ñ‹_¬w8;köÅt?þxÍsï¾û6“X§·Z‰u%c}É¢Ëÿ÷W]g6Ú5jTÝæcúåøãr­‘¼1Н¿Ë[o½UuíݱcǦK.¹¤ßCÒo}ë[)~”öˆ5IûÔÆôìñe‰ëÓ®°Â íêî ø]zá… ë* @€ @€¾ ,Ø·Ë]]„@ŒzŒ­;å–[n©;е;uÄ9&L¨{ê2Ë,S÷xÇÁXÿ±^¹ÿþû+7.źtÕÊg>ó™j»›¾ïøCÍ:µ’/½ôÒ5¯Ÿ9sfÍceˆ5vcÔp¾Ä¿±eëÓ*²@¬Aál|v¾ôÒK馛nÊÖIÒ䧪ï @€ @€ü}†9ø}úå#8»úê«ÓÆoœþò—¿4ìÃyç—V_}õôãÿ¸á¹µNh4 hwƒÚ]Z¯¼ú꫇ŸþùŠ};Ê j§L™ÒÑdÅkÉ÷ÜsO¶¶îˆ#²×XóröìÙóbZçV-gœqF¶~o¾›o¾y6{ñÅÏï¶M`@ Œ3&‰ëc/¿üòªï:K€ @€ @€µ-úo B‡X¯q³Í6K³fÍjØË#<2=:í¼óÎ Ï­vB£éFëÍ××è¼jíL:5_E§íFõu:¹oê­áÿä“Oö¡öþ»ô²Ë.K‡vX§Ä”°W\qE>|x§ýÞ‹,²HÚ}÷ݳ©¼ãóQ!@€ @€ @€UÀÔÇ-üä¾ô¥/¥ÿ÷ïÖú¡1uðÞ{ïªM-Üèß}÷Ýôá‡6:­[Ç«o¿ývE=Ó¦M«Ø×±ãÍ7ßìØ,ôµÚHß¾6Òlþáþ¡¯Uõêúo¼1í·ß~ÖÎ=ꨣ²ÑÚBÚ^‘º¨btý‰'ž˜¦OŸž­«,¤íLJ¡i @€ @€hŠ€µMa,®’]vÙ%ýâ¿HÇ{lÃFbäç×¾öµ,ˆkxrî„÷ß?÷®úfŒ„íÎ4ÄK-µTõ þº·Z@Xo ×®ëªÖ­¼Õ1‚¯V‰@vÉ%—LqïñÓ?¯³Î:iíµ×N±~m”Ûo¿=íºë®iîܹYóÑÇX¿3Â|…À@Xk­µÒÑGöÜsϴ𠤮ë+ @€ @€¨+Ð?)RÝ.9ØUàg?ûYzüñÇS¬•Ú¨¼øâ‹i‡vhtZ§ã#GŽìô¾Ú›jSW;oذaÕvÏßWmÄíK,‘j…Åeµõú°à‚ ¦K/½4 :tþ}´òF¬§Ów˜ÆúÂ1ö¦›nÚÊÝÖ7Ö[o½ôÓŸþ4Å—Uâ¿A… @€ @€ 6ý Oô /Lo¼q·zÛÝPµ£² «tí8¯Ý­³ÚÔÆùzªµ±¶n­òÎ;朗uÖ°­u]O÷ÇHØZ%¦•~þùçkn©ý1õuõï½÷^Ö¯•W^9Ý{ï½ CÚ+¯¼2üñé‘Gi©ûÑ™öXuÕUÓå—_žzè¡lT¸¶ýþ ¸c @€ @€´‹€ v€<éE]4]sÍ5iÔ¨Q…ôøÓŸþtÝz_{íµºÇ;6 tW_}õŽSç¿®¶Újó·«mœ{î¹ÕvWì›7o^¯×Úmtÿ1JµÕËäÉ“ÓW¿úÕÔ–ÇôË'NLcÆŒiØõ“O>9›bûßþíßžëE Ä—¦L™’öÚk¯n­Í]T?ÔK€ @€ @€ÊÔ–¡Ü¤6VXa…l ÛF£_{Ó܆nX÷²x îñŽƒÝÍ7ß¼ãÔù¯‚Ú .¸`þ4¾ó/ª²ñÄOÌIZåpÝ]믿~Ýã1¢¹•KL½í¶Û¦7Þx#ëf8O˜0¡[ë Çú¼=öXv]¬««è/wÞyÀL1Þ_FÚ%@€ @€ @€Á# ¨-øYF–/sçÎÍ¿íñöl.¹ä’¦6k´~é 7ÜЭ¾6ZS¶7Aí«¯¾šN?ýô†íŸwÞy Ï©uÂ&›lRëP¶?F¦Þwß}uÏ鯃O=õTÚzë­Ó+¯¼’u!®[n¹%Õ›Î9ß×G}4Í™3'Û%¨ÍËØ&@ ÝfÍšÕîîŸ @€ @€µâFÕï¿ÿ~§fÏžÝé}oÞìºë®é”SNéÍ¥5¯Ù~ûí놿ÀþéOªy}Ç›nº©c³â5¦å­6uóرcS£u(9昺aí™gž™â§·eË-·l8’o·ÝvKF ÷¶ýÞ^÷ÜsÏe!í‹/¾˜Uqà¦ÿüÏÿL1UvwËï~÷»ù§ jçSØ @€@š1c @€ @€& ¨-ŒöÿWÜ5˜íú¾·Íÿä'?IÊu§ÄÚ­J¬cºÝvÛÕ=-Àz%¦Ï}æ™gjžrÔQGU=mï±ÇUåwvØaé;ßùNŠÑ½o½õVzùå—ÓÍ7ßœ6Ûl³ôƒü jÕí®¡yþ¤e—]6í¾ûîù]ÛÓ¦MK[mµUЬÊÇœMS}öÙg7:µ×ǧOŸž…´£˜÷Þ{ïÿ.žþùôì³ÏVý‰¾ÇѺßyç)¦t¾è¢‹²>,²È"iôèѽî  PM Þgoog™hfË,³LµngûbDíÕW_]ó¸ @€ @€è‹À¾\ìÚÆ1â1_"DkV9çœsR„uÿõ_ÿU·Êîþ!üè£Îêªìžzê©i›m¶I1ú´Z9묳ªíÎöí¸ãŽé+_ùJÍã?ÿùÏÓ•W^™õõ·¿ýmŠŸÞ”7ß|³îe‡~xºâŠ+ÒG}Tó¼Gy$ÅôÓˆpÀ)Ö Î—×_=]|ñÅ)ÚŽgçÿíßþmþ´>oÏœ93 i§N:¿®Ë.»,ÅOoËk¬‘Zh¡Þ^î:T¨÷Ùûî»ïV½¦ÑÎfֹꪫÖm.¾sðÁ§u×]7û2R|A(¾ ¿ß[l±º×:H€ @€ @€zFÔÖÓéã±?üá)FaæK„‘ï½÷^~W¯·#T‹úÖ_ýºu|ðÁuwŒ‘©GydÇÛŠ×Xo7¦]®6¢ô„NHµÖˆ]mµÕÒ¥—^ZQ_~ÇꫯžþùŸÿ9¿«ÇÛ?üáë^sÇw¤éZ«Äáã>•Ž?þøôÙÏ~6m¸á†YpÏ ¦uŽ‘Yøv„´±Nì¾ð…FUöèxŒ$Ž5ië^îQ…=ùsŸû\o.s j <ôÐC)Ö¯Ubdÿo¼QëpÕýÍ®³QP£jã‹JØÆïˆ?þñÙšàÍþ ®z³v @€ @€ 0¨† ê»+ùæb´å%—\’MÉ;yòäSw-wß}wèm¼ñÆ)Ì•W^9ÅHÖÞ–ÍëÂÆˆÍŽp°k]õ¦ˆìzî‰'ž˜}ôÑtã7v=”½£èE{1B6Bç{î¹'ÝÿýUÏ?€_ýõi©¥–ªz<¿3þ÷£E{R† –Má»ùæ›§3Î8£æ¥ÑÏc7Ùd“,P=è ƒÒJ+­ÔéüX 7B€îLu£|ðÁN×w}óÝï~7-¾øâ]w÷ú}±Öï”)Sz]G­ ­O[KÆ~º+ðÀ¤«®º*½óÎ;وӻî¥ñY_Ô‰/»Äk|^®¹æšéÛßþöü늨s~åŸl|ñ‹_Ì~wýbUþœ®Û1ÿzë­×u·÷ @€ @€è‘ÀŸLsÛxÓUÙ¾'Ç´³ûì³ObmÔ—^z©G×T;9F¹FùÚk¯U^e•Uj†¸'²#¦þ?BŸyæ™Õw{ß¶Ûn›Æ—…¢Ý½è駟ΦVž1cF·.‰ õšk®ÉþÈÁxOF¯Fh£ˆ»–uëéÆª¾”±cÇfE×é‘ûRçµ×^›vÚi§¾TQóÚßýîwi·Ýv«y܃I FCÆtßQöÝwßlÊòÁtýu/1%ÿ!‡Ò§æã‹L“&Mš_Guίü¯ãÇO{î¹g×ÝUßo¿ýöÙ\Ф$Uz±3ÖG©¤ã÷eO¿¬Õ‹æ\B€ @€ @ eü•±‰b¹å–kbm=«*F ÅÈÕ]ڵ̞=»ë®ºïcJå_ýêWi„ ٨ٺ'W9¸é¦›f#co½õÖ…´QU'?üpÚo¿ýÒ ,P¥öÿÛíÄHÞŽ©Ÿcjæî–!C†Ôì[üñý_ÿõ_ÓĉÓßÿýßw·Ê켘êøÐCM1ºúþç*Ö°íQeUN>|x•½ÍÙeDmsÕB –_~ù>ß~×:º¾ïMêØ}÷ÝÓGÑpî˜Q"¾€$¤íÍSp  @€ @€]Œ¨í*ÒÇ÷1ýpŒ‹,²HZtÑE³×¡C‡fÔ‘ª1]nLEáiüľY›Už}öÙÓDÆt’O<ñDöám„†½-QÏm·Ý–b ËéÓ§g£vcMÁ¸¯¥—^: ´ã>#jÛñ©»g @€ @€Ôúw@€m' ¨m»Gî†[X@PÛÂG× @€ @€ 0õq¡¼*'@€ @€ @€ @€@¥€ ¶ÒÄ @€ @€ @€* ¨-”Wå @€ @€ @€¨ÔVšØC€ @€ @€ @€Bµ…òªœ @€ @€ @€•‚ÚJ{ @€ @€ @€ P¨€ ¶P^• @€ @€ @€ @ R@P[ib @€ @€ @€ ÔÊ«r @€ @€ @€T j+Mì!@€ @€ @€ @€@¡‚ÚByUN€ @€ @€ @€JAm¥‰= @€ @€ @€(T@P[(¯Ê  @€ @€ @€ P) ¨­4±‡ @€ @€ @€… j åU9 @€ @€ @€*µ•&ö @€ @€ @€ @ PAm¡¼*'@€ @€ @€ @€@¥€ ¶ÒÄ @€ @€ @€* ¨-”Wå @€ @€ @€¨ÔVšØC€ @€ @€ @€Bµ…òªœ @€ @€ @€•‚ÚJ{ @€ @€ @€ P¨€ ¶P^• @€ @€ @€ @ R@P[ib @€ @€ @€ ÔÊ«r @€ @€ @€T j+Mì!@€ @€ @€ @€@¡‚ÚByUN€ @€ @€ @€JAm¥‰= @€ @€ @€(T@P[(¯Ê  @€ @€ @€ P) ¨­4±‡ @€ @€ @€… j åU9 @€ @€ @€*µ•&ö @€ @€ @€ @ PAm¡¼*'@€ @€ @€ @€@¥€ ¶ÒÄ @€ @€ @€* ¨-”Wå @€ @€ @€¨ÔVšØC€ @€ @€ @€Bµ…òªœ @€ @€ @€•‚ÚJ{ @€ @€ @€ P¨€ ¶P^• @€ @€ @€ @ R@P[ib @€ @€ @€ ÔÊ«r @€ @€ @€T j+Mì!@€ @€ @€ @€@¡‚ÚByUN€ @€ @€ @€JAm¥‰= @€ @€ @€(T@P[(¯Ê  @€ @€ @€ P) ¨­4±‡ @€ @€ @€… j åU9 @€ @€ @€*µ•&ö @€ @€ @€ @ PAm¡¼*'@€ @€ @€ @€@¥€ ¶ÒÄ @€ @€ @€* ¨-”Wå @€ @€ @€¨ÔVšØC€ @€ @€ @€B†Z»Ê  @€@‹ <ýôÓé‚ .hñ^êÁ+ðöÛoÞ›sg @€ @€êjëà8D€ƒ_`âĉ)~ @€ @€ P¦€©ËÔÖ @€ @€ @€>0¢Ö?h;å–[.]tÑEmwßn˜@+ 6¬•»§o @€ @€š.°À¼OJÓkU! @€ @€ @€Ô0õqM @€ @€ @€ PŒ€ ¶Wµ @€ @€ @€ @ ¦€ ¶& @€ @€ @€(F@P[Œ«Z  @€ @€ @€ PS@P[“Æ @€ @€ @€# ¨-ÆU­ @€ @€ @€¨) ¨­Iã @€ @€ @€ŠÔãªV @€ @€ @€ÔÔÖ¤q€ @€ @€ @€Åj‹qU+ @€ @€ @€j jkÒ8@€ @€ @€ @€bµÅ¸ª• @€ @€ @€5µ5i @€ @€ @€ @€@1‚Úb\ÕJ€ @€ @€ @€š‚Úš4 @€ @€ @€ @ Am1®j%@€ @€ @€ @€@MAmM @€ @€ @€ PŒ€ ¶Wµ @€ @€ @€ @ ¦€ ¶& @€ @€ @€(F@P[Œ«Z  @€ @€ @€ PS@P[“Æ @€ @€ @€# ¨-ÆU­ @€ @€ @€¨) ¨­Iã @€ @€ @€ŠÔãªV @€ @€ @€ÔÔÖ¤q€ @€ @€ @€Åj‹qU+ @€ @€ @€j jkÒ8@€ @€ @€ @€bµÅ¸ª• @€ @€ @€5µ5i @€ @€ @€ @€@1‚Úb\ÕJ€ @€ @€ @€š‚Úš4 @€ @€ @€ @ Am1®j%@€ @€ @€ @€@MAmM @€ @€ @€ PŒ€ ¶Wµ @€ @€ @€ @ ¦€ ¶& @€ @€ @€(F@P[Œ«Z  @€ @€ @€ PS@P[“Æ @€ @€ @€# ¨-ÆU­ @€ @€ @€¨) ¨­Iã @€ @€ @€ŠÔãªV @€ @€ @€ÔÔÖ¤q€ @€ @€ @€Åj‹qU+ @€ @€ @€j jkÒ8@€ @€ @€ @€bµÅ¸ª• @€ @€ @€5µ5i @€ @€ @€ @€@1‚Úb\ÕJ€ @€ @€ @€š‚Úš4 @€ @€ @€ @ Am1®j%@€ @€ @€ @€@MAmM @€ÿמÚ ûÿk.@ÎU“ jG€ @€ @€¡¶qõJ€ @€ @€ @€+ Ô^ @€ @€ @€¡¶qõJ€ @€ @€ @€+ Ô^ @€ @€ @€¡¶qõJ€ @€ @€ @€+ Ô^ @€ @€ @€¡¶qõJ€ @€ @€ @€+ Ô^ @€ @€ @€¡¶qõJ€ @€ @€ @€+0£2ćbã(XIEND®B`‚upstream/docs/design/images/bluestein_fig1.png0000775000175000017500000025644514637212246020464 0ustar kaolkaol‰PNG  IHDR è!×o'iCCPkCGColorSpaceAdobeRGB1998(‘c``RH,(Èa``ÈÍ+) rwRˆˆŒR`ÎÀÊ Á ÀÀÌ`œ˜\\ààÃ0|»ÆÀ¢/ë‚ÌÂ”Ç ¸RR‹“ô ÎN.(*a``̲•ËK @ì [$)Ì^bdo±Ó!ì`5ö°š g ûÍ—f3ìâK‡°@l¨½ 蘒Ÿ”ªò½†¡¥¥…&‰~ JR+J@´s~AeQfzF‰‚#0¤R<ó’õtŒ ŒŒ@áQý9žŒbgb€›#ÁÀà¿”åB̤—aÿT„˜š!ƒ€>þ9É¥EePc™€vâ1HJ_G9+8eXIfMM*‡i  è ”Òž@IDATxìÝ ¸]ÓÝ?ð_BbŒDSJDjž)5!Â[ÔL«”¢¨¡ÆòzÕ<KÕPEiÕSš’ hbÓ˜*cbˆˆÈ¿kÿßsÞsî½Iî»é³žçäì½Ï>k¯õYç}ëœûÝku˜ñŸ  @€ @€ @€ @€ ЮÛµ6• @€ @€ @€ @€ @€™€€ž @€ @€ @€ @€ÈA@@/TU @€ @€ @€ @€ @€=Ÿ @€ @€ @€ @€ ƒ€€^¨ª$@€ @€ @€ @€ @€z> @€ @€ @€ @€ @ ½PUI€ @€ @€ @€ @€ô| @€ @€ @€ @€ @€@z9 ª’ @€ @€ @€ @€èù  @€ @€ @€ @€ @€ôr@U% @€ @€ @€ @€Ðó @€ @€ @€ @€ @€9è倪J @€ @€ @€ @€  ç3@€ @€ @€ @€ @€rÐËU• @€ @€ @€ @€ @@@Ïg€ @€ @€ @€ @€ä  —ª*  @€ @€ @€ @€ @€€€žÏ @€ @€ @€ @€ÈA@@/TU @€ @€ @€ @€ @€=Ÿ @€ @€ @€ @€ ƒ€€^¨ª$@€ @€ @€ @€ @€z> @€ @€ @€ @€ @ ½PUI€ @€ @€ @€ @€ô| @€ @€ @€ @€ @€@z9 ª’ @€ @€ @€ @€èù  @€ @€ @€ @€ @€ôr@U% @€ @€ @€ @€Ðó @€ @€ @€ @€ @€9è倪J @€ @€ @€ @€  ç3@€ @€ @€ @€ @€rÐËU• @€ @€ @€ @€ @@@Ïg€ @€ @€ @€ @€ä  —ª*  @€ @€ @€ @€ @€€€žÏ @€ @€ @€ @€ÈA@@/TU @€ @€ @€ @€ @€=Ÿ @€ @€ @€ @€ ƒ€€^¨ª$@€ @€ @€ @€ @€z> @€ @€ @€ @€ @ ½PUI€ @€ @€ @€ @€ô| @€ @€ @€ @€ @€@z9 ª’ @€ @€ @€ @€èù  @€ @€ @€ @€ @€ôr@U% @€ @€ @€ @€Ðó @€ @€ @€ @€ @€9è倪J @€ @€ @€ @€  ç3@€ @€ @€ @€ @€rÐËU• @€ @€ @€ @€ @@@Ïg€ @€ @€ @€ @€ä  —ª*  @€ @€ @€ @€ @€€€žÏ @€ @€ @€ @€ÈA@@/TU @€ @€ @€ @€ @€=Ÿ @€ @€ @€ @€ ƒ€€^¨ª$@€ @€ @€ @€ @€z> @€ @€ @€ @€ @ ½PUI€ @€ @€ @€ @€ô| @€ @€ @€ @€ @€@z9 ª’ @€ @€ @€ @€èù  @€ @€ @€ @€ @€ôr@U% @€ @€ @€ @€Ðó @€ @€ @€ @€ @€9è倪J @€ @€ @€ @€  ç3@€ @€ @€ @€ @€rÐËU• @€ @€ @€ @€ @@@Ïg€ @€ @€ @€ @€ä  —ª*  @€ @€ @€ @€ @€ÀÜ @`Î>þøã¸òÊ+çìÍÞE€@. ,°@tÐA¹Ô­R @€ @€Ú_àÎ;^z©ý+V#Øf›mbå•W®Èµ]”T«@‡ÿ)ÕÚ8í"@€@5 Œ;6–_~ùjn¢¶h8Þ½{Çøñã®ß:L€ @€ @ VvÝu׸ùæ›kµùÚM€@ßýîw±÷Þ{79j—4¶€%n{üõž @€ @€ @€ @€r°ÄmN°ª%@ ±6ÝtÓ8üðëÓzK Š8à€x÷Ýw«¨EšB€ @€ @€@[®¾úêèÑ£G[ßæ|*,0lذ¸ôÒK+Ü —'@€Õ+  W½c£eÔÀRK-C† ©¡k*ú8âˆ#ê«CzC€ @€ @ ¶ÜrËXrÉ%°çºL ¶&L˜PÛÐz ³€%nsV= @€ @€ @€ @€4¦€€^cŽ»^ @€ @€ @€ @€ @€@Îz9«ž @€ @€ @€ @€S@@¯1Ç]¯  @€ @€ @€ @€ @ g½œUO€ @€ @€ @€ @€)  ×˜ã®× @€ @€ @€ @€ ³€€^ÎÀª'@€ @€ @€ @€ @€ÆÐkÌq×k @€ @€ @€ @€ÈY@@/g`Õ @€ @€ @€ @€ @€@c è5æ¸ë5 @€ @€ @€ @€ä,  —3°ê  @€ @€ @€ @€ @ 1ôsÜõš @€ @€ @€ @€rÐËXõ @€ @€ @€ @€ Иz9îzM€ @€ @€ @€ @€9 èå ¬z @€ @€ @€ @€hL½Æw½&@€ @€ @€ @€ @€œôrV= @€ @€ @€ @€4¦€€^cŽ»^ @€ @€ @€ @€ @€@Îz9«ž @€ @€ @€ @€S@@¯1Ç]¯  @€ @€ @€ @€ @ g½œUO€ @€ @€ @€ @€)  ×˜ã®× @€ @€ @€ @€ ³€€^ÎÀª'@€ @€ @€ @€ @€ÆÐkÌq×k @€ @€ @€ @€ÈY@@/g`Õ @€ @€ @€ @€ @€@c è5æ¸ë5 @€ @€ @€ @€ä,  —3°ê  @€ @€ @€ @€ @ 1ôsÜõš @€ @€ @€ @€rÐËXõ @€ @€ @€ @€ Иz9îzM€ @€ @€ @€ @€9 èå ¬z @€ @€ @€ @€hL½Æw½&@€ @€ @€ @€ @€œôrV= @€ @€ @€ @€4¦€€^cŽ»^ @€ @€ @€ @€ @€@Îz9«ž @€ @€ @€ @€S@@¯1Ç]¯  @€ @€ @€ @€ @ g½œUO€ @€ @€ @€ @€)  ×˜ã®× @€ @€ @€ @€ ³€€^ÎÀª'@€ @€ @€ @€ @€ÆÐkÌq×k @€ @€ @€ @€ÈY`îœëW= @€ @€ @€ 0‰'ÆØ±cãµ×^‹É“'GŸ>}¢oß¾±Ì2ËÄ‚ .8›w{™¨V½jí"@€ @€ @€ @€º¸óÎ;ãä“OŽÑ£GÏ´¯›l²I{ì±±õÖ[Ïô/ @€Õ)  Wã¢U @€ @€ @€ PÇï¿ÿ~ 2$}ôѬ—óÍ7_¬µÖZ±êª«Æ„ â¹çž‹_|1{mĈ‘«­¶ZÜvÛm±ì²ËÖ±Œ® @€úЫ¯ñÔ @€ @€ @€¨rI“&Å[lO?ýttéÒ%®»îºØvÛm£S§Ne-¿ûî»ã裎^x!;>f̘ØrË-cÔ¨Qѳgϲsí @€Õ)б:›¥U @€ @€ @€ @ >.¾øâ,œ—z—fÆÛa‡š…óÒkÛl³M<þøãÑ¿ÿ´›•±cÇÆàÁƒ »ž  @€*0ƒ^•æ @€ @€ @€ @ š>øàƒ,\öÉ'ŸÌq³]tÑØ`ƒ Úüþ>ú(þö·¿ÅC='NŒå–[.vÚi§Xc5Ú\W%ßpùå—/¿×^{Ešoî¹[þóý‚ .˜-k»Ê*«Ä´iÓ²÷=ñÄ1nܸèÛ·o± @€@u ´ü¿ðÕÙV­"@€ @€ @€ @€ 6,Ž8âˆâr«ß´7ÜpCì¾ûî­®æì³ÏŽã?>¦OŸ:tˆ3fdï=óÌ3ãÈ#ŒsÎ9§ÕuUòÄ©S§ÆÛo¿]lÂóÏ?÷Þ{ï,gÅëׯ_ 0 F]|ßÈ‘#ôŠ6 @€@õ Xâ¶zÇFË @€ @€ @€ Pq¯¾ú*öÙgŸØzë­Û-œ—:5a„Võí‹/¾ˆwÜ1Ž9æ˜X|ñÅãÚk¯Ï>û,ž|òÉèØ±cÔ;÷Üsã®»îjU}•>©sçαØb‹•5#õcv%Í WZJÃz¥Çm @€Õ%`½ê­!@€ @€ @€ @€@Õ|ýõ×±÷Þ{ÇM7ÝTlS—.]bÕUWX Fi©Û%–X"›á­pÒsÏ=ÿþ÷¿£wïÞÙ¹…ãé¹S§N±æškÆþûï_zx¦ÛG}tüéOÊêà¢W¯^Ù¹©ŽVX!^zé¥l?-}»í¶Ûδžjy!ÍþwÒI'Å!‡’… ‡ ƒ šmó&MšTvN²U @€êЫþ1ÒB @€ @€ @€TDàØc-†ó=ôÐ8è ƒ²P\ ™¥òƒü n¿ýö8üðÃ㨣Ž*¶1-;{ÜqÇÅöÛo—]vYñx[7R ï’K.‰®]»Æý÷ß_ ç¥zÒ·ï¿ÿ~±Ê¹æš«¸]íx`ì¼óΑf',gÕæ´¬ïSO=UvJÓõÊ^´C€T€€^Õ …† @€ @€ @€ @ z>üðÃ8ÿüóc¾ùæ‹+¯¼2öØc²Æ¥Ýî¾ûîìØæ›o^öÚã?ží§%i¿I¹à‚ ²·Ÿzê©Ùl|¥u½ð e½4«_-•îÝ»·º¹×_}¼õÖ[Åóûôé›nºiqß P½zÕ;6ZF€ @€ @€ @€Š tëÖ-{ì±lùÚÅ[¬Y;n»í¶˜:uj¤óš†ãÚ+ —fæû,žxâ‰â5ÒҶ믿~q?o¼16Ûl³léÞÕV[-žzê©øéOÚìR¿ýío£GqÚi§5{Í @ úôªoL´ˆþ#0lذXf™eâ¿øEŒ92&L˜¹|øá‡ñä“OÆñÇßùÎwâ¶Ûn+z¥íþýûÇ¥—^¯¼òJ¤;ÈÒ|/¿ür\{íµ±á†Æ/ùËâù6 @€ @€ @€¶ \|ñÅñùçŸgoêÒ¥K(kZüóÎË/¿|ñp ”ÞS<ø 6žþùxÿý÷‹5äÐK7ý§¿3JšÕ.õ+¯òßÿýß±Ç{d³âýüç?Ç{,›€ ¥ë¥×RYn¹åZzÙ1 @ Êôªl@4‡"»“nÛm·I“&e;ï¼s\qÅqÿý÷Çé§Ÿ^üüé§ŸÆN;í·ÞzkÜqÇñÃþ0 䥻ò;ì°¸ýöÛ³©ß^xá"ë9çœ7ÝtSqß @€ @€ Ðz«¯¾ºxrúý>­fÓRI«áÊG}”Ý|_Øÿ¦Ï¥ËÛ¦ºòè >¼¬™y\#] M4°ûî»Ç)§œݺu‹?ÿùÏqÑEE𱝥òÉ'ŸÄ]wÝ•½´òÊ+·tŠc @€@• Ì]eíÑhpc=6Î:ë¬L¡oß¾qå•WÆæ›o^TÙb‹-²™óÒ—þBÙe—]¢S§Nñõ×_G×®]³°^á‹ò 7ÜP úÎðÁc·Ýv+ìz&@€ @€ @€Z)°à‚ fg.°ÀqÔQGÍô]'œpBÜ|óÍÙ,t+¬°B».[Ð[i¥•¢W¯^3mÇœ¾PzTGáïsZ_Kï›8qbì°Ãñè£f/§~¤U‚Ò£´LŸ>=sœ:ujüë_ÿÊ–ÀíØ±c¶ªPéy¶  @€êЫÎqÑ*4¤À}÷ÝW ç­¸âŠÙÒEY¤™Eš5o饗Ž7Þx#{-óÒ—ÒT†Zü’üÕW_Åá‡3fÌÈ^+ü3nܸ¦g @€ @€ @  if¹^KK¾Îj‰Õ~ýúÅË/¿cÆŒ‰m¶Ù&»Ñ¾ —™é©é7ÿ#F_Ï#8÷ÙgŸ•Íø—f³[ýõ‹×l´LïàÁƒãõ×_/V÷â‹/Fz´¦¤IæŸþÖœê @ Âz—'@ }~ûÛ߯Ž;î˜Í¢Ö¾5«-o)S¦ÄÏ~ö³ì2‹.ºhÜ}÷ÝÑR8¯ÐŽ>ø °Y|NËâ–ÎŒ7a„xÿý÷‹¯66Üpæg @€ @€ @  ‹/¾x¶$kkÞ’BdéÑžåñÇ/ûíàÀíY}V×È‘#³ë §0â¼óÎ[Ø-{þüóÏ㦛nŠ´zO Û¥YðRðnß}÷:”[ØùòË/³ÕƒÒß1æ´ 0`Nßê} @€À·,Ðñ[¾žË @ W‹/¾8»[ë7¿ùM¤/7JíœwÞyÙ´ì©Å—]vY,»ì²³lü /Üìõ<°ìXú‘ éÝ{iÊýÃ;¬ì<; @€ @€ @€@u¼ûî»qÐAeÁ¾ã?¾Y£®ºêªâ±>}úÄvÛmWÜo¯Ö,o›fò»üòËc©¥–ŠýöÛ/ 饥jï¸ãŽlÿàƒžisÒÊ@~øáL_oÍ +¯¼rkNs PzU0š@€@û ¤™ÕÒ²¦+¬°B\zé¥ñÅ_´ïÔ–‹À7Þ˜Õ›¦¼ÿ¯ÿú¯Y^#ÝQööÛo—Ó£G4hPÙ±tgZ û¥éí»víiæ¼»îº+ºwï^vž @€ @€ @ :~ýë_ÇСC³ÙèÎ8ãŒ7n\±a)8wÍ5×÷Ó߃枻ý›]@oüøñ±É&›Dš8àã?ŽvÚ)î½÷Þ¸à‚ ŠmK}hi5 tBš/M4‘B~sú8í´ÓŠ×²A€T·@ûÿ×Ju÷Wëh 7Þx#ÒÝI§žzjyä‘qÀD—.]H vºšÂv/¾øbÖà£>z¦S¾z”î@kZÒá¹æš«éáØb‹-²G³ @€ @€ @€ªNàÏþs±M)È–nÀOåþûï=öØ#Òìs©ì¹çžñóŸÿ<ÛnÏ>ûì³xòÉ'‹UÎ3Ï<±þúë÷G•òRH/­âsýõ×Çzë­S§NÝvÛ­x^ÚxõÕWMP&b‡4¦€ôsÜõš@C ¤©ÐSèk‰%–È–6M_†”êèÕ«Wö¥zÈ!±ûî»Ï¶qÿûß›“z  @€ @€ PÛë®»n±)œwÊ)§Äæ›o[mµU¼÷Þ{Ñ©S§8æ˜câºë®Ëeö¼‘#GÆ´iÓŠmHíIAÁTÒª=›nºi¤pÞ;ìcÆŒÉÂyéµ4ëÞG}”6³Ò¹sçl™Þ¾g @ q̠׸c¯çNà“O>‰‹.º(.¾øâØzë­cÿý÷Áƒg_ä£Ê:œ¾L§;ÌZ[Z è¥el @€ @€ @ ¶®½öÚ,Ø–fÒK“.¤¿í¤€ÜÊ+¯Ûl³Mzè¡Ù¤ yõ²¥åmÓìx‡rH\uÕUÙeO8á„l§:›±êª«F 奥kSI³ý¥  @€€€žÏ '0cÆŒ¸çž{²GÏž=cï½÷ŽÿøÇÑ¿ÿ†³¨ÅOž<9»#­´íÝ»w”²M€ @€ @€Ô À‚ .çœsNöHËÍ~þùçÑ£G( ÃåÙ­¦½4É@ZÂ6Í–—‚‚W_}u‹«õîÝ;[†wذa‘&Øb‹-òl¦º  @€Ы¡ÁÒTÚ_`âĉqî¹çfUVY%vÛm·Øu×]c™e–iÿ‹©±]y䑘>}zY]o¼ñ·öżìÂv @€ @€ @€@+Ò lwÜqG|úé§­|Ç7;m¾ùæËf›;î¸ãÚ\ÑÍ7ßW^ye›ßWMoH;8餓ÚÔ¤46O>ùdÙ{~õ«_eûË-·\ÜtÓM±öÚk—½^º“®™  @ T@@¯TÃ6 -ðÌ3ÏDz¤/ªk­µVl¿ýöÙã»ßýnC»T[çGŒѬI›l²I³cßä@ ÞrË-ñÄOÄ’K.k¬±FvØa‘~ÌhZ~øá¸ýöÛã_ÿúWL˜0!]tÑXvÙe³e”ÓRÊ  @€ @€HK¤¦™×¾øâ‹o cÒ¤IqÖYge÷hëE_ýõxðÁÛú¶ª:‘Eis{FÓ¦Mkñ}cÇŽƒ>8û›AšðÁä-29H€´ P½»ï¾;†ÚBó"@€@¹À¸qãÊÌá^º;*=Ò]Qi6½-·Ü2›Š|³Í6‹´œªR9–ziªøö*éîÅwÜ1¾þúëb•)¬wíµ×Æþð‡XmµÕ²ã?þxüä'?‰çŸ¾x^éÆo~ó›XguâÏþs¤ií•oG -a½ÝvÛ};s9 ¤%»Ï8㌜jW- @€ @€@%æ™gžøýïifºo+¤·ÐB e¿wÏIøÃŸ“÷WÃ{æä·ù¦ËÛ.¾øâÙx}øá‡Y—Òýéqùå—g3éýå/ñ7€&ƒ–øMWQT‹Àž{î»ì²Kµ4G;hPšè¥;4þú׿6èé6•Hÿ?èŠ+®È;vŒ•W^9¶Új«l†´J·­Ñ®Ÿ¦–ꩧʺ½ð Gkf9|ÿý÷³¥q{ôèsÍ5WY…^x!vß}÷,œwä‘Gfw¿zè¡ñÆoÄË/¿?øÁ"sÉ%—ıǛ՗œû↓¹æšÙ {·ÞzköYIwCþãÿˆwÞ9†sÏ]ÿ“[ ¨ÙçÐóß 5;|þ¿“'OfA€ @€ P‡;í´S¤G-”¾}ûFz4ZiÐûãÿn¸a¼óÎ;qã7ÆÅ_o¾ùfÆ’‚zë®»n¶:S×®]j¦ýM>i¥"…@µ¤ÿ;U PiiJ€ë P]ºt‰´ŒêÀ³ÐÖ*«¬í5[_MTI#Óº¯¾úª¬5m´Q¤àä¬Ê—_~™-[œB/×\sM¶tqKçŸrÊ)1eÊ”,ˆwî¹çf§<ôÐC‘fÃK%yš]/ÍìÚ¡C‡8è ƒ²å\pÁìõôOšÒ>}ùL3祒ڜfÛK_à @€ @€ @ :Ò$)tW(;wÎnÎOûi&½£Ž:*ÒL\›nºi¼ôÒKÙiéï÷ß³àeZ¥gúôéÑ©S§Buž  @€¨‰€Þ~ûí—ÍhÔÀãTñ®ßtÓMY%5䢋.нöÚ«âmÒ- ¤pÔ³Ï>ÛÒKm>¶Â +Ä!C²Çúë¯o´6 ¶í i†ºÇ{,zöì™![šq®éké ­ ¾¥%Ò,x©¤ú[*o½õV¤ÙïRùŸÿùŸâ) ,°@q;m¤p^*—^zixàÙvÓ¶ÝvÛb@/½6f̘Vµ³i=öÛ.š}ôQÛßè*,~¬K3|* @€ @€jY ÝdŸ~ëjZÒoþ3[ݦ鹅ýTWK+H¯§\*³»?;©•ÿŒ5ªl’€ÕW_=æwÞ²w§es÷Ýwßøå/Y<žnÔ/1­ô’VþI³î¥¿M¤¥†©ì³Ï>qçw6R—õµ xàl•«*lš& Р5ÐKw'¤‡R9ù矿xñùæ›/Ò’’ jhë—»¦}èß¿ìºë®Ù°õëׯéËös8묳âøã/~¡^~ù峻ϚŽç_þò—f-hÍ´ÔçŸ~ö¾Å[,Ö[o½fu¤·ÜrKvýÕV[-[ƸpÒèÑ£ ›Åç´¤íÌÂyé¤ÒõÒþĉÓ“ò- øß¨o ÚeÚU ¥-Ûõ*#@€ @€ ³Àyç—Í27³Ë¤¿œvÚi3{¹ìø;ìÝŸréFú4]ZÙ&…ö>ÿüóøì³Ïbë­·Ž{î¹§ì}ßdgøðáeoO+ø´Tš†öZºÑÿ…^ÈÚÝôÜ–ê«·céïú~§¯·Q­½þ¤ÕÑT“@Môª L[¨?4-yšr÷Ýw´t­òí ¤\i8/]ýµ×^˾T<¸Ø˜4»Þ‹/¾XÜ/l¬ºêª…ÍŸÓ  i»T~ñ‹_d_à[:qäÈ‘Ùáô¥¿PÒx鎹Ғ—\rIé¡fÛ…ë^HŸ1… @€ @€Ô³@a•ˆI“&Å´iÓšuõì³ÏŽ=öØ#Òd ³+… \¾øâ‹H¦%Ýà¿Ì2Ë4=üö›®â“Vmj©Œ?¾ìð€Êö/¼ðÂl?­¶cš2; @ a:6lÏuœ†HwZ¥ VšbûÍ7ߌ3Ïúhénqû©§žŠí·ß>¦L™;vŒË.»,æž{æÇ2$®¿þúèÝ»w±Ž#F·ÓÆæ›on¸aÙ±¦;i9Û'žx¢xx¡…ŠÕW_½¸oƒ @€ @€Ô»À?þñ¬‹M—Máµ;ÕÝ_a…â;ßùNvþG‘­pó½ï}/û;B«+iʼnéo )@X(k¬±FtëÖ­°[ö¼ÖZk•í?óÌ3Ù~Zv÷§?ýi¶þž0³øÊÞl‡h½†f$@ -ƒzÝu×e³årÊ)±Øb‹A©µ×^»YKÒq‹,²H6³^¯C9$;çûßÿ~¤ìJKúBþïÿ»x(òN8á„Xo½õâwÞ‰ùæ›/n½õÖÙ는l4 ïç?ÿyÉ«-o¦Ò4þ…2hÐ !Іg @€ @€Ô°ÀŒ3j¸õßnÓ ËŦem·Ùf›²‹yä‘Ùìxeg²“Và9rdöêž{î9“³¾ùá4ó]iI¿íϬlºé¦‘nú/”“O>9[¡'ý½cÔ¨QÙÒ»7ß|sáeÏ @€˜ùTBp P)¤•ÂZi¦<¥:Ò]dë®»n<þøãÅî»ï¾Ñ¯_¿x饗b„ Ùñþýûg³¦éÒÝh¿ûÝï²ãéKsz-ÕñÁijÏ>Ó¦MË^K3&þå/‰uÖY§Xw[6Jzi¾ÖÜí–Â…¥å?øAé®m @€ @€¨?þ8î¿ÿþ¸ï¾û²¥OÇŸ-ß:Ï<óÄÒK/Ë-·\ì½÷Þ±ãŽ;ºI»Éx¦›Ø ¿¯§ï·Øb‹lÙ×´ôm*ÿú׿²›ñ?þø&ïl¾›þðÑGe³Ù¥Éò*¥+átïÞ=Òß)fVÒò·¿ÿýïã裎«®º*ÒÌy7ÞxcvzúÛFú;AªC!@€ô ž ¨+5×\3Î<óÌìK_]u¬;“f¸»÷Þ{ã ƒŠ[n¹%›B>…ò Á¼Ôåt§ZZ¢vá…ή½öÚHSÈŸqÆÙ,y“'OÎ~()ð¤e‹Óò¶i¶½e–Y¦p¸MÏ/¼ðB¤åj %}9O³úͪ¼ùæ›1|øðâ)iêþm·Ý¶¸Ÿ6¦Né… @€ @€ªOàÕW_Í~[N«ò¤[š–t,Ý\žwÝuW¶üjú{Ùe—mzjÃî3&&Mš={öÌn°O‡zhœ{î¹E“ÓO?=öÙgŸXb‰%ŠÇZÚ(ÌÄ—–µM7ÒçU6Ùd“,TøÚk¯ÅÀcÉ%—œå¥Òd—_~yœxâ‰1zôèèÔ©SöÛjgÚV @€¥z¥¶ ¨yôø˜cމ]vÙ%ÒLJm¤à]º»,…*Ó]uãÆ‹O>ù$úöíiJøÆkZÒ²·p@v×ÝË/¿œõºté’ý’¾HÏîËsÓúšî—íÒkiÊúÙ•4«_šn¿PÒìy)¤W(iùÛt7eº+ðÔSO-öL€ @€ @€øôÓOãØc¡C‡–ýÎÛµk×H³À¥ß©×Xcèܹs<òÈ#qÉ%—d«º¤@ßV[m=ö˜YÓþw ¡ºÒUiN:餸þúëãÝwßÍÎJ³Î¥¿ç¤c³*…ßêÓä]R¸.=ÚR–Zj©H… 0+½Yéxš¸í¶Ûj®Íü}úô‰=÷ÜóÿÌf+Ý…–f×Kö.ié‚Ò’B³+MHHËÊŒ3²P^ ð¥p @€ @€¨‘#Gf¿M¿þúëe Úÿý#ÍôÖ£G²ãiÉÖŸýìg±á†ÆØ±c#ͺö§?ý)ÒùJD! WªK7اUq~üã‰n¸á†lu 6Ø x¬t#ýž^ºTnék¶  @€µ$ß<Àµ¤ ­ @ D }é/ü€§ió7Úh£’3šo¾òÊ+‘…Ò½{÷Øl³Í »Ù ÿüç?³ºvÞyçâq @€ @€ P9x [Ò´4œ—~¾âŠ+²GÓp^¡¥½zõÊg…ýQ£F6úyúôéñðÃg¥½t -i»Î:ë”ù¤¥oKW¦)}ñé§ŸŽ?þ8ºu뫬²JéK¶  @€5%  WSÃ¥± ðm¤/ý“&M*^*}ñOËðΪ”†óÒyÌÂxi{êÔ©q '¤ÍØc=¢_¿~Ù¶ @€ @€ @ r>ø`l·Ýv1eÊ”²F\sÍ5­š /ù eòäɅ͆~=ztöûú¢‹.ýû÷/³èСC\tÑE‘ž å©§žŠßþö·…ݲçÂôi©ÜR벓ì @€j@àÿþ«±«‰ @àÛxä‘GÊ.Óô.¿²ÿwg¹å–+;\ØO?ìüèG?Š7Þx#Yd‘l™Û²í @€ @€ @€À·.nÒÞ{ï½ã‹/¾(»vZ‚5oM6lXñ´5×\³¸ÝÈ¥¡ºÒ ^ÁdÝu×mæ›np/½i¾pîðáóÍÖüF_xg @€@5 èUã¨hTTà™gž)»~éRµe/”쬸⊱þúë¤;þöÛo¿XuÕUãøC6ßý÷ßK/½tñ @€ @€ PcŽ9&Þy粋÷éÓ'.¼ð²c3ÛIKãÞ|óÍÅ—K.lÀB@oV¡º3Ï<3ºtéRÔ™8qbœrÊ)Åý´‘–½ÙR¹e'Ú!@€Ô€€€^ ’& @€À·+P:íþJ+­›o¾ùl¦×¿í¶Û²¥mÓÉãÇ«¯¾:^}õÕHu¤pž;(gËè @€ @€ä.ðì³ÏÆW\Ñì:G}t,´ÐBÍŽ—øê«¯"̶Þzë(,k»Ë.»Ä¬i¥ï¯çíéÓ§Cu›l²ÉL»Ú»wï8ñÄË^¿ä’K⥗^*3fL|üñÇÑ­[·Xe•UŠÇm @€jQ`îZl´6 @€<9äøÎw¾Ÿ|òIlµÕV1ÿüó·êr‹-¶XÜwß}‘fà{ýõ×#ýѳgÏlf½àS @€ @€ @ ò×]w]̘1£¬!;wŽÝwß½ìXÚ™2eJvCöC=÷Þ{o¤™óJ—cÝ`ƒ âšk®‰––smVY=ztZìÑ£G 0`–½=üðÃ㪫®ÊnrO'N›6-Ò±dœJa&¾7Þ˜m&â @ –ôjyô´rèÔ©S 7hÿ¢¶„êR ò‚ .(û-~ذaqçwÆvÛmÇÏ*53áÿ·õ/ PÛzµ=~ZO€ @€ @€ @€@+žxâ‰xçwZ<{äÈ‘-OÓÝk¬±F¤óÒ¬né&ï¹çö§ÖR°B¨nVËÛ–ž¿í¶ÛÆ Aƒâž{î)>âˆ#bóÍ7/.•+ W¤±A€Ô°€ÿj¬áÁÓt @€ @€ @€Z/0nܸO:thÌ3Ï<ÙkiF¼…Z(ºví /¼pö¼ÄKļóÎÛâ{ŒlæÁGy$£hK¨.Í¢—– NKܦòÚk¯Å>ûì“-#Ü­[7«Õd*þ!@€j]@@¯ÖGPû  @€ @€ @€h•ÀÛo¿Ýì¼%—\2<ðÀfÇh½À?ÿùϘ»:ÄE]T\wã7.nÏî½^'@€T»€€^µö @€ @€ @€ Ð.ýúõkVÏ›o¾ÙìXk¤™âvÝu×Hõî¼óέ}[MŸ7yòä?~|Ì;ï¼Ñ³gÏXj©¥¢o߾ѧOŸèÝ»w,°À‘BóÌ3O›ú™Â~§Ÿ~zVïN;íÔ¦÷:™ PÍsWsã´ @€ @€ @€í%°è¢‹ÆÊ+¯Ï?ÿ|±Ê§Ÿ~º¸Ý–4“ÜŽ;î÷ÜsOö¶.]º´åí5{n¯^½r›-ðØcôP @€õ$`½zM}!@€ @€ @€ @`–\pAÙë£FŠ1cÆ”›ÝNšuoРAÅpÞrË-çž{îìÞæu @€ÐkÀA×e @€ @€ @€*0pàÀ2dHY÷õ«_ÅW_}Uv¬¥©S§Æi§+­´RŒ1";eÀ€ñðÃgK½¶ôÇ @€[@@¯±Ç_ï  @€ @€ @€4œÀùçŸi¹ÛBùë_ÿƒŽI“&Ÿ§OŸûÛßâ°Ã‹4SÞ‰'žŸþyÌ5×\qôÑGÇO<‹-¶Xñ| @€Jæ.ݱM€ @€ @€ @€zHA»§Ÿ~:öÚk¯xðÁ³î6,ºwïžÍŽ7eÊ”èÛ·oŒ?>Þzë­˜ @€ @€ @€u/f½»ï¾û⬳Ί .¸ Þ{ï½H³å=÷ÜsYßÇŽ[f°ì²ËÆ.»ì»í¶[|÷»ß-{Í @€™ èÍLÆq @€ @€ @€êZ cÇŽqÜqÇewÞy'›U/ͬ÷á‡Æ’K.K/½t,³Ì2Ù£k×®um¡s @€ùèåãªV @€ @€ @€jH`ñÅô4hP µZS  @€ª] cµ7Pû @€ @€ @€ @€ @€@- èÕâ¨i3 @€ @€ @€ @€T½€€^Õ‘ @€ @€ @€ @€ @€@- èÕâ¨i3 @€ @€ @€ @€T½€€^Õ‘ @€ @€ @€ @€ @€@- èÕâ¨i3 @€ @€ @€ @€T½€€^Õ‘ @€ @€ @€ @€ @€@- èÕâ¨i3 @€ @€ @€ @€T½€€^Õ‘ @€ @€ @€ @€ @€@- èÕâ¨i3 @€ @€ @€ @€T½€€^Õ‘ @€ @€ @€ @€ @€@- èÕâ¨i3 @€ @€ @€ @€T½€€^Õ‘ @€ @€ @€ @€ @€@- èÕâ¨i3 @€ @€ @€ @€T½€€^Õ‘ @€ @€ @€ @€ @€@- èÕâ¨i3 @€ @€ @€ @€T½€€^Õ‘ @€ @€ @€ @€ @€@- èÕâ¨i3 @€ @€ @€ @€T½€€^Õ‘ @€ @€ @€ @€ @€@- èÕâ¨i3 @€ @€ @€ @€T½€€^Õ‘ @€ @€ @€ @€ @€@- èÕâ¨i3 @€ @€ @€ @€T½€€^Õ‘ @€ @€ @€ @€ @€@- èÕâ¨i3 @€ @€ @€ @€T½€€^Õ‘ @€ @€ @€ @€ @€@- èÕâ¨i3 @€ @€ @€ @€T½€€^Õ‘ @€ @€ @€ @€ @€@- èÕâ¨i3 @€ @€ @€ @€T½€€^Õ‘ @€ @€ @€ @€ @€@- èÕâ¨i3 @€ @€ @€ @€T½€€^Õ‘ @€ @€ @€ @€ @€@- èÕâ¨i3 @€ @€ @€ @€T½€€^Õ‘ @€ @€ @€ @€ @€@- èÕâ¨i3 @€ @€ @€ @€T½€€^Õ‘ @€ @€ @€ @€ @€@- èÕâ¨i3 @€ @€ @€ @€T½€€^Õ‘ @€ @€ @€ @€ @€@- èÕâ¨i3 @€ @€ @€ @€T½€€^Õ‘ @€ @€ @€ @€ @€@- èÕâ¨i3 @€ @€ @€ @€T½ÀÜUßB $@€@ ÜtÓMqÇwÔ@K5‘@} |òÉ'õÙ1½"@€ @€ Ð@ýû÷:4Pu•@}|ùå—õѽ @€9 èå«ZK`Ú´i‘  @€ @€ 0gnÄ37ï"@€¨n½ê­#@ Š:wî ¨âjÆèÑ£GãuZ  @€ @€Ô°@Ÿ>}üÖ^Ãã§éš ,²È"MÙ'@€ /  ×ðÌ©ÀRK-Ï>û윾Ýû @€ @€ @€@à œ}öÙ‘  @€zèX¯Ó/ @€ @€ @€ @€ PI½Jê»6 @€ @€ @€ @€Ô­€€^Ý­Ž @€ @€ @€ @€ @€@%ô*©ïÚ @€ @€ @€ @€ P·zu;´:F€ @€ @€ @€ @€•Ы¤¾k @€ @€ @€ @€ @€@Ý èÕíÐê @€ @€ @€ @€TR@@¯’ú®M€ @€ @€ @€ @€u+  W·C«c @€ @€ @€ @€ PI½Jê»6 @€ @€ @€ @€Ô­€€^Ý­Ž @€ @€ @€ @€ @€@%ô*©ïÚ @€ @€ @€ @€ P·zu;´:F€ @€ @€ @€ @€•Ы¤¾k @€ @€ @€ @€ @€@Ý èÕíÐê @€ @€ @€ @€TR@@¯’ú®M€ @€ @€ @€ @€u+  W·C«c @€ @€ @€ @€ PI½Jê»6 @€ @€ @€ @€Ô­€€^Ý­Ž @€ @€ @€ @€ @€@%ô*©ïÚ @€ @€ @€ @€ P·zu;´:F€ @€ @€ @€ @€•Ы¤¾k @€ @€ @€ @€ @€@Ý èÕíÐê @€ @€ @€ @€TR@@¯’ú®M€ @€ @€ @€ @€u+  W·C«c @€ @€ @€ @€ PI½Jê»6 @€ @€ @€ @€Ô­€€^Ý­Ž @€ @€ @€ @€ @€@%ô*©ïÚ @€ @€ @€ @€ P·zu;´:F€ @€ @€ @€ @€•Ы¤¾k @€ @€ @€ @€ @€@Ý èÕíÐê @€ @€ @€ @€TR@@¯’ú®M€ @€ @€ @€ @€u+  W·C«c @€ @€ @€ @€ PI½Jê»6 @€ @€ @€ @€Ô­€€^Ý­Ž @€ @€ @€ @€ @€@%ô*©ïÚ @€ @€ @€ @€ P·zu;´:F€ @€ @€ @€ @€•Ы¤¾k @€ @€ @€ @€ @€@Ý èÕíÐê @€ @€ @€ @€TR@@¯’ú®M€ @€ @€ @€ @€u+  W·C«c @€ @€ @€ @€ PI½Jê»6 @€ @€ @€ @€Ô­€€^Ý­Ž @€ @€ ðÿØ»ø+æýã_e¹¡ÜÒb¹²FÊ­ˆR¨P…«ì”5ke;†,éÚ"dÉÍV$×ve_ !ûRʲ»¶ßÞ_ÿ9Î2sΜßoΜ™9¯ïãq~¿sfÿ>ç,³|¾Ÿ/ € € € €TS€½jê³n@@@@@@@@@Ô  —Ú]KÅ@@@@@@@@@ª)@€^5õY7 € € € € € € € € €@jÐKí®¥b € € € € € € € € €Õ @¯šú¬@@@@@@@@@ µè¥v×R1@@@@@@@@@j  WM}Ö € € € € € € € € ZôR»k© € € € € € € € € €@5Ы¦>ëF@@@@@@@@H­z©ÝµT @@@@@@@@@ šèUSŸu#€ € € € € € € € €¤V€½ÔîZ*† € € € € € € € € PMôª©Ïº@@@@@@@@@R+@€^jw-C@@@@@@@@¨¦zÕÔgÝ € € € € € € € € €© @/µ»–Š!€ € € € € € € € €TS€½jê³n@@@@@@@@@Ô  —Ú]KÅ@@@@@@@@@ª)@€^5õY7 € € € € € € € € €@jÐKí®¥b € € € € € € € € €Õ @¯šú¬@@@@@@@@@ µè¥v×R1@@@@@@@@@j  WM}Ö € € € € € € € € ZôR»k© € € € € € € € € €@5Ы¦>ëF@@@@@@@@H­z©ÝµT @@@@@@@@@ šèUSŸu#€ € € € € € € € €¤V€½ÔîZ*† € € € € € € € € PMôª©Ïº@@@@@@@@@R+@€^jw-C@@@@@@@@¨¦zÕÔgÝ € € € € € € € € €© @/µ»–Š!€ € € € € € € € €TS€½jê³n@@@@@@@@@Ô  —Ú]KÅ@@@@@@@@@ª)@€^5õY7 € € € € € € € € €@jÐKí®¥b € € € € € € € € €Õ @¯šú¬@@@@@@@@@ µè¥v×R1@@@@@@@@@j  WM}Ö € € € € € € € € ZÅS[3*† € €@L†jæÌ™hk^|ñE³ÑFvÈ!æðÃ/: #@@@@@H›ÀW_}e¶ß~{óóÏ?ûVí›o¾ÉŒ»òÊ+Í]wÝ•yíõäòË//y]Þk>†!€AÐ *Åt € €ÔS`çw6'N 4÷·ß~kfΜé;í²Ë.kì;ž € € € € €¤U yóæ¦k×®fܸqªøá‡=üJ=ÎóÃa8„&@·¡Q² @@¼ `zõêå=²Ì¡ÇsŒiÓ¦M™s19 € € € € €é8õÔSM³fÍB©Ì\ÊrX PL€½b:ŒC@@ $1cÆ4xI­Zµ2G}tƒ—Ã@@@@@Hª@Ë–-³7´¨qý&›lÒÐÅ0? PR€½’DL€ € €@Ô&Ë-·lЂŽ<òH£.n) € € € € €Ô²ÀðáÜEï”SN©eBêŽ  !6«B@¨m“N:©ÞË-·œ9ì°Ãê=?3"€ € € € € ]3?üðÃë]~ýú™6Ú¨Þó3# PŽzåh1- € € Ø|óÍ2éÕ§èBC³fÍê3+ó € € € € € :‘#Gš¥—^º^õjHƒúz­™@ ¦ЫéÝOå@@¢8öØcË^åRK-e”®Ÿ‚ € € € € €´jÕÊ 2¤leÎëÝ»wÙó1 P_ôê+Ç| € €ÔC`àÀfÕUW-kÎ]wÝÕ´nݺ¬y˜@@@@@´ qÄeW‘ñe“14P€½2; € €å4nÜØvØaåÌBö¼²´˜@@@@@ VÖ]w]Ó¯_¿ÀÕmÓ¦Ùe—]OÏ„ €@è…¡È2@@(Cà€0Mš4 4G=L×®]MËD € € € € €Ôš@9Yô† f–\rÉZ#¢¾ Peôª¼X= € €@í 4oÞÜ 4(PÅ:è @Ó1 € € € € €µ(°í¶Ûš•W^¹dÕ5jdößÿ’Ó1 ¶za‹²<@@ì·ß~%§jÚ´©&îÛŸ¦í{ûí·ù¾NÓ¥.©èß¿*êA%@¨-ZÄñLTج–Yf™–Äbh¸À AƒÌGa~ùå—‚…)x‚TK€.n«%Ïz@@pü. ø  @@@H«?Òºg© ÀòË/oúõëW°²VXÁôéÓ§`8@¨È •4ëAÄ Ü~ûíf‡vHìö³á ð‡À¨Q£ÌE]±PZý#<2g»Úµkg:uê”3ŒÄE`Ú´if›m¶‰Ëæ° PEÍ6ÛÌ<óÌ3UÜV]L`æÌ™¦sçÎÅ&aTH@ÇKÿøÇ?*´t‹¤[ ®®.SA]Ï=ztæ5O@ ^]»v5ê–š‚@Ü”E/¿ÛeŸ7jDþª¸í+¶Z @¯–ö6uEz 4nÜØ,¾8_—õÂc&b$À‰WŒv›’#°Ê*«˜ 6ØÀ¼ð ™á†g(xCŽb¸SØ$ª$@v“*Á\­Îc9— ˆÅd„, ã%  €@ý²1u=ã™ú92Qd^£Xë@ ¨ÀöÛooƒñ~ÿý÷Ì,\sÏPðª$@ˆp•àY- € €®@þÅü×îtüG@@@@@V­Z™ž={f&hÖ¬ÝÛf4x‚Õ @¯Zò¬@@ÿ8p`Æ¢eË–92#x‚ € € € € €%²¯¹o³Í6fÉ%—,9 €• @¯’º,@@;w6­[·¶Sn¹å–6ý~€Ù˜@@@@@ò¶Új«Ììç™ûì3óôÓO›o¼Ñ\vÙefÚ´iæÕW_5ß}÷]êëNȘ4i’ Ì6l˜™7o^þèÈ_òÉ'æè£6«¯¾º¹âŠ+L]]]äÛÀ @@@Âàü3|S–ˆ €ñà˜'~û„-Bpg1,@Ò. @¼ÓN;ͼøâ‹¾UíÝ»·9þøãÍ6Ûlã; #Hƒ€á”w÷Ýw—]e—]ÖvK»ÒJ+™V­Zå<[l1³páBóùçŸgü±yæ™gÌ¢E‹¯K´‡z¨¹õÖ[Í„ lf½À33! € € €@l8ÿŒÍ®`C@@ ‚óT—E#€@,ЋÅn`#@ˆ¯€‚…h³æi+›4ib6ÜpCÓ¹sgóé§ŸÚìy¯¿þº­À£>jôèÒ¥‹¹ãŽ; ŠïneË pÓM7™#F˜/¿ü2ÐR_|qÓ­[7Ó·o_Ó¯_?Ó½{w³ÄKš×è·ß~3³fÍ2=ôyðÁÍSO=e”æ¿Tyä‘GL§N̘1cÌa‡fHA@@H†çŸÉØOl%„#ðÕW_5l-÷ºY8kg) PMŽyª©Ïº@ *º¸Jšõ € @¯¿þÚ©KÛ¦M›š)S¦ {ì±Ç̸qãlv®×^{ÍLŸ>ÝtèÐ!S×^zÉlµÕVFY¼(¤EàÿûŸÙsÏ=ÍÞ{ï(8oµÕV3—_~¹ùâ‹/Ì“O>iÎ8㠳馛Öë"cãÆm߉'žhf̘atÁrâĉ¦}ûö%y¿ÿþ{sÄG˜m·ÝÖ|ûí·%§g@@@ê pþY]ÖŽÑ Ì›7Ïì¶Ûn¦E‹ö´¾) PóÔÆ~¦– ð‡z¼@@ÀW@Ax³g϶ã•oÇwô .RàϳÏ>›¤÷î»ïší·ßÞwÙŒ@ I ˆSÐé¤I“Jnö:ë¬c®¿þzóöÛo›C9Ä4kÖ¬ä<åN°ôÒK›}öÙÇÌ™3ÇÜvÛm6ke©eÜwß}f³Í63}ôQ©I € € P%Î?«Ïj@ rõNqÊ)§Ø¨“'O¶ëÿå—_Ì¢E‹"ßVˆÑ p̽9kDê  W]ÖŽ €@¬®ºêªÌö)kد¿þšyÿDݨ[Ûì.žþyóþûïçOÊk%0wî\³É&›ØÌ‘Å6|õÕW7º˜¨¬’C† 1êÚ¶Ò¥Q£FfРAæÅ_4÷Üs騱cÑU*àV]ì¾òÊ+E§c$ € €@ú~ÿýwóé§Ÿš?þ8Påtƒ\;Èbˆ‹‰E€óÏPYÄPà“O>1êB™òºuëfZ¶li–_~y3zôhóÓO?e¶XÇ+ô‘áà ©à˜'µ»–Š!€@Êß5,²rF!€ ¦€º‘TðICNà[µjezöìYöf©¥Ï#Ú·~švíµ×6?üðƒYl±ÅLëÖ­Í*«¬bÚ¶mk3§xàö¹ïe pþY63 €@‚Ôí¹çž[°ÅºžüÆo g¤W€cžôî[j†ÅÐ+îÃX@Üÿýæ¨£Ž²Y«ÂØÜ›o¾Ùì±ÇuþùçÛÖº¢uuuvÞ1cÆØ\pAàeÅiÂ%—\Ò¬¸âŠ9Ù”­«TéÔ©SN€ž2{íµ×^¥fc<±øì³ÏLÿþý‹ç5iÒÄ\rÉ%æ ƒŠÅök{”ù²oß¾F7M¿þúkÏíúæ›ol°íÓO?mÖZk-Ïiˆ € <eÇ»òÊ+Í„ l@žW 7nl”½XÑùàÒK/môtŽ«Ì{zÌœ9ÓÜyçöû?þñÛ8¤>ÜŠ­›qԢ矵¸×©3µ%ТE {Í]™óÔH~…V0½zõ² ô\ÇI/Êþ÷ùçŸý×uõbE&4­².µÔRÅ&e©à˜'U»“Ê €@™è• Æä €ñPw«ûï¿¿¹á†Bݨ ”zÏ=÷´7'”IଳÎ2»ì²‹ Tš~ˆ++²o)³UÒŠ‚ O=õTsøá‡Û ÃÚ`¥RõÈÒ IP¦e‹,ÕEósÏ=g³FÆ­~Êè·á†šÎ;ûf]¸p¡ýL+HOG) € €ÉP¶<5ÒX´hQNEÔÐjÇw´ç¦]»v5]ºt±Áw9å½Ðsû½õÖ[FÙÔ_}õU{î­l|*j vÛm·ÙÇðáÃÍØ±cípþ €@ùœ–oÆ ûìcn¹å–L›6mjƒQ–YfóÔSOÙ ”•W^9'xF7t¬ 1®d—%–XÂèf…nh)Ç{¬ ÎS—®=ôiÓ¦MËPW@nj~u}›Ä=Uæàƒ6 ôQ0¤[¿b6º01kÖ¬œI”Q‚@’ô>Þ}÷ÝÍóÏ?_t³u£S"õÇòúë¯uU]¬¼óÎ;fÀ€¶{neߣ € €iPƒ¬÷Þ{Ï6ÄPšÎ-+U”©N™èì¶Î:ëØsP5„ªD¹õÖ[Ín»í–Éèî®cРAæŒ3ΰÝҺÂþ_|ñÅí|:t°ç…'Ÿ|² ÒÓ¹ðW_}•YÌ¥—^jt,©,ò(O€óÏò¼˜¨¶@ÙŠÉT\í½Èú«!À1O5ÔY'ÄM€½¸í¶@ ÀñÇŸ ÎSkýC=ÔŹ7;ÔÕΔ)Slw;ÇsLf™ºap '˜vØÁvû“Qæäýë_ÿ2Ë-·œyðÁs‚×Ôý2S¹E7e’\”f?h¹é¦›Ìüùó3“·mÛÖl¾ùæ™×Ê î½÷^£ïÁŸþ¹äjŸyæ3bÄ3~üø’Ó2 € Du!¦6ey+Õ£’õSã¥ûï¿ß6 s=_|ñ…Ùo¿ý ‚óFjМµ)‹½êÑ·o_óÍ7ßdªqÞyç™=zØsíÌ@ž €@IÎ?K1 °²“©86»” ‰P€cž±YÄV€½Øî6 @ÀOàË/¿4]t‘m¡õÕWÛnf³§U« NQÙrË-³G™gŸ}Ö¾^i¥•r†—ûââ‹/¶³¨[Ûü.\_{íµœ½üL}å®+)Ó+…2*dt©E ¤Ìž=ÛŒ=:ðæÆ1HÏ Î+•=/»’ú.Uv•­¶Ú*{0Ï@@Ä L:ÕvØaæ£>ªz]^~ùeóŸÿüÇì»ï¾¡nË 7Ü`¾ÿþûœeöéÓÇœsÎ99ÃÂz±ÑF™K.¹Äf/S™ôÔŽ‚Á8ÿäü3Ø;…©â,Ö̼q6¯Ö¶…­˜LÅÕÚ“¬·ópÌS÷ëD ŽèÅq¯°M €EZ´ha”ñIÝ×®¸âŠÓÞqǶ[GM—V€ÞÈ‘#ÍöÛoo† V°~ui›]tc$íE@ÊÖµ`Á‚LUO=õT³×^{e^ó¸ (³Š2âé9%NAzõ ÎsëzÀuÞ¬Y3wÿ@@D ({zܺ]u³¾‡ «À¿ü¢sÖFåíõ{ìaŽ;Æi3f̰]Wr½¡U€!PeÎ?9ÿ¬ò[Õ7@ í™y@“ÚY£ÈVL¦âÔ¾}j¾bópÌSóÈ @/ ƒ§ €ÉØpà }7vÒ¤IvÜ[l‘sCB™÷>þøc;®I“&¾óѯ_?£‡WÉÐ[gu<ƒ½æKê°~øÁì¼óÎæ©§ž²UÐ͘sÏ=×Þ¬IjØîÚP†‘—^zɳòʺ©®µ<òHóÛo¿L‡ ½RÁyúl*û§sçÎ-¨ƒº§>ꨣÌ5×\S0Ž €ñP+ôÇ{Ìì¸ãŽf•UV‰çF²UTIà®»îÊ ÎSWb ZÓÿ¥–ZÊ4nÜØäË©±×wÞ™³Å:¼à‚ r†ù½Ðq¢2ÙéXK™ûÞ|óÍœI×Xc ÛÐ+g`/”E>¿l°ÁùƒB}-ÃõÖ[ÏdŸÿÖÕÕ.»ì²¡®‹…!FÎ?9ÿLãûºêT ™yka?–[Ç(³“©¸Ü½Ãôqà˜‡cž¸¿GÙ>¢ @/JmÖ… Pq>øÀ¨Õ¾Jß¾}sÖ§.Ü’éÍÆÝxôÑG3‹J{ö¼E‹™˜'žxÂÖ¹uëÖæúë¯7ýû÷Ïð$¼÷Þ{F]2{ݸ½îºël÷¯ÊÌ9dÈØé λöÚkí¶+À¹W¯^žu˜0a‚ÙÿýM=¼(† Peþ¨QÄ´iÓÌÝwßmæÍ›g·HF‡zh•·ŽÕ#/©üå/± ŠÔ%kË–-‹n¤W&ºÞ½{e‹+·Œ5ÊèØÑ-Ûn»­Ñ±V©mp§oè}/Tºxe^þùçŸ+½Z–@â8ÿücrþ™ø·rÍU V2óÖÜŽ Pa¯cÄJf+&Sq€Â$G.g¡@IDAT‰à˜çÝÄ1O"Þ®l$‘  3+AˆJàüóÏ·]êh}ùî>ûì³Ìf(ST%Êœ9sÌ矞YtšôÞÿ}›ýáµ×^³õU—¿:ÑP¤ (8ϯkÛÃ?Üç©N{î¹§­Zœ‚ô‚ç鳩mVéÙ³§9í´ÓŒº¡ö*gœq†¹ï¾û¼F1 ª(ð^]©+Ð@™ õ?ÿá5ܦàye4rÊxé>/ö¿K—.f…V¨JÓºÒ^xÁ¨k uÝ£Çâ‹/^ð\ûMûì×_Íyè;Z wÿ+€ºyóæ¦M›6v_54CrœÜõžë­·Œ.fë˜ëwÞ1Ï>û¬Íô* ”8ýôÓÍ`:uêd3敞ØÇ¼`²M6Ù¤`Xú~Rfb5jRãeC‰²(0±Ò%Š ÀJ×å#P Î?ÿTçüóO žÅ[ –2óÆ{OTgë¢ÎVL¦âêìgÖ¾Ç<šrÌó§Ï¨eôjyïSw@ eê¾VA(*ê:Hì¢#nqƒÊÜ×aýÏîÞGËLk€ž2·¨+5#.½ôÒ¶Ë̃:(‡Q7Ï¿ùæÛ½‘.*Pˆ«€ÔU…WYwÝuÍyç—3*NAz¥‚ó¼¢ÃC‡ͩÉ'žh|ðAÏ›Ð÷ß¿ éÞ½{Î<¼@ Zûí·Ÿí*0êõo¹å–桇Šzµ©]߇~hºuëæ™½3¬J/¿üòfàÀf÷Ýw7[l±… ä kÙQ/GßÅ;ì°CÔ«e}¤J`ÅW4z-:_T l~Ùl³Íò~­ÌÄ@ [€óÏl c8ÿÌõàU|j=3o|÷Lõ¶¬Ò ÈT\½}ËšÃà˜'בcž\^!P«jµâÔ@ }^x¡ùé§ŸlżºX]uÕU3•V ݻロyÖ“ì½öíÛ§2óΤI“ìMoç)»Ð¬Y³L~pž<Õ¦ºoòë64,s–ƒ@CÎ9监)9 n»ñÆWF&éMœ8Ñ7øCº§ù+U‚çí»ï¾› ‹ˆ7Ýt“gÝ4±ZôQ¨uï¾û®Ö B­ÿ?þXÑà÷Üs63®®§é²¾™yuý[Û1}úôT^wŽßž÷ߢJg+®t Íƒ@8ó:rÌShÂjM€½ZÛãÔH©€‚Å®¼òÊLíÔÝj~QÀœ{b«®Ó®¸âŠüIôZË|ôÑG3ËHcö<]ŒR`’º];âˆ#Ì3Ïxð`ß›õjéèèéÆðÌ™3M¯^½2ó6äIv÷¶ZNZô>ûì3{Cøé§Ÿ¶<Ê€£›âzdu¥@'e×{ï½÷Œ²*ˆ¢C‡Ù“ñX Lž<Ùs{ôù š=+Ê ½ Áyp€g½ò*‹Þ^{íeƒnóÇÝ~ûíæ’K.±Ÿãüq¼F ZÊÚªlµ­[·.{~øa£ 'y+Íš53·ÞzkàÌÅ–Å8æÍ›Ûc°bzK,±„ ÐË^ʆnh_êÆÓ¡‡j÷iöøüçü± tKú £þýûçW× ¢€[-X° g‰ æ³—C h çŸœ6ð-Äì ¨Ff^]“Õ5r5´Žª¨ÑÔyçG€^T଄pÌÃ1OBÞªl&‘  99+D¨„À²Ë.k«L:ÇsŒï*N:é$£“’)K=|§-wDv€ž²ž(-éE7ªÔ]pöÍó×_Ý褬¾úê‰ËX¤^L“ëΞ=Û³2årD¤$8OÙð‚ç¹5j”íæ÷÷ßwÙÿ jyì±ÇŒ‚)ÄE@Ý«×'8O™ÔöÝwß’Áyª§2ìÒ={4{|•UV©÷ŠVZi%3eÊÛ½Ó¬Y³Š.G-·Ëý^/ºÀ*ŒT¦Hu§¥›n_Àë&úf›mf³‡‡¿6–ˆµ(Àùç{óÏZ|÷×f«‘™Wç 7Þx£½öUž¸í¼óε¹“©5x pÌó Ç<žo"PóèÕü[@ 3fÌ0 SÆ«b7Õ•yG'/½ô’ÙvÛm2³„Q”G}Ü’†€–ŸþÙl¹å–æÓO?u«UöÿõÖ[¯ìy˜¨üZòuîÜÙÔ§;³Jé λòÊ+ÍX6ß:ë¬cÔeµWןÊ"–†ï³²Q˜!¶›nºi½¶mĈæƒ>(9¯¹¹P’)´ þò—¿4hY‹-¶˜¹è¢‹LïÞ½‹.G™}u¬¦é“\äE€^’÷ Ûg¯›èt+ç=ƶ!<Î?ÿØgœ&ï½Ë—/PÍ̼ƒ 2zP@j pÌó‡<Ç<Õz²^â-Ð(Þ›ÇÖ!€Ì›7ÏvÁ—Å ²”Ee=ö(œçN¯¬n;í´S¨]×=ûì³fáÂ…î*L¿~ý2ÏÃxòÄOôìÙÓì²Ë.f̘1æÇô\ôã?nŽ:ê(Û-­2*ðfäÈ‘Æë¦“çþ ²i}ùå—Å&)9®cÇŽ%§a‚ø((Sï—×^{->UÁ-ñ HÓêþñÔ{­ Ò›8q¢Q—h^EŸ«¡C‡ÚÍ^㽆 λâŠ+ÌAä5{ aÚï^ÅÏÈkZ†E+ðöÛoÛ÷Òüùó£]qצàªvíÚ•½êÖöúë¯/9ßFmd.¼ðÂ’Ó1AxMš4iðÂzõêeÖ_ý¢ËÑ1ÓG}Ttš$ŒLz€aŒ+±O>ù¤>|¸ùüóÏ+±x–‚À?ü`t•_vØa‡üA¼FÔÀ¨Ük!oBd‹ó;·âüóÏ]àgôç<«¦€‚¾tý“RZ€Ì¼¥˜ZøïÿkŽ>úèßkIŠ™ßï9Ç<îA?£?§à(ÉÎ+¯¼’:ôR·K©éPÆ uM¥ìg:Í„Jg©UÜ>ùäs衇÷x≛wÍ5×d†µmÛÖ 0 óº¡Ot°®Ì0—^z©yúé§Ím·Ýf»iÓÍhet‹.’)cnT_|ñÅF Ï<óŒ™6mš;v¬éß¿¿Í0¨º)ÊÒ¢€-}þêû8û쳃¬Šib$ ÷K§Nl7© ,ˆÑ–…»)Ê@äwpß·oß­,Ì ½ Áy—_~¹6lXƒ¶Ù¯Îêæ6 A- ‰éÌ öT0¨ºk?î¸ãjâ"ßÊ+¯lÊ èR@LàÕ¦M›Ún€Ô(%:Å'©¿>¥JC”Z>ãðÐñô¸qãlC¢Ñ£G›ï¿ÿÞoR†WI@™Ðó»SÐöj«­V¥-bµÔŽÀË/¿l¯U({ÿÌ™3S[qÎ?sw-矹IyõØc™7ÞØ <ؼõÖ[IÙìªl§Wà1™y«²+X)±й†²ÿ«ç§óÏ?ß7ñB,6¶Á1O. Ç<¹¼B ÿüç?¦K—.6IAžqÊYv5§%@¯šú¬ÊÐÁdu¡\z   …€n¦)C•²8ž{î¹æý÷ßϬV]ë^wÝu™×ÊVÖ ge2Sf@bè=¯ »UW]Õ®K]õªÕ‘Nð”ñg“M61êBAŸÆébn6©»¾¥–ZÊÎóÜsÏÙ i¿þúkf{y‚@¾Ào¿ýf&L˜`/(°E]¦­èF×ç Y³f¦[·n ®nAzA‚ó.»ì2sðÁ7x{WYe£´û^å…^ð̰˜è7à‚ .°¿ ÔkH·ä1©RÁfÜpà Fn¿ýö‚q¥(xõ³Ï>+5™ùç?ÿiƒðKNȱXc5JnW›6mJNÃTRàÛo¿5§œrŠý¾>묳̢E‹*¹:–]†€W–›]wݵŒ%0)4Tàá‡6 ŒUÃBõ ¶Âùgîåü3×#i¯t^¶îºëšÝwßÝ·ácÒêæö’™7LM–…@ºt8jÔ({N¨{Lß|óMº*èÔ†cžÜ]Ê1O®¯(WÀMR°ÖZk™ý÷ßߨW¡¤ô’¾Ù~jT@Y¨§lf Lxã7jT‚jG% À8·(³ÜrË-g_>øàƒ¶ËY$¨ìµ×^6 ξáÏgœa[T)OAxê†2»Å¥wÞygsì±ÇÚ >eùSV0eùÛzë­m6=eÞÛf›m2[£‹ÝtI‘áàIe|¹úê«m†.½·Ó¨åW—>}ú„`Û ½RÁyÚm Î;äCŠìÁòFùµèó³*oéL]iï¾ûÎê)Hû°ÃKÅɪk¶Ùf›™­¶ÚÊfu‡ù¯À¾)S¦”œT¿‘ê^’\R™ÕJ½uëÖÉ­ [ž*/¾øÂœzê©ö¦ŒnÎÔRWåqÝ‘:îÊ.êNZÙ’^7n\ñ*D±ŽŠW‚ÄJ@ uì§^î¹ç{#VXÏñ;§âü³ÔϪpJ†TS@×Aÿýï›Î;u ¯ìz”?ÈÌË;J ¨·Ý¿Q"†“O>9U½—øýŽsÌSø®ð³*œ’! ðË/¿˜k¯½Öö´¨F"Ï?ÿ|bQÂéO&±ÕgÃ@ éʨwÕUW™ñãÇ›~ýú™áÇõI® êÂèÞ½»q»ûTpžç^}õU3cÆ ÛýëK,aŽ:ê(›]/¬÷ŸnÖ¹Ù‚Î<óÌLu–Yf™Ìs=qo(©›K¿LZÛm·Í¾çΨ®q•q‚@eÔS7ãzè}£ïZ†•)2È6„=ß °_Z}ׯ =•!C†9æ]Ô:t¨¼÷Þ{ÛÿÕÎÓŠõ;ª ¿üâg•?¯ã! Œzú=PÖWžéóª€í°~›âQËÒ[¡ßPÕ½Tùë_ÿj3†–š®œñÊb8}út›mwÞ¼yö8A]°*KeûöíM»víÊîª7ýju­u<ðÀF¿ñ»ì²KÎ$úW7ú¿üòËÛ€ýÝvÛ-g¿ú®Òçþ™gž1Ÿ|ò‰í>ù«¯¾2 ÂвZ´ha·uã\ÝÇ¡” pêÙ³gàÍT¶ä_|Ñf}÷Ýw‚©TOù©µ¦º_qÅ//è„Ê ® „wÞyǾwÔÝòßþö7Ó¡C›M¨yóæAÅt P¶uo¤ ž;í´“ýÎRP %Z§Ÿ~º ¨½®®Îî›r¶D¿³5²ßóa6 º ^­È£è¶}饗.ØDýöé·‚‚@C죇~?üp³ï¾ûfJ6d¹Õš×óOÎ?«õž k½úͼûî»íCÁz:SO jÜ\«…̼åïy]ËÏ.­ó0QH³€Þ÷ê Iç…J ïÐ=z$ºÊóî>®¹š0ú ¸DÔPdã7¶ß›ƒ 2ºGŸ”B€^RöÛ‰Et1@7(õPWºp§€ÝÔ¢ †Àõ×_o36*“žn~(+.6uìØÑ…êä)ì›Ô·Ýv›m-Þ¥K»·ºiœ_”ùÇ/8OÓ.»ì²9³éî/g^ ðÿO>ù¤Ñc¥•V²Agûí·_"/˜ùe^Ýpà Cß×åé)øEÁ @÷+ÿú׿Œ²e†]t1Ý«øYyM˰øèØHZz袶>« Õg7íEu×±à×_]²ªãÆ ÍDÝxŒ3ÆÜqÇFHýв½©¥´²ßº]ÐûMëWМ–ÿøãÛl2<òˆQËAsô ©ã‰'º³ÚÿúÞ. §`¾óÎ;Ïþ©+Î E‡#FŒ°ÞAëd¹åN£ŒÂÅJ=™*C·²õè=äWð¢ï`uUFŠŽ+õ¾¹é¦›|ß7 ˆW@dþM+¿mdx²ôV£=þþ÷¿Û.;”¹XÇ”Ê ([´Wñj´à5]þ05ìŠ:@ïÃ?´Ç²·¥Y³f Ï^žßsç}Ÿ®¿þúùƒy@½,ä‘GÚì2:ÞQ·FIllèwNÅùgáÛÂϪpJ†ÄM`öìÙö3ªóËèóªÆ-µV܆Ôn½ÄŸ†Ì¼n}ú_Àt¾¥sfu¬R×]w]Áu°#Ž8Â6†Wƒ¥¦M›ÚsgsÆé¹Ò™„+½ü†îKæO@×wl¢‡Ž£õý©`ç$6Ôóû瘧ðýâgU8%C@ÀK@ËõPòœ}öÙÇ~wª‘|Ü zqßCl”-ðÞ{ïÙf§všÍ£/eu êÕ²»ì…3CUtòîÞŒŽzCt3V­™ôÐÅ=Z¶l™³ÅnÆ»ê¢LÐH~ÝLWÉîÒV­žzê)wqö¿n)`§XÑM÷ìR Ùõåyø}ô‘Íyî¹çÚ þ(°Ìíþ9ü5†»Deƒò*•ê1Hž~¯J©ûÒJ”V­Zy.ÖÏÊsbÆR@¹¦€¢þýûÛÖí·ß>’÷ÕQPÅÿûß’«VÆ*Ý8jhQДº«TæBÚ”*?þø£Ý þWÀ¿²?çuE¯®‘t±îÍ7ß´Ý×— šSpžŽ¼26ëV™ tñWÊ-Ú>¢Œ=ÚÜrË-¶Kºr—ÑÐé•=¯X—Zº‰£.’ýŠ.䨡EЮt¼wÉ%—í?e–õÚ~ëÊþ믿Ún©••¹XP¶æÑ´AÞÓÙËçy2^yå3räHsÜqÇÙϳ²ë* jÐó‡dÖºz[­,†·Þzkh  zê¶8ê¢ß üÆï[þ2½^ëý™_g{ký~Ç–^Ëa¥¾ÿþ{À¡ 5P£\umÔ¶mÛR³Æb¼ß9矅»ÇϪpJ†ÄUàË/¿´ç9:×Q@†>¯ °­…ß…´dæ­ä{kµÕV+yþ£õëüLßóÙE —^{í5›•>{xÏ«‘­Øë~™Š£ØÛÕ]‡’3({ð1Çc3­ëœPØ’Ò“ßï8Ç<…ï+?«Â)‚Åü¯L¤zlºé¦öØS÷*ãäL€^±½É8H´€™ÜÌ1êTAz:©ÓÁl-§ÙÏß©:ØŸ:uªùî»ïòGUäµnë&è 'œPöò~øav÷eoDgÐEˆRY^ÜU(àI^Æ sÙÌùÔ-V©î‹òo\wíÚ5³Lž ÐP‘è¡ ÿè»V ù™ºž0ç÷ËBTÉ Æ¥‚ôJÕO·õ]©¢ßJ}ç(x(»øYeOÃód(xLÙÁôÐçSÁ\ʪ¦ßÖjf? Sï­·Þ²-¥–©Ïú•W^Yj²’ãÜ¥@?](·(#Œº§½óÎ;í2²çWà½2ª-jÀ  ^ÁyZ†×…} ×…_m¿.ò{5,PÆͯnJü•½iË-·´AŠp€×¢*6ìœsÎñÍ<§•*PD¶óËÂ… ÍñÇo®½öÚ¢óòçs_ë»QÕÊlXnÃeQVK¿®_Üuð¿vªÀ1=tAQÝéûZ™“rc& {OÅ ú £èƒ¾»õeQN}eæÍeó¹20ëxB×Ü¢®Þu.«¬~¿?î´üG >j  ßp]WR–\ê8¨]Ð×gû¼æñ;§âü“óO¯÷Kš†Íœ9Ó衬Û:_Ðçu‡vˆí Ó†Ú§!3oC Jͯ ʈ§c]‡PCeŠÓpª¨‘¾j¨¤sP5hÒµ*5ZË¿.^j}aŒ¯V¶b2‡±÷’» 5ÂÔùŠJ¡.uN¨à“8gW䘧ð=Ç5÷B† P)'žxÂè¡ÞOÔ PÇžJT c¸ôâ²'ب¨€.º»³:Ò—².(€$? ZE7$f ×Éí„ Œö£*:‰ÖÒúèé$]'ëI.ål¿‚JõÈ. ‚Ê.º¸Uª{ug›FÝÑåP¶"ÏÃÐwŠnÎé¡‹lzê»VÀå/„µM~ËñºX mÖ磒¥¾AzcÇŽ5êÞ£ÒE­óu$  ¡Å2pUz»X~ø ÌW˜ ÖÛf›mìçUA¶qê2¦œšë½¦òƒL½–qÅW˜†¶ÞÕ±¥‚¬ò£t£õ¬³Î²Ù"4N™ÏôZ7¼Šñus¹M›6^£KS£e¥Pÿâ ñÁØÌr Tó*:Q–8·µ¡Zö^uÕU¾Çp T]Vs#DÕ BÁKù)Ùuѱ¾nzhê8¸!EÙeÿÀ^ÌÅ_lƒH½Þ«®ºª}ß(À@ :´Üje\!&¬¸€>{×\s}èó¨À^_)3d¥[*^¹*¯Àë&º†)09HÑw¢Ž›”HÇiú GU”=F]æw£§@ÁÉ“'GšULÇ©ÊÖûꫯfª¯ß¤õÖ[Ïn냂@%¼¡žôÐg°[·nöûQÇ0^A •؆ ËäüÓ[ŠóOo—4Õ¹šŽmõPcƒÍ6ÛÌ~^ àÙ˜&‰iÉÌ[i{õJ“´R­lÅd*NÚ;¥rÛ«ë6º†¡‡®ÙéZ»Î •Œ$n ä9æñ~pÌãíÂP*% {ZÓ¦M³5bÜb‹-ìýv{þío«Ôj-—½@LL”-pÐAådsÊÇs’  `=e*ÑC_ TÒMD’ôèÑ#VQÔ•öT0Ê7Þh/àçß\®ÔºuI êSôê—¹¥>ËKâ<ù™ð‚ì(X*ÛMÁqneÅ~9øàƒsn E±ÎZ[‡‚õtÃнi¨$îw­Zú¹ÕpÑçÁ+khCƒu‚Ö¥Ü =u¥¨Ì£Qåèi½º¸R­=w* ¥rú<(Ë $×±‘.òéØhã7ŽÝÅ>? eR×°¥Ê{ìQïcwÙ –9ðÀÝ—™ÿ+¬°‚™1c†ízͨ@ú¿ÿýïfðàÁ9¿ÇîøÏ?ÿÜ.ëî»ïvÙϼŽ{Ô=Ôøñã2ú –*ùzúŽV¦¿à<ݸí¶Ûr2ôê{[Ùj”=@Y¢¼Š‚Õ2ñõ×_µŒôÝíf™Q ˆ‚ë´ÏýŠö…ºÁ ZTg½ïW^ye›™AuP‹ºt/V|ðAû½$0GÝÙž~úé‹Ó1¹‚!Õ>»¨‘Å™gžiÔmsJ­_F±¬çWëæ¶‚QÜã+='S|ð½  ¢³fÍÊ™AÇ«ådUpuTEYWµÍʼ©íVðwö9ž¶cõÕW7'NŒ¼»qu1ª.ýÔ]zv—Á 4Ö¹§²ìm°Á™‡Ž-Êi¸•q%×sß}÷ù¸Wr½IX¶‚ìÂ(ZŽŽõ8ñÄí﹎eõÛÞ«W¯HƒVóëÃùg¾ÈŸ¯ãxþ©ã1S Âú¼ª¡ŠÎ™ô9r¤YsÍ5íñŒŽiôyêÚLa 6DçeIÏÌÛ0tÎ]ÍlÅIÌTÌ9aå?j(¨c~=”RÇÖî9¡éúBµ Ç<þòq;æ™3gŽéÔ©“ÿ3 (XOçäz(³^ûöí3÷>Ôh¤E‹ÑÖÖ9¨¦ PRÀ¹Y «%<0Hõ{À Ö«ëÒ¥Kݸqãêœ,'™ºN™2¥äg„ ˆJÀ¹H•yo:75êœÖ%Wí\”ÎÌ£ïrçfsÉyÒ8Á1Ç“qp%2Ïù}‹þ÷Ý9®s.pÕ9Uy«9™<=÷¿“å#Òíq2MynGö{ÒÉ’é69t<·É Št;²Wædóܦl'žWæs¬ßç†zÝå—_^ç´Ïì'ð6{Åâ¹°Pç\˜Ìl£ß{ÂÉLè·³X¥œ ¶:'XÍs]N–`ßY Bžó¸ÛúÊ+¯xÎë4¢¨s.ä×]†ß§«Áœe;‹.ïÍ7ßÌ™>û…“Ñ­Î *:¿Þ/AŠ€\t9N°QÖç´·Ó9™¯ë–[n¹¢óÈÀ ²©sº¿+º ÙÇûÝ»w÷œVÇYN`eÉõ9-2=çÏè7z.ǹQ÷øãgOZðÜ ž-ùþv‚ø æ‹b€ÓÀÉÖKçR~ï?†Wæ;:ÛÕ Ö«s‚ÅêtíÆiˆ’Ù/½ôRoƒD®ãCÉ8¹–NFŠXÖÅÉLW°­î6ë¿\çÆÕ9Uß~ý–9QŠn綾^ZõíŒbîºë®ŒCŸ>}2ϳ÷Ï+ÿýè~FœOuNð}»¾`œdÄñü3ȱŸÝÊ}vlÜu'Ÿ|rŽ]g§–Ì{&ÎOºvíšÙfwÛ̼uN–Ï@'|]ïÞ½ëŽ>ú躹sçÆ¹ª5±mNðJ“‘½`ŸêU¿ñQ§m®Wºï)÷¿Ó0£ÎÉXÕf]Hh·Ï鹪`;Ýíåå¾7][]sMêœ]uºŽTÂ1¿zÜŽytÞæ¾wø_ùÏ'Æñ5^{íµëÎ>ûìºO>ùÄÿò2è9ŸJiEv+³j (ˇZЇ]ÖZk­L =e)q3:]xá…a¯Šå!Ð`uc¤L*nQ†#÷=ëËÿ¯îëÔ"Õ-êâYÝbe}¾ªÙ²*{[¢z®Ö¹qKÿUÝ‹­Ç9Î4Ï<óL±Iê5n•UVÉ|×*‹A}»q¬×Ê=fòËç•UÏcöP=ñÄææ›o.¹,efR*n¥ÞŽ¢|ûí·ž«ñ3óœ8äjý©ìY”\e¾U&°K»víl 2µ¾Ý|óÍÍ_ÿúW» çD5ìU…¶<ýŽ©kÛ ]*ó]©ßÎRvÚi§e±Ê/ʤì|~E™åÔRϯ̞=Ûv˜?^¿Ñ:fU¦½bE] žsÎ96£–²¿©¥ÿý÷ßogÉΠ§Í\pçâ„ïxeí’÷sÏ=ç;2¾)Û^ßZßk¬aÔJÜ/㟻!²RfCÕ]Çcq,Êì¹îºëÚì¤^Û§LÃÊ*ªVàÊØZ­¢ciext{¼¶C߫ٿI^Ó¤q˜êÍw¤÷žU×È~ç Þs”êóÛ.Œôý¨G±ãšÒKkø~ÇCœß}ïgÖð½Qz z¿ðyõvÒ1ms.ï¹½‡*[žûYÕ]?Rq°xÏÓ¡IËÌSƪnV\³'-S1ߟÞocõL¢ë4aeYÔw§Î •ªÚÇÙ~¿ßóÄï˜Gï>¯a"Y^ØÊÔvÑõ•ìcOõÀy 9àÅ!€pºF %š_™`”Y@ÙMеœunZfÖG½ŠìÒHªÌ+j5”ÿ¨O†-˯87¼ëô¨Tqº“˼/†:e„+UœnÑræqn:åÌ¢÷·>jšö’Aï©§žJ{uëU?'È%çý¢÷Y}ʺåtãWç\¤¬×¶Tz&¯VΉi¥Wk—¯LIÎMôÀ¶K.¹dÓõe$ÛæÁxn—s!%’õ³’àÊVßÏgö|ú p‚ƒêtìS,SâèÑ£3ë‹[½c=6³mÙuËît½ØgÊùóç×)ãYþ²õZÙY‹§kXÏùÜe5Êwöül¸î<î§»Ú‚ÌI?þøcdi³ÉMŸ>=³l'p¢èv8ˆ™iýž96衇üfÏ ’AOgg!rëìþw³`+CÝ‚ 2Ë.õDÇÊRá\ȶYúŠMïtSÔLQ‹•AƒùÎïtSYlÖœqN‘ïrâA/gcy‘xøá‡}÷›û>ò_œ‹ˆ6ã»2iø•Ã;,³>2èy+9¯#×^Ùjâ^œ@åºÇ{¬Nç…^Á•I1hÓ°ëúÈ#Ô97 \•eÆé6Øfw,vœööÄayÙ¿]Å2ìÆa[«¹ ¥²òºŸÑRÿ®æë”%OÇÅ®ÛT«®œzËsþéíס Íêí~Ž ó:»;7]ë~ÿýwÏêê÷Î> ô’”™×¼Æ&%[q\3ggЫñ·’oõï¹çžÌwšûÝVŸÿºF¼õÖ[Ûž.t}*Ž…cï½Â1· C(&PŸïI¯yô;¥k%³fÍ*¶ºÈÆ‘AÏÙKH¿€Óm•QÖ’]vÙÅ8]›¥¿ÂÔ0#ðÏþÓ8Y™×ùON<ñD4+ÐŽ;îhœ‹ìƹÉ`³²87ÉŒsSØ8~FY¾ÿþ{ãt_gœôòù« åµ2ie§Ûƒì—žÏ•Á »(ã[œ£ ãtïfœ‹aÆ9¹sóz têÔÉ~×î¶ÛnÆé–¤ÞˉbFeIÑg6»ès¬G%[*s^ÿþýM9-þùgãw§‹—ŠgÒËÎÒéÚ({U9™¨Üùø_ývm¼ñÆ™c£jgµlˆ”2+è·¾TqnÚš‹/¾¸Ôd%Çë·X輊Zà+Ê!{ýþzeaó+Å~§n_m&('p0gv¯8A9Ãô ¢(–=@ß奊¾ã;tè`”Ýׯhß(kjeàÀ6ƒž²8]Øïj}«îÚ§ ܲW£–ÂA[ ë{»XÑ1¡_yçwÌwÞé9ÚÍðã9’8ÊÌÙ§Oû}­LŠ Í êNws9úþ<üðÃs†ÅñE‹-lf eÇ8ôÐCÓ­­q2í÷£¶Wç¥N-û;£ÿQeTuº€³ßÍÙëÔw·ˆmœ.زóÐtì¥ë|ºÞ·ÑF…¶ÜJ,ˆóOoUÎ?½]Ò8TÁõYÕCYaÓT’–™7MöaÕ% ÙŠu‘LÅaíñd-Gç*º¶¢ëíNãL£ëq.óxïŽy¼]Š@¥:vìh;õÝ©ÕâTЋÓÞ`[@ T݈Ýwß}m75ê‹R›º‰®›·_ýµg7 矾ÙsÏ=íMæRBº9®‡ºÔ#¿èdI7‰+QD—}C]7ëJu‡æd(0z¸E7¯Õ•³[&Mšd»ŽÖ²ìæ?e (°GŸC‡ ô9*káœX œlVkÐÉr¥>Çõ Îs70Š ='Û–gà ¬(éÐ r÷بRïó(¥tZß=ú,Vwíµ×š¦M››,Ð8¯€7wÆRz vUp…ºjõ*}ô‘×`;Luð+êÚRÁxA‹“¢è¤AƒÝÔP X€^ØÝlÊNj'[ˆ½®ãI¯RÌÿ–[nñ}®´ÒJ^‹cö˜j¿ýö³ç)Uén#Åû@ß[NÍœ*,i¿‹ú]Øu×]º¦×9^ö÷“a 0 ’€%ê\RÁ Ùå /4NVÂìAÿüóŠÜ  œçt5mðxÄGxrV:HOu÷*~V^Ó2,~ ,WÖ%Ý Ð üb^ñÛúâ[¤ÿï½÷^ñ‰œ±úÎP/9C‘ œ®…}Ç*KŸõ&MšØãýWC]ˆs~Áy¾ 0Bë)§(£[±4@¯Ôâ… [MìÇ)ØÅéúÀÌœ9Ó>òoºeW`©¥–Ê~™óÜé8çuö ô²5x® Ý|Ñ÷uÐ쎨•/0nܸ‚™’H¦sЋ.ºÈ¾o²+æt]o3ìe«ÄsexοY¬¬òI6­„Ël¸@×®]Íh¿'›5kÖðF¼¿s*Î? w„ŸUá” ‰«@Ÿ>}ìçU™žŠ5d‰ëö—»]IÍÌ[n=kaú8f+&Sq-¼óþ¬ã²Ë.k›uN÷ìÀnuî3¿ßqŽyrôÊϪpJ† €€Ÿ€îuè„îUª¡b±^Nü–õðäÝMZˆõ!€@"tƒpذaæàƒ6ÜèJÄ.«ÊFê„VE]5fwmùÐC™©S§ua¤¬½öÚ¶EšôŽ:ê¨@]ëYn±itÃ?»èbW©’Ÿ¶×}­ eä˜7oží&K(P }½wÔXÒ3“êóðä“OT[­cþ48/û³X =·ep>ŠûÝ‘?œ×ñPÆ%éø(Ù—î½÷^3~üø’;A])wÞy%§ :A±;erƒ÷ƒ.¯Ó)+@±4K]©éJ­§Ø6Tcœö­2$*+‚²«¡ƒ_wÄùÛW¬ â¹sçæOžy]Ê03!OR- Ìm $ÖMº°­ì®ÖM¡‰'æ¬DÁ›l²IऽPæ”N8Ádw›äuœ[‰z)£K~Qf3 a¨!˜š >Ü(3P’ 矅{óÏB“$QC5Öçu½õÖKrUÊÚö´dæ-«Ò52q²“©¸FÞlN5uœ ëí:ŽŽ{¶¥ö Ç<…Bóš0† ¨Ñ–²4ë>š®ÿ'©$#|’DÙVˆT@Rê²L-¶Ï<óÌš Î zó0ÒÕ¹]Ī[[eÿÊ.jᯬrAŠºÒsoxèfHE©õ³KL@묳NNö }VÔŠ sçÎæßÿþ·m¡£Ìêš‚@1UVYÅ\z饶KXeèHzpžêªl#^E»a–úç邌Wvw»ÜLzÓ¦Ms…ò߯î~V¡¬”…„.°îºëÚàaŸvÚi© ÎSà—~ÏJeûT †n…U*‘NÙ×”F­ü*]Ô-ð/¿üÊjJ—Ë8Ê„°Ýð˜2eŠÙzë­m—1»ì²‹íÊH–å_ ÐÓgѯ,Z´ÈoÃk@@]½ÝqÇæÝwß5Çs ÁyìsϪ±Rv9õÔS³_&ò¹¾ƒ:tè³íü±ùé§Ÿr†Uâ…×÷XÒ.ŽWÂ…e6L@ÙcŽ=öX£ w]»Hzpž4üΩüÎÁê+Èùg}嘯¾-[¶4gŸ}¶™?¾m@UKÁy2óºvCÙú¾›â9Ÿ›­8ë”­¸Ò…LÅ•®þò7Þxcs×]wÙ‚#GŽL|pžD9æ)|_ùïùY.! à ¬¼òʶ ˜±cÇ&.8Oõ ƒž»7ù‰P€Ñ‰'žh ”é²3QhÀÆê¸‚ªxàÛÝ–.¼+€nî*ÐJ-TöÙgÛʸqãÆ XSºfUs=ö˜­”²ÏõíÛ×èÀX.*ê"ïŸÿü§}_ÙEþ(X¾2º1­÷b%û†‹‚/¶ÜrË’«U`‚nú 2ľgô^™0a‚O˸á†l@@É1AÍ èæÚñÇo¿S’ºœ¥`¯¢ÌI Ê£ÐúÜq·IAz*QfÒóëꛋî^‰÷í'©¡0Þ¿q®í¡‡jô›VªèÆHØ7tK·©Ë{¿¢c5u_¡ Yú¯ Ðÿûß~“£ê*š¨‚r·Šé@<èè¤÷õ×_7ÊLRKÁyêŠLAíÛ··]n¹Áyºh¹Ã;Ø›Ï ÈS+ ueçÞDT@Ÿºëúâ‹/ò4kó¥T§, nQwF+¬°‚ûÒ|ÿý÷ö‚pf€Ï“3fØ1QžHé†ËöÛoovß}÷zµ$ïÔ©“8p Ùi§Ì&›lBpžÏ¾­õÁ 0P@Ë;ï¼ctÀ›Öà<ígu›¤À&¯â—vÞkZ¯aaç¹ËÕ÷~¶wœþ‡ÕÝ­2z]|OC—Æ^uKú0eʹì²Ël`e­ç)Ùa‡Vr×)›™åê{ÃèÅ_´]ÙðÁëRæ;¿¢`¯÷ßßot,†ëÆB© ÿ z¥º°k€Þ‡~höÞ{oßà¼N8Áè= ßA÷¸ºÔÎS¿¢F~E /ü‚þüæaxòÔõÛ7ÞhfÍšeÎ oÿÝvÛm6éøñãí9¯¾“ößãõ¦sâ‹.º(gå:—V£¥´”8]#!@/-ïªhê¡kº~¥'i Γ$矹ï'Î?s=’òJ´ï¾û®ÍœWßs­¤ÔµØñ½êö̼IÙOQn§_¶âJn™Š+©í²uJM•É?Ê{JÑÖòµqÌ“«Î1O®¯(GàôÓO·÷*9äÔç©þè•ó.`Z¨š€`Õ2¯Z­Â«Uñ'Ÿ|Òv}¦ðÙ>tÃP4S§N5'Ÿ|²Í .[õc¥ Hî@MsçwVkócµ^7@/û¨iÓ¦æÜsÏÍÙΛo¾Ù<õÔS9ò_h?dw•›=Žç$]@A-úN)¼‘ôzºÛ¯ìI^eÚ´i^ƒ 38Ï]aAz~uö3r·ÿÕøÛßþfÔÕk±®5«·uá¯Y7HöØcÏÀ‹üµ)£ƒ_—ùÓæ¿V& uiºé¦›šnݺå6­Zµ*–=@ßq/¥ê´q‡WLvÝÕMz‹‚ž.\è¹ijÈ¡lÝåžsxÝ8qWP*ÈùÊ+¯t'-ú_ïÍR],]#«& ®¬Õ-Vœ¨ª†ÒŠŸ~úi›U_ ¬²Ë'Ÿ|b»‰R nvQ”ììyº¾pÕUWeOÂóx¯‡ˆY‹RÀO©^ ÒÂàwnåw.¤ÞœQbš°tž¥F|i-ÊÌ;lØ0£Æjl¤ Ú~%í™yýê]ËÃË=G ÊLÅa(ÆcºFUK½‚qÌóçûÎï8ÏÏèÏ9y†JJ¤ ß´ôÒ¶G©¤F@œ”a!;«†²[)C€ºXàU”î5;è¬X°™×üi¦›ú?þ¸­Zv€ž 2¤à¼º¾ÍˆÌ6™={¶m%© 5ÊJGAä (#«WyôÑGÍÌ™3½FV‰›#î +¤§ß¿`n?#w»ø@Têv[ŸÍRE]H*и¾E!”ÕLÅ«;ØR]>_{íµõ]udóm¸á†E×ôû¯T _vÖâ¢+Œx¤ÛhÃkµÊ4\ŸÀ’ÿýï^‹³ÃJèM˜0Á·Ûäì…*“x~0Röxž#PK_}õ•ouu¾¦ ÈãŽ;Î(ïÖ[o5—_~yfzSOš4Éè¼™‚D)àwnÅùçŸ{ÁÏèÏ)x†@eÈÌ[×4-µ>牕¨?™Š+¡Ê2Ãðû=ç˜çOi?£?§à¤U€½´îYê…‰PÚã·;[·2×]wínË}í÷_7ÜRªû1wº4ÿW7eʘ Œ1:tÈ©ªN®Õê1û$[ÝOùÝ`woêê¦s¶sÎBy‰Øh£|»o3fLYu¨dpž»!• Ò»à‚ Ì¯¿þê®&ó_]ynµÕV™×ûdÒØwß}íð }ÿý÷g&«oo™¤àIvP×Il÷îÝ lO:餜n\†3fاù™øÜñüGd ¨{¯2eÊóÖ[oy*Epž»Ò°ƒô>ûì3£ào¯¢LR´Ìõ’aX”ê®P]Cúe¶ÍÞeÙ+°‘=­ßsõ»Å+@OA¥>ú^)•]Î]G5þo»í¶9 ò·AwÙùãÝן|Úzë­uW÷ÓO?¹‹óü¯,È^Äž¨ìˆÅ–§`P¯òïÿ»hÇbïÏ7Þ¸d£uV,HOIô  €À ÄV0q—.]“¬´ÒJ6«ú AƒÏÄ €@Øœrþö{Šå5\€Ì¼ 7d  €@¾Ç<óä¿'x Ç;ˆ™À¨Q£ÌG}”³UmÛ¶5—\rIÎ0¿º±8yòäÌè=zdž×ê7@¯XP²e5mÚ4C¤ ÝèÏ.ºùê×Unötºäº^~ùe¢:|üñÇ‹ùòË/ÍE]dÖZk-³ãŽ;š#Ž8¨k%¿¢ã¿R,ãƒß<'œpBÑ@5uÃQÌ~ܸq~‹6Ûo¿}àãÏbß±î Þ}÷]÷iƒÿÿå/)šÙOÁŽl°:t¨­ƒŽùF]r½ ¦+VÔècñÅ/6‰wõÕW›u1Þ¦MHùä“O–œO{Z!@}ÿß|óÍFçiÊ<|øp ­ d½N˜0ÁË~úéös•À*²É 2Î?9ÿLÙ[:Õ!3o*v#•@˜ pÌÃ1OÌÞ’l±(}…86›Ê† €é¸á†L]]]NEÕšWVeêР߇~Øf×PæKª‡æÑ1žÊ˜Ÿ-YY~ÝójúìÀÁüu>÷”™(ï@IDATÜsv™ è Z”•MY›Õ¸Ã«èHß \{íµs&Qݯºêªœaî eÔñl2sæLã×¥löüêÚUÁÄ ® £((RÝøú•… Ú€O¿ñ^ÕÅKûT]hz­SA:άoÑqj±ŒÚ ¨TFÆüß«ú®ùH’€¾cõÐ猂Ä]€óÏÂ=Äùg¡ C¢p3ópÀfÒ¤I¾+Wf^—ë¡s8  €€¿Ç<…6óš0Z @¯Öö8õEØ è†š×X 8Ð(œð”%EÿýïžuÑ @ä)¨tåÕ)òâ‹/¶™_\Üûï¿ßL›6Í·¸™dj=+¡kÃÒ"°þúëÛïٻᄏ J=ö˜¹ð ÍqÇ—‡àûìcƒ‚®WŸÕ°‹_€žÖsâ‰'š^xÁL™2¥äj•åxÖ¬YE§;øàƒMÓ¦M3Óh¹Êœ¦ÆêvUǃ~EYßD§Æ«¯¾ºÍº¬cG½.V8¨ìÍÓ§O÷œLYþ:tè`”QSÇ>ßÿ½ÍúüóÏ{N¯u똩yóæžãh¨^ ˆTc-?HQV,ýéW] kùú^n×®]Ù ¦Q†i·ÑEÁHêWY¸Ž<òÈ‚ÀJwò?üШuøÎ;ïlÔݲê™_Øxï½÷ú.#z÷µ2„]{íµF DŠèé·éÿØ»0¹ªòqÀ'C鄪© ”fh!T©ÒAŠ€(¡I RýQ”@TŠÒ;ˆt顇ˆô^!ùïwýϸevwv³³{gæ=Ï3™¹íÜsÞswönæ›ïD€RŒûsÌ‘öÚk¯´à‚ ªñL€äDÀߟMÂߟM=,õœ@!3oüÿO|á;ñE©~ýú¥¥–Z*»ßßl³ÍæõÜ93U&àž§é€¹çiêa‰@Ý 4|ƒ_!@€f ÓzE»ìÑði³­•Y¼ÿþû‹ç,œ»œç†4üÓnì¦5|h8-ÚÚñ¤2 ¬ÒZ7Ø`ƒÌõŒ3Î(» S"5‹†„§5|(=­á[’Ùú†)”ʮˎù8è ƒŠãÚé&? Ó’\4dr*^Íß{‚w§~î¦ÌžÖ¬Ñê¾qlÃtÙÝÞ§†é&ÛlSô¡!1kWÃTžÓZÝ¿!€¤ÛÛï„ùh˜æ³xÍtÇ5Òð!Hñ|Í&»k¹!3]›Óðå‰i|ðt·³! kZCöµ&çÚ~ûí§«Þ“O>¹I}­-4Nk˜rºÎã±îºëNkÈ<×Úi²õ AyÓ}žÂØOÏïñèó²Ë.[V[>ˆ›öôÓOgí÷ÑÂùÛzn¤kÕá¹çž›6ÿüó—UOœ£!ÀnZCpgV_ǃeÇÆï«î( œY»²vwÇ霣 }öÙ§x­î_Ê8Ì.U.°é¦›ǽðÕÞûrWtyà 7lqÞwß}·+ª®ú:®½öÚ¢ÍI'TõýÑ®ð÷çÿÏ5Þ¯ºão‹®=µu‡@ðŠï¡‡vXwœÒ9´)Pê^«Ò÷<=qÎ6ZÙØðE±ìçuÀ€­ìau= ¸çqÏSÏ׿¾(%Ðt®®†¿ˆè†€‰’'>çœs²¬‘9c̘1骫®Ê2©D&–^x!›¾µ!¸/zê©iĈ©o_ÉQ ‘u0²‰DéHֻȢ×øX¨&sŽi.#ƒáì³Ïž>Ô-nó‚ÚXqÅ["32W5ËdS16𦘶²µÒœWö´œ­ÕÑ™õ‘I¯!H¯ÕC ÓÝÆT¾ñ~S—*‘u+ú¨ ²Ltm9D¦â†@¸Ó›6¬­][l‹ébzêÇ<›FvРA-ö™žóÎ;oY‡÷éÓ'5|‰!EÂŽÜ+*_}õÕ³¬p·Þzk–µ­°>ÏÏÑçË.»,Å7—Û*‘i/¦2dHŠ)#Ûu9¥-ûÈú×0•vÞyçÔÐÖfuaÙ WXa…l¿hC¹%þˆ,z õ#Pê=¥#ï•j+Ãkgëtzð÷çGÙߟõpµë#Ô³€{žÿ޾{žzþ)ÐwMDq4õ°D€˜8qb‹s/°À)¦ûDã~EâQç¯èCô%Æ&ž }+8ŽìJç"óÊÅ_œ¼Ê*«t¸’Yf™%p -Ž ×‚Ä 5%Yò:\“—à¼Â@´7Ýma¿ÆÏ›l²IÚn»í¯òš @ J¶Þzë-=ýôÓSÑUª{ì±YÏÆõû{¹±†×Úð÷gûFö @€@ZËV\ɶÉT\I]uw·€{žîw>ò* @/¯#£]Ô@©`¼˜Ké˜À‡~˜xà4|øðôøãg_~ùå)¦Êëè·ÎvÚi§´òÊ+S»-»ì²Åe/¨MSO=5­¸âŠeu.oÁy…Fw$H/²~Eæ@… @ :V_}õ,s|ãÖ¿öÚkiƒ 6Ȧßn¼¾+^ÿéOJ£FjRU|é0¦W蘀¿?;æeoô„@kÙŠ+Õ™Š+%«ÞžpÏÓ“úÎM€@^èåe$´ƒº(5eÕÔ©S§Û%¦hýôÓO§»ž¼Wðù矧2‰?–¿÷½ï¥›nº©Øä΋é‚gžyæ"w*ñ"R×Ç”…öC‡-¾.±»UÔˆÀ€Ò 7Ü^xá6{Óî¿ÿþmîÓ“wÜqÇvOà7ß|sÞ.• @€¹ˆ òƒ jÒÆ»ï¾;û[øÒK/M]ñÿ /¿ür–mzçwnržX8ñÄ“ z-X¬ Ю€¿?Û%²z\ »³ËTÜãC®pÏSTU PuôªnÈ4˜Z2dH‹®½úê«-Ö•»"²ÅÅŽQï–[nYîaU»ßG}”&Mš”fœqÆ4÷Üs§\0-²È"iðàÁiÞyçMqóHôëׯC}Œ`¿ã?>«w‹-¶èбv&@ zâ}#}g›m¶V;Si/·Üriܸq­îÓS~øáìÃØ¶Îß¿ÿtýõ×gÁÍmíg @€@þâoàÈ&ßúètÕUW¥É“'·Ù©iÓ¦¥þóŸiôèÑéCI«­¶ZZtÑEÓÿøÇÛ eŽ9æH×]w]:øàƒ «< ÐAvÌîèfîÌV,Sq7®Óu«€{žnåv2r( @/‡ƒ¢IÔ§À\sÍ•¾ýío7éüc=Öd¹Ü…È&7bĈô·¿ý-}ðÁiàÀåZµûÍ3Ïzè¡)êÝn»í:|¬¨^ÈlzõÕW§dk­¼ñÆiuÖI#GŽL°—‡rúé§g ¾ð ­6§oß¾)²¬D²B€ P¤wÇw¤¿ÿýïi›m¶É¾hVèYd¿»üòËÓQG•6ß|ótÆg6•|ŽÿXsÍ5Ón»í–e¢¿ï¾ûšæEÀÞ˜1cRL¥»ñÆ—¬ÃJÊð÷gùVö$@€@OT:[±LÅ=1ªÎÙîyzBÝ9 È‹@ß¼4D; @ ¥ÓN;-­·ÞzEŠ{ï½7=úè£iùå—/®kïE¦ýô§?MwÞyg¶k|Kþ”SNiï0Û  @ „@|(p><½óÎ;%öHYvΘzâÿøG:묳²¬z%w¬ðÊçž{.xàéÆolóL1Ýw|8;lذ6÷³‘ @ úzõê•ÖZk­ìñþûïgÙí^zé¥ôú믧‰'f_`‹ìòK,±D›‹ím´QŠcã … ,°@–©>‚‡Úâ †mVf#e øû³,&; @ G ÙŠã³—ø?ÀB)d+>üðÃÓÊ+¯œÝ#}ç;ßIn¸a«³ùD6â»îº+=ÿüó)Ž¿ûî»Óý÷ßßäËQd*¾ð }¢€í¹fÜóÔÌPê ×A0» @ ’ë®»nÓÃÊG‘epŠlGm•˜ž&ñb:ÖÏ>û,Ûu™e–I·Þzk4hP[‡ÚF€m|ÿûßO‘1dýõ×Ome¥‹ÿX‹€êø ó7¿ùMZuÕUÛ¨µë6=ñÄé¸ãŽË‚îâÃÖ¶Jü>ˆ¾ï~÷»míf @€@ Ì6ÛliÇwìTOfši¦týõ×wêX ÐyvÞΑ¨´@![ñ¸qãÒù矟}nóÅ_d§ x…ŒÅ±â¤“NJ‡rHÉ&2—ÜØ°22ï±Çi«­¶jsfÖŽ·ž@5¸ç©†QÒFºZÀ·]-ª>L§À©§žš};½PÍ 7Ü{|øá‡…UÅç˜N1¾­µÿþû§È”!œ×§OŸtðÁ§|Pp^QË t^`ñÅÏ‚ôÊ™6Þ·c¿˜ú¶ñ7j;öÒGþë_ÿJ›l²I–±/¦4o/8/¦Qoã Î+íi- @€ò àïÏ<Œ‚6 @ ´@![ñ%—\’Þxã4f̘tä‘G¦ÝvÛ-›­b¹å–K‘A¯­lÅ…LÅñu?üáÓöÛoŸ;ì°töÙg§'Ÿ|2ÝsÏ=Ù—,ú÷ï_ºÖ¨÷<52ºA€@Ùm§c*»; @€@W D ]|ƒj‡vȦUŒzÇŽ›¥3_j©¥Ò矞Yd‘lZš×^{-}ôÑGMN›bŠÅH§® @€@× Ì9çœÙôáÇsLö-Ø)S¦´YyLÁƒgÁz°·öÚk§¹çž»ÍãZÛS”Å7to»í¶tûí··™Í¯qñ‡ûì³O:ñÄÓ€oòš @€r(àïÏŠ& @ ™@g³ËTÜ Òb] ¸ç©ëá×yu' @¯î†\‡ ¨˜‚0¦¦4è§vZzûí·SdË‹oOEyñÅ›tcÑEM?ùÉOÒ6Ûl“};«ÉF  ÐeßøÆ7Òoû۴馛¦vÚ©ø¾ÜÖ ^}õÕtÁd–[vÙeÓСCÓ|óÍ—eLk®¹ŠÏ±ýwÞÉÞ÷ã½?“&MJ÷Þ{oz衇ÚÍ’×¼ñû!νæšk6ßd™ @€r,àïÏަ @€]&àž§Ë(UD€@Îèå|€4úèÝ»w–ÖqÆ‚ó:¤gg @€äSÀߟù­"@€ºVÀ=O×zª| ÐË×xh  @€@• Ä´ä§žzjz饗Ò/ùË Šëׯ_ÚsÏ=³À¼?üáYvÕ*cÔ\ @€hGÀߟíÙL€Ô„€{žšF @ ™€½f   @€tF`žyæÉ¦»}óÍ7Ó¥—^šFŒ‘fœqÆÎTUÖ11í°aÃÒ…^˜þóŸÿ¤sÏ=7›þ¼¬ƒíD€ @€U+àïϪ: '@€: àž§Xv%@ ÷}sßB $@€ PE H[o½uöøè£Òu×]—Æ—þýï§§žz*}õÕWêMŸ>}Ò!CÒ +¬†š6ÝtÓ4Çstª. @€ @€@õ øû³úÇP @€öÜó´odò/ @/ÿc¤… @€U*0Ë,³¤í·ß>{D&Ožœž|òÉôÈ#¤I“&¥>ø ÉcÚ´iiÖYgMßüæ7‹ø–àòË/Ÿ–[n¹Ô¿ÿ*•Ðl @€¨¤€¿?+©«n @ /îyò2ÚA€@GèuTÌþ @€:)Я_¿´âŠ+fNVá0 @€ Ю€¿?Û%² Pîyj`u@ô®“~ê& @€ @€ @€ @€èVzÝÊíd @€ @€ @€ @€ P/ôêe¤õ“ @€ @€ @€ @€ºU@€^·r; @€ @€ @€ @€Ô‹€½ziý$@€ @€ @€ @€ @€n ×­ÜNF€ @€ @€ @€ @€õ" @¯^FZ?  @€ @€ @€ @€ @ [èu+·“ @€ @€ @€ @€ @€@½Ы—‘ÖO @€ @€ @€ @€èVzÝÊíd @€ @€ @€ @€ P/ôêe¤õ“ @€ @€ @€ @€ºU@€^·r; @€ @€ @€ @€Ô‹€½ziý$@€ @€ @€ @€ @€n ×­ÜNF€ @€ @€ @€ @€õ" @¯^FZ?  @€ @€ @€ @€ @ [èu+·“ @€ @€ @€ @€ @€@½Ы—‘ÖO @€ @€ @€ @€èVzÝÊíd @€ @€ @€ @€ P/ôêe¤õ“ @€ @€ @€ @€ºU@€^·r; @€ @€ @€ @€Ô‹€½ziý$@€ @€ @€ @€ @€n ×­ÜNF€ @€ @€ @€ @€õ" @¯^FZ?  @€ @€ @€ @€ @ [èu+·“ @€ @€ @€ @€ @€@½Ы—‘ÖO @€ @€ @€ @€èVzÝÊíd @€ @€ @€ž¸ë®»z¶ÎN€ @€: WGƒ­« @€ @€ @€³Ï>;M›6  @€tƒ€½n@v  @€ @€ @€@¦Nšn¾ùæ4~üø<4G @€ Póôj~ˆu @€ @€ ð_Gy$}øá‡éÿø @€èzÝ€ì @€ @€ @€<Ü~ûíY3n»í¶<4G @€ Póôj~ˆu @€ @€ ð_B`^dÐûú믱 @€ @€@…èUXõ @€ @€ @€<|úé§éî»ïΚÓÜÞÿýyh–6 @€ @ ¦èÕôðê @€ @€ @à¿cÇŽM“'O.r\{íµÅ×^ @€ @€@eèUÆU­ @€ @€ @€\ 4Èk¾œ«Æj  @€jD oôC7 P1gžy&ÝsÏ=«_ÅtÀ믿Þ='rÔ¸ÀøñãÓ,³ÌRã½Ô=ÊøøãËÙÍ>=$ð裦O>ù¤‡Îî´ê[àé§Ÿ®o½Ï­À”)SÒ7ÞØ¤}Ï=÷\Šÿÿ2dH“õô”À´iÓŠ§ž8q¢ÿ›/jxA 1mºB€”' @¯<'{ PLJvX÷^×  @€M:è ¦+, @€@.vÚi§\¶K£ @ çþþ÷¿§wß}·E.»ì²4räÈë­ Ð½zõ*žö/ùKЇB€ @ ÚLq[í#¨ý @€ @€ @€vþú׿–ÜãÒK/-¹ÞJ=!Ð8ƒ^Oœß9  @€TB@½J¨ª“ªXe•UÒ!‡RõýÐJ Ì?ÿü¥7XK€%V[m5÷F%e¬$@ z÷öýϼ\ ë®»n0`@^š£4¬ºêªäB`òäÉéꫯ.Ù–˜â6¦F_~ùåKn·’@w ,¸à‚þþìNpç"Ðýúõë‚ZTA€j[ WÃ7Q¦ÕvõŽ @€ @€ P¿W^yeÚb‹-Z8è ƒÒ¨Q£ZÝn @€t^ÀWœ;oçH @€ @€ @€@î.¸à‚6Ûø—¿ü%M™2¥Í}l$@€ @€Î Ð뜛£ @€ @€ @€¹xýõ×ÓØ±cÛlç›o¾™n¸á†6÷±‘ @€:' @¯snŽ"@€ @€ @€ä^`̘1é믿n·£Gnw; @€ @€@ÇzMk(?Ì @€ @€ @€y˜:ujZl±ÅÒË/¿Ün3ûôé“&L˜Üî¾v @€ @€òdÐ+ßÊž @€ @€ @€ª¸îºëÊ Î‹E–½³Ï>»jú¦¡ @€¨ôªe¤´“ @€ @€ еÖZ+7®ì#æ˜cŽôÚk¯¥þýû—}Œ  @€ @ môÚö±• @€ @€ PuO<ñD‡‚ó¢ƒï¾ûnºøâ‹«®¯L€ @€< ÐËóèh @€ @€ @ 'Ÿ|r'ŽJiÔ¨QiêÔ©:ÖA @€ ÐR@€^Kk @€ @€ @€U+0a„t饗vªýÏ=÷\ºâŠ+:u¬ƒ @€ @ ¥€½–&Ö @€ @€ @€ªVओNJ_ýu§ÛüñÇwúX @€ @€@S^ÓJÓU– @€ @€ @€ªQà•W^IK,±DúòË/§«ùW_}u1bÄtÕá` @€HI=W @€ @€ @ FŽ8âˆéÎ ŠÃ;lº²ðÕ§n @€ @`ºèM7¡  @€ @€ @€=/ð裦‹.º¨KòÌ3ϤѣGwI]*!@€ @€@= ô­çÎë; @€ @€ @ VÆŸöÞ{ï6»så•W¦ÿüç?©W¯^íîûöÛo·Y— @€ о@¯i ¥ýÝìA€ @€ @€ Pí«­¶Zºï¾û²½©S§V{w´Ÿ @€¹0Åmî‡H  @€ @€ @€ @€ @ èUã¨i3 @€ @€ @€ @€ä^@€^î‡H  @€ @€ @€ @€ @ èUã¨i3 @€ @€ @€ @€ä^@€^î‡H  @€ @€ @€ @€ @ èUã¨i3 @€ @€ @€ @€ä^@€^î‡H  @€ @€ @€ @€ @ èUã¨i3 @€ @€ @€ @€ä^@€^î‡H  @€ @€ @€ @€ @ èUã¨i3 @€ @€ @€ @€ä^@€^î‡H  @€ @€ @€ @€ @ èUã¨i3 @€ @€ @€ @€ä^@€^î‡H  @€ @€ @€ @€ @ èUã¨i3 @€ @€ @€ @€ä^@€^î‡H  @€ @€ @€ @€ @ èUã¨i3 @€ @€ @€ @€ä^@€^î‡H  @€ @€ @€ @€ @ èUã¨i3 @€ @€ @€ @€ä^@€^î‡H  @€ @€ @€ @€ @ èUã¨i3 @€ @€ @€ @€ä^@€^î‡H  @€ @€ @€ @€ @ èUã¨i3 @€ @€ @€ @€ä^@€^î‡H  @€ @€ @€ @€ @ èUã¨i3 @€ @€ @€ @€ä^@€^î‡H  @€ @€ @€ @€ @ èUã¨i3 @€ @€ @€ @€ä^@€^î‡H  @€ @€ @€ @€ @ èUã¨i3 @€ @€ @€ @€ä^@€^î‡H  @€ @€ @€ @€ @ èUã¨i3 @€ @€ @€ @€ä^@€^î‡H  @€ @€ @€ @€ @ èUã¨i3 @€ @€ @€ @€ä^@€^î‡H  @€ @€ @€ @€ @ èUã¨i3 @€ @€ @€ @€ä^@€^î‡H  @€ @€ @€ @€ @ èUã¨i3 @€ @€ @€ @€ä^@€^î‡H  @€ @€ @€ @€ @ èUã¨i3 @€ @€ @€ @€ä^@€^î‡H  @€ @€ @€ @€ @ èUã¨i3 @€ @€ @€ @€ä^@€^î‡H  @€ @€ @€ @€ @ èUã¨i3 @€ @€ @€ @€ä^@€^î‡H  @€ @€ @€ @€ @ èUã¨i3 @€ @€ @€ @€ä^@€^î‡H  @€ @€ @€ @€ @ èUã¨i3 @€ @€ @€ @€ä^@€^î‡H  @€ @€ @€ @€ @ èUã¨i3 @€ @€ @€ @€ä^@€^î‡H  @€ @€ @€ @€ @ èUã¨i3 @€ @€ @€ @€ä^@€^î‡H  @€ @€ @€ @€ @ èUã¨i3 @€ @€ @€ @€ä^@€^î‡H  @€ @€ @€ @€ @ èUã¨i3 @€ @€ @€ @€ä^@€^î‡H  @€ @€ @€ @€ @ èUã¨i3 @€ @€ @€ @€ä^@€^î‡H  @€ @€ @€ @€ @ èUã¨i3 @€ @€ @€ @€ä^@€^î‡H  @€ @€ @€ @€ @ èUã¨i3 @€ @€ @€ @€ä^@€^î‡H  @€ @€ @€ @€ @ èUã¨i3 @€ @€ @€ @€ä^@€^î‡H  @€ @€ @€ @€ @ èUã¨i3 @€ @€ @€ @€ä^@€^î‡H  @€ @€ @€ @€ @ èUã¨i3 @€ @€ @€ @€ä^@€^î‡H  @€ @€ @€ @€ @ èUã¨i3 @€ @€ @€ @€ä^@€^î‡H  @€ @€ @€ @€ @ zMk(ÕØpm&@€ @€@µ üú׿NÏ?ÿ|µ5[{ Ô´ÀÈ‘#Ó2Ë,SÓ}¬†Î=øàƒiÔ¨QÕÐTm$@ ‡N;í´4ÿüó÷ÐÙ¶ pë­·¦óÏ?¿°è¹JÞzë­ôå—_f­_`ª´š]XsÍ5Ó>ûìSXôL€ @€@úæ°MšD€ @€š¸ãŽ;Òý÷ß_“}Ó)Õ*°çž{ ÐËÁà½ñÆéòË/ÏAK4¼ }ôÑôr08/¾ø¢÷댃&h,пzA¼&@€ CSÜæpP4‰ @€ @€ @€ @€ª_@½êC= @€ @ Êz÷î&NœXe­Ö\µ#S©ÆT‰J>Ž8∴×^{å³qZE€@· tÐAé’K.éÖs:Yù¿ÿýïÓf›mVþö$@ Ë^~ùå´Új«uY}*"@€ @ ²ô*ë«v @€´èÕ«W4hP‹õV Ð=ìž9K§f™eï‘’sÚ0`@íuª†zôÍo~Óûu §®T—ÀçŸ^] ÖZ @€u.`ŠÛ:¿tŸ @€ @€ @€ @€*# @¯2®j%@€ @€ @€ @€ @€: Wç€î @€ @€ @€ @€ @€@eèUÆU­ @€ @€ @€ @€ PçôêüÐ} @€ @€ @€ @€¨Œ€½Ê¸ª• @€ @€ @€ @€ê\@€^_ºO€ @€ @€ @€ @€• WWµ @€ @€ @€ @€ @€@ Ыó @÷  @€ @€ @€ @€ @ 2ô*ãªV @€ @€ @€ @€¨szu~è> @€ @€ @€ @€TF@€^e\ÕJ€ @€ @€ @€ @€u. @¯Î/Ý'@€ @€ @€ @€ @€ÊЫŒ«Z  @€ @€ @€ @€ @ ÎèÕù û @€ @€ @€ @€ Pz•qU+ @€ @€ @€ @€Ô¹€½:¿tŸ @€ @€ @€ @€*# @¯2®j%@€ @€ @€ @€ @€: Wç€î @€ @€ @€ @€ @€@eèUÆU­ @€ @€ @€ @€ PçôêüÐ} @€ @€ @€ @€¨Œ€½Ê¸ª• @€ @€ @€ @€ê\@€^_ºO€ @€ @€ @€ @€• WWµ @€ @€ @€ @€ @€@ Ыó @÷  @€ @€ @€ @€ @ 2ô*ãªV @€ @€ @€ @€¨szu~è> @€ @€ @€ @€TF@€^e\ÕJ€ @€ @€ @€ @€u. @¯Î/Ý'@€ @€ @€ @€ @€ÊЫŒ«Z  @€ @€ @€ @€ @ ÎèÕù û @€ @€ @€ @€ Pz•qU+ @€ @€ @€ @€Ô¹€½:¿tŸ @€ @€ @€ @€*# @¯2®j%@€ @€ @€ @€ @€: Wç€î @€ @€ @€ @€ @€@eèUÆU­ @€ @€ @€ @€ PçôêüÐ} @€ @€ @€ @€¨Œ€½Ê¸ª• @€ @€ @€ @€ê\@€^_ºO€ @€ @€ @€ @€• WWµ @€ @€ @€ @€ @€@ ô­óþë> @€ ÐHà­·ÞJ/¾øbzá…ÒG}”œYd‘´ð §™gž¹Ñž^ @€ @€ @€í ÐkOÈv @€ P×_}:òÈ#Ó#<Òjo×\sÍt衇¦aƵº  @€ @€ @€ÀÿèýÏÂ+ @€ Pwï¼óN>|xºï¾û²¾÷ïß?­´ÒJi¹å–Ko¾ùfzòÉ'ÓÓO?m»óÎ;S<–_~ùtå•W¦E]´î¼t˜ @€ @€tD@€^G´ìK€ @€jHàÃ?L묳Nzì±ÇÒÀÓŸÿüç´á†¦f˜¡I/oºé¦tðÁ§ñãÇgë}ôÑ´Þzë¥{ï½7Í=÷ÜMöµ@€ @€ @€üO ÷ÿ^zE€ @€Ô“ÀYg•çEŸ#3Þˆ#ZçŶ 6Ø =ðÀi饗ŽÅ¬¼øâ‹i£6*,z&@€¨s©S§f˜'MšT–ÄW_}•Þxã4yòä²ö· @€ªU@½j9í&@€ @€@ ¼ûî»Y@ÙÇÜéÞÏ5×\iµÕVëðñï¿ÿ~úÇ?þ‘î¸ãŽôÖ[o¥Å[,m±Åi…Vèp]y9à¼óÎ+6e‡vH‘¯oßÒÿU0óÌ3gÓÚ.»ì²)>Lòàƒ¦—^z)-²È"Åz¼ @€¨}¯¿þ:ÝxãéŠ+®H/¿ürzíµ×Ò믿^¼O<å”SÒ/ùËV!bß%–X"}öÙg©W¯^YVæ\0 <8ûRÈî»ïž½nµ @€ PE¥ÿ×½Š: © @€ PûcÇŽMx`qŠÕéíñÅ_œ¶ÝvÛ²«9ùä“ÓᇞâƒÈøqÚ´iÙ±'žxböÁã¨Q£Ê®+/;F¦’‰'›óÔSO¥[n¹¥Í¬xC† IË,³Lzä‘GŠÇÝsÏ=ôŠ^ @€j[ ²ã{î¹iôèÑY@^©ÞöéÓ'Å—;Ú*ßøÆ7ÒL3Í”èŽõ›o¾™=zè¡tÕUW¥N8!m¶Ùfé€èÔ—kÚ:·m @€ènSÜv·¸ó @€ @€@ÙS¦LI;î¸c6lX—çÅÉãÀrÊ_|‘6ß|óô«_ý*Í7ß|i̘1éÓO?MñÁaïÞ½³@½ÈÙCª­Ä‡¢ƒ jÒìèS{%2è5.ƒõ¯÷š @ ¶"[ÞÒK/Ž9æ˜&ÁyqÁtgžyfŠ/o|ôÑGiÏ=÷l³ó‘Õ:‚ýâK"—]vY9rdZxá…‹ÇÄc.¿üò´úê«§ý÷ß¿¸Þ  @€T£€ zÕ8jÚL€ @€:˜:ujúéOš.½ôÒbo˜–[n¹4`À€tï½÷¦˜êvþùçϲºvpE"@IDATzòÉ'³ çwÞlßÂúxža†ÒŠ+®˜bʬrÊÁœeðˆ¬q·ß~{šgžy²Ã¢Ž˜’ë™gžÉ–cêÛ 7ܰœ*s³OdŒB÷Ýwß,Ðpøðáiýõ×o·}~øa“}ÂY!@€ø¯@|d„ 饗^ÊÐâž¶R%2ÕÅ—."ØmÉ%—Ìî}ã¯%‚è¶Þzëb&éÂ9¶Øb‹tôÑGg{…uå>÷íÛ7;.‚þ¶ÜrËô›ßü&ýùÏNqþþû韛À¿þýû§È^­ @€ @ èUã¨i3 @€ê@àÐC-çí·ß~iï½÷΂â :F–Ž«¯¾:›öê ƒ*ŠÄw‡vXÚd“M²é·Š:ø"ò~ÿûß§Yg5ÝvÛmÅ༨&¦ázçwŠ5Ƈ£ÕX~ö³Ÿe†F¦ÂBða[ýˆL&?üp“]šgÔk²Ñ @ ¾úê«, Ügœ‘|ðÁëqÜ—;6uõ(Þ}÷Ý´Ë.»´΋,Ó]4_¦Ùu×]SôcuÖÉ2ñ0O:é¤ôýï?»Ç/¬óL€ @€jhîšjé‰v @€ @€@ͼ÷Þ{éÔSOÍ2e\tÑE)>쌬 …à¼ÈâvÓM7eý]{íµ›ôûÈ–cJÚé)§vZvø±ÇÛâCÎñãÇ7 Ћ¬~ÕZæ˜c޲‚ó¢1¯½öZ±«ƒN?úÑŠË^ @€êMàšk®É¦fÝ~ûí{48/ÜüñtóÍ7wùDV»O?ý´I½?üáÓñÇßd]W-¬¼òÊéôÓOoQ]dÒS @€ PôªqÔ´™ @€5.0ûì³§ûï¿?½øâ‹i»í¶kÑÛ+¯¼2Mž<9Å~̓ãº*@ï€HgŸ}vÚgŸ}Zœ?¦´m\âÊZ/1][L;Ö¸wÜqÙ´j×yM€¨Èڼ馛¦7Þx#7].|¡¥+ÍKÜ+÷î]¹˜¶ÝvÛ4çœs69í¸qãR%§ nr2  @€èBSÜv!¦ª @€ @ ëVZi¥V+»ä’K²mk­µV“#óÞ¤I“²mýû÷oõør6¬»îº)¥Jã½Èì7hРR»Õ̺†Œ)…'NœXìÓÈ‘#SdŠQ @ v{ì±ôÏþ31"-¸à‚µÛQ=#Ð k¯½¶Éô®sÍ5WŠ µxîׯ_êÓ§O1ûs¡úø’ÉUW]UXÌž#ëó¨Q£š¬kmá믿Î2Ù½üòË)2÷=ûì³Mv]tÑEÓFmÔd]W,Döêæe…Vh¾ªK—Ãp™e–Iï»§M›–>ûì³4óÌ3wé¹TF€ @€J Ы´°ú  @€ @ K^}õÕÙ3¢¬³Î:MêŽ,o…Ò8˜¬°®+žãƒÁ;ï¼³XU­gÏ‹A7ß|ótï½÷f}ŽL)'œpB:äCŠ^ @€@mÄ–ñ~ýõ×§ë®».½òÊ+YÇ"Ðhï½÷®Nê.¸é¦›²šfœqÆì^)¦dmžñ­ù©Je¢[sÍ5Sd‹ëhùÕ¯~•e“.·Á¤Ñ£G·Û†ÂþÓûï •.³Ì2K‹S|ùå—-ÖYA€ @€¼ ÐËûi @€48ùä“‹S[5Ïp÷Ö[o÷}íµ×Н»òÅSO=•Þ~ûíb•µ ÷Á¤7Þ8Ý}÷ÝYçž{î4f̘´þúëûïjˆ€ßÈ€ÏÍ¥ÖÖEÐnd4*üðÃb}¥^|ôÑGY€Û /¼Ðe¿gJ§ëâ?ãŒ3ŠU_{íµiĈÅe/èz[n¹¥E¥ôZXA€ @€š W“êS @€¨=Ñ£G;µå–[¶4³òÊ+ô"@㡇JC‡-;=/OoõÔJ€^L¼Ë.»¤K/½4Í>ûìَÇo•êã?N7Þxc¶=¦yTÔŠ@dáŒ,™§s.·owÜqGŠ §òj«Ì2Ë,é²Ë.k5øµ­cm+_`¶ÙfËÞûÛ Ð›a†²½Æµ®´ÒJÙâ¾ûî›öÞ{ïlLooþzÒ¤IY Ûa‡Ö|SU-—š6¾ª: ±r._ò˜8qb“VF°Ÿ½&$ @€ P³ôjvhuŒ @€µ%0óÌ3gŠŒVtP«ûõ¯þö·¿¥¯¾ú*ËŠôýï¿Õ};º¡q€^dŠ)«½¼õÖ[YÖ¤ûî»/ëJô)2GÅ£qùúë¯3ÓÉ“'§ &¤˜72-½ôÒwóš@U DæÈÎçE&µwÞ¹Ýà¼À‰Ìž2OvÏe²à‚ vúDóÍ7_ºúê«S}ÇTßm•‹/¾8U{€^dŠŒŒ©ñ¯ Ðõ¥¦·]c5²/FtýÙÔH€ @€y —·Ñ @€()0nܸr«®ºj›Á-‘+¦¸}ôÑGÓl"CRW”ÈŠ™µ ¥²çE6—6Ú¨ÉO?ýtŠG9e‘E©ºiËé—}êWà?øA§:¿ÿþû§W_}µÝc#póÍ7ow?;t@L{>=¥W¯^éÔSOMk®¹f›ÕDÐrüŽˆý«¹„—½jAmϳ@©émM+çÓ6 @€t­@ï®­Nm @€¨?þñé·¿ýmúì³Ïª§ÑuÜÒÈf´í¶Û¶œWà‰À±M7Ý´K§|àÒ;ï¼S8EZwÝu‹¯§÷ÅÝwß"ÀgµÕVK[mµU:ñÄÓçŸ^²Ú»îº+xàYÖ»ÈSÑpÀ©Ô¿%+øÿ+¿üòË´öÚk7 ÎkkÿRÛ–Yf™R«­«vÚ)½òÊ+5Ò›ö»ÁUßúÖ·Úß±Ù1­í˜1cš­m¹™ØN9å”–¬©˜@ÿþý§»î˜"ý»ßýn›õÄûõo¼Ñæ>Õ°±Ú «Á¸m|÷ÝwÓ>ûì“"#®’Oø[#îßš—M6Ù¤ù*Ë @€ P£ôjt`u‹ @€öbJÂ#Ž8"-¾øâ)‚õ¦L™ÒþAö¨Yÿüç?iï½÷NÜwøá‡·èçùçŸ_\7xðà´ñÆ—§çÅ5×\“eg:óÌ3SL3{ùå—gS%F@Hd,”Œ€¸9í´ÓRÝÿýéúë¯OgœqFZýõ³ì‚ÑrÊÔ©SÓ{ï½Wή­îóío»Õm6T¿ÀE]”–\rÉlJéé½VªAcþùçO 芩ž÷Øcv»7pàÀlêí˜FTé>¾}»fò%–X¢ÝF×ÃÏH»vèø}ÓÒÇÔÙÇsLúä“Oz¤NÚº@d`þâ‹/šìAÛ /¼p“u @€ @ vèÕîØê @€e Lš4) °ˆÌIçœsŽéÝÊt«µÝ"›bŒÿË/¿œN8á„ôÒK/»Së^xá…ÅåÈX×ãÇϲƇë¿üå/³ »…Z(;OLÓ»Ùf›eèFÖ­ÕW_=Å”´ñaîqÇ—eÌ‹|cÊÌ~ýúeÇüë_ÿJ[n¹eYÁ¦1•adÑ‹i;ûˆv(µ-Ó]þîw¿Kq]þêW¿Jo¾ùfÍuøÏþs;vlºâŠ+:Ü·=÷ܳ¬¬UaÁ¿Ju ,ºè¢í6|žyæiw;¨¤@æyä‘Ù}±Ç›>øàƒJžN݈ß1ÍËO~ò“æ«, @€ @€@ ЫáÁÕ5 @€Úˆ ¤Æ%³ ÔF•ÞÿýÆ›½®qÈHW(¼6묳f‹·Ýv[6ílÑEÙ~ûí³ ¸la:ÿ9ú裳©l#/‚ðbºÚ#Fk ÁÍ7ß<|ðÁ)Î×çO<‘eøûñœeÓ‹Ì{Æ +ÓåF¶=…@W DàÇÉ'Ÿœ~ì»ï¾é…^èÊê{´®5ÖX#­·ÞzYÊŽ4$û®¾úêv‰ŸÏÝwß½Ýýì_ö2+Fæ²¹çž;¿вºˆ)oGŽ™Vzè¡éõ×_¯«þç±³7ÝtS“fÅtÒñ…Šj/}úô©xºãï„ @€ @ A kæy@I€ @€ˆŒz‡rH:ꨣÒ;ìc™Ê³†¸•®¬ºêªiâĉÙÖ΋à¹'Ÿ|27.Ë07à 3¤<0Ë®¬Noyíµ×Š»bJºB0`@áeö\øP7¦¯ûÙÏ~Öd[aaà 7̲ï–cjÜȸ§èj˜¢ïÿþïÿ²é7Ø`ƒ´ß~û¥u×]7uÅÏDW·µ’õÅÏoô½½òÍo~3=º½Ý:´=²ÞxãY–ÏW^y%{Š)Xc:â!C†¤ÈÛ^@Y{'ŒÌ[qŽ[o½5ÅûËV[mÕäx¹ù曳i¸ç˜cŽ,Xxë­·n²Ok _ýuú÷¿ÿMÑSrÇÔ°AQ×ì³Ïž¢?k®¹fŠi‡óPb¼Û*«­¶Z[›[l‹/<òÈ#i„ éÅ_LP}@¿Å_<-»ì²iРA-Ž›žqosË-·dÁµüÓ-/°Ài饗ΦHŸm¶Ù¦§zÇæPà£>J'tR–5‚ý#ã®{ƒî¨ûî»/=ÿüóMN_Š ÷Ž”ø=Û»wïì}~¯½öêÈ¡]²oó>D¥Ý1mûL3ÍÔ¢ýñ»/~W( @€¨&zÕ4ZÚJ€ @€@· |öÙgé¼óÎËC‡M»îºkÚb‹-R©ŠºµaNV1cÆdSPF&½ø22ÓE&½Î,"ue°Èå—_žeÅ[~ùå³s:AÍKdßj-8/öyæ™›òÖ[o5Y¶@ «"¸ ¸âañþøÓŸþ4ÕÃ4ŸÑ÷wÞ9}øá‡í²žuÖYi¾ùækw¿rvxüñÇÓ‰'ž˜®¼òÊlzêÖŽ‰à¼Ã?<˼Y˜þºµ} ë#h.ê¿ë®»Ò 7ÜbZﯾú*ÛÛ z ïEúÓŸ ‡fÏ÷ÜsOj/@/‚ú"X(®™?þ¸Éñ­-DÀáþûïŸy—Û—Öêšžõ§;{ÄûÄÆoœÝŸÄÏY[Ù1k ‚)5½m¬‹ÀärJ|)(œãýþç?ÿyŠŸáî*ãÇϦJožÅ3cÊäÁƒwWSÒgœ‘^xá…&ÓìÆï¤e–Y&»n‡ Ömmq" @€tV *ôbÚ}öÙ§³}t @€*.0qâÄÓÆ#ÖXc´îºëf™£"óCŸ>}*Þ†Z8Ad‹`j. ×ÙÒ<^|Û^‰Œ"³N­¿þú®·öÐm?æ˜c²iþ­òòÿ tÕÏb^E¶¶xÌ0à é{ßû^`ÁtP-Í‘E(þª½²í¶Û¦Í7ß¼½ÝÚÜ~þùç§Ýwß½Å>óÎ;o7n\2dHq[ñÆ4¦‘«ñ{Aa‡¾‰º®»îºÂªlÊܵÖZ+½÷Þ{éøC6µwqc³(Ø^i SGƽւó"PóòË/o2ö¨Ùæ"[^kÓzÇ5YŸ~úé4çœs¶×¬²·‡CØEðc”‰ÀºóÖJŒE¡v¤D¿ãÞ ¦L€ôèGº´—96¦Ž ö‚sŽ>úètÔQGµhRLÃÁ[o½u“màïqïR %dãËJSÎdglZÃÿ–"3Y¼ÿÄ#±~ðƒdïבÁ2‚ÿÝÏþϪ½W‘A44.1õxG²€–“‘¹qýÓó:²®F›#óf´;‚¿›ÿNYd‘E²iÎãïœî, xß}÷¥]wݵɔÁh÷½ñ·VLÓ^xÄ}†LÝ9BÎE€ @€@9U WNGìC€ @€¼|öÙgÙ4€1`-DÄ^{í•—æåº1-f=—ÆzñÁâСCÛåˆ OKÅ(å |òÉ')”îˆl]wÝuWö8òÈ#SdòÚ{ï½SLžçA üÔ^‰þ´\ÖÞ±…í‘1­µitñ‹_4 Î+SFà×-·ÜRXÕäùúë¯Ï2E¶¡(+¯¼röˆ×ûî»oæ|-Íô"p­y`JãºÏ<óÌ&Áy·E¸ýë_Ù£ñúÂëú;묳ÊÂ1m=G¦¹%–X"…{ýÅ5Ú|ªñæÇGÍ•W^Ù¡,‰«®ºjÉñ9âˆ#²©Î#`±­òÄO´ ™KF@Õí·ßžZ5¯?²<Æõú£ý( |Ì{6½öõ~Ý|+·Ù;ãÚ‰G”˜Ê9Þ/º*x»r-ÏGÍ¥²ç•óÅ‹žhýSO=•e£kíÜïÛqÙSAš3ÏÖ @€ @ 'ª"@/R˜ï¸ãŽ=á㜠@€Ô°@´–¥¨#Ý8p`6lX>|x5½Á9·}ÿ+šOÃ["𢣙>¢®8®T)d©Ä´YíâܑѨ½ò§?ý©I&›ÈlÒ8h$¦¿À‹Ã?\€h+˜ñS÷)-âç ÔÏUË=Û^üo¼ñÆY Pd­–)m ½Šl=1ýv{%¦ÞÛ`ƒ ÚÛ­Íí1]ú¹çž[rŸÈÚ¶À ”Ü+#ˆ¦­ØZ+í3Ä{Kdë,¼ŸÎ3Ï<ÙTÅÑß,n8Ó"¶,TÎô¿h™ºZ+Lwï½÷vÙÏîo~ó›ìú,u¾ éã÷|´)®çrK¼·<ÿüóiÒ¤ImN{üGõÿÚ»p9ªòà‡â#ò€té½…’ˆ( „" UQDZ EE €JQz&Ex(ÒQD¥Dz/ŠRŸ„^B Páï;¿ÿ¬³÷n»ÉÞäž{?çy–™9ó9Éì%û½ï9°å¿F »Y;âˆ#šmJßýîw›n«nøú׿^\ß„ ª«Ôòi§V„ˆT§@g"\Õ»Ñ"¬?ÏF°3¦1-ÿÎwãØCéq¿|óÍ7ë.9>#ÚÝkëÞ0_Dx0~Q$¦ûŽjzŽ¿oåçF„õ#÷÷ø™²“вÝîþ˜1cŠðvÏ{aLżí¶ÛAä˜xÉ%—ìö© @€]hüCWí  @€¼Qµ''[o½uÈP¡lêõ1Ç“öÚk¯¦ˆ/þóŸ7Ý^Ýð¥/}©¨ä_öEÐ-BΈÐ^TCyûí·‹Æµ×^[}[W–ÿô§?Õg­µÖª{ÝèET©¶¨:U¶ø25ª6F¨0·@Ty žóˆéð¶ÜrËbŲ́$rl1oÜcÚµ…^8ÅÔ¬“Ûâ>Ð,Ù.pnVõ0ÍZ«{DT™ŒJP=ƒ:qŸ¼á†zò¦›nêµ®ºbĈÕ— —Yd‘´ì²Ë‘†;üweŒM·ÂµHŠÐ_T|ñÅ‹û}VâÚ£/sÌ1G³n´\¿êª«¦xtÒ6ÜpÖ»•Õx{îôÄO¤Ë/¿¼çêâuT@‹P¬F •@Ü¿âçÙ¨ÂaTmòzNo÷ÏV¡ãÉ?cwŽ0ûì³ÁÌgÆôó4ŒoÜ£ÅÏÃ|pñ9ÏSªEEÕ„ÇÏâÕ÷îã?>-ºè¢ÕÕ–  @€ 0 ôä°è @€Q ‚15cTÀØ|óÍÓL3Í4»9äúa–Q¼ñÆ § <òÈ#STаG»!•x¼ûî»Å£çþñk„5ºÝ"DW µD…¾vS>öØc)e‹I5ˆqÁ¤ûî»/ű¾úÕ¯–»y&Ðo\Žêh;î¸cQ²?*Mö[ç8¦Ò‹k)+g6Ø¥XŸ guVŠjª“ÛÞÊc¶ èEÅÃW”AŠò}åóóÏ?_.özn ŒÏ½ãuÚþò—¿´ÜµÓ°[„”£¢S³v÷Ýw7Û4IëÃ.S«Í5×\Å”¢ñYÖ¨5ƒ /¼°éŸÑ˜V#ÐHàãÿxÈ‹`„H[ݽߺæqߊ ŸÕA²þøù±zŽn/ÇŸ‰­¶Úª¨H?_VïMñ ñ‹J+­´R·OÛëx ŒŸc{†óŽ>úèôÃþ°×þV @€ @`  è Ô‘Ñ/ @€Œ@T‚úÆ7¾Q<_|ñÓ/ù?˜¢´œªø–[n)*Tm⋽=öØ#õ¬PWݧ\ŽJ!Ñ"ø_¦>úè£Åë¨_R®¸âŠuSÈ»ðŸ|°î‹Ï¨0Õ›Zµj8/ö[o½õŠ0^,¿÷Þ{iÿý÷Å"œ8lذbÙô‡À§?ýé´ÓN;a¨Ø5XZ|ñßjªÖò:ãþP Ç–ë'åù‘Giú¶bÅ=.‚Qá3ž# BúG€ @€Àˆéœ¾øÅ/¦}÷Ý7­²Ê*Sôܹž,Âej´ršØ˜Öö÷¿ÿ}ŠMÙ¢ÒÆ†n˜šU!*÷‹ç˜Ò²üâ1¾”ìïöÐCÕ¢“j\K/½tŒºýöÛ‹÷Æ›/¾øbŠé%üñ¢_T \xá…ëŽíɈi¿óï¤ÝvÛ- Öê\üŠ SíZLáAŒgœ±Ý®oïªpqÏ[~ùå‹û_Ç™ÄcZàýë_“øîú·µ —µª6W¤©û*ª¸^uÕUéÔSOMo7er³Þ6 èE»Y›0aB³MÖ9çœ3í¹çži×]wMV®,ýz‰'œpBQÑ´z’Ÿüä'Õ—Y.ÇýgÙe—­ è½ð éÝwßíÓè“rñîcŽ× @€ ›€€^n#¦¿ @€ô‹ÀtÓMWLaºß~û¥å–[®_Î1_’Eèëüc1_|9ÕB"A°¨â¶ýöÛ•µÂtJ¶˜ê1ÂiÑ¢Ýç?ÿù" ñþûïëbªÊcŽ9&Å·k˜{ýõ×SD>ùÉO¶Û}²·Ç—že[f™eÒºë®[¾lúá Ë.»,í°ÃŘÄXœyæ™ÅþqŒsÏ=7ÅÔ£nD¸#¦ŠŽ`^»é—»q¾©yŒ Æß§v-B¿«­¶Z»Ýú´½]¸-¦ÚnÖâ>c•3ã9¦ˆ>|xŠûÁôÓO™Œ°F»ÈNÂq­Z7ƒ‘­Î39Ûî¼ó΢¢éßÿþ÷¦‡‰±Yk­µÒú믟N9å”4®IE¼fcøüóÏ7=ö³Ï>Ût› ƒW`þùç/¦ÿüÖ·¾ÕÕñàëìÊâçÌV?ÛÆÏÈ=§·]sÍ5SLÇ:ZT§®¶ø¨ì:à 3TWw}¹ü9¾z`Óª†e @€r˜2ÿ:—‹†~ @€ 0$¢bÞᇞ† 6$¯¿“‹ŽŠln‹ÐWL¡Ú³ÅºGy¤xDåº%—\2ýáH‹-¶XÏ]ûíõ<Þxã"”RÞbúÍ£>ºvÎC=´´Å—×­ZY‰o5ÖH„ëïö½ï}¯0{ë­·ÒlÐñêóÎ;o–Œ@a„:âËãåÄtjS¢ßýíâøS_`¦™f*‚{íµWŠåÁÞ~ó›ß¤‹.º¨íeÆ=æCi»__w˜e–YŠŠDÞ§ãܬ’Z£÷LéuQa±]ëtjÚ˜–³UkWa¯Õ{§Ä¶ø Üxã›VÌ‹_ˆÐë—¿üåZu³¨úÚ, ×ìžÞêÏŒ€Þ”ésŽø;±ÿþû§ï~÷»U 8=¸=yæ™gÒÏ~ö³â""@}á…¦6Ú¨a‡£z^üZmñÿƒ¥ÅÔöS£5š| N #ç$@€ @ ½<ÆI/  @€èørü–[nI#GŽì‡£ŽCÆt…1ÝoTõ©NË€Ïý·JÝg>ó™´âŠ+‘°<餓ҫ¯¾ZL±A³;<èo‘2TÕJÊÓŠwÞyÅÔ¯±îí·ßNûì³O±®Ü§Ñó7ÞX¬Žkœ-¾ôÜd“M&ùT#FŒHñÐtSàÛßþv:à€ÒÜsÏÝÍÃØcE%²¶´kQÉ,‚rL—ÝèX÷ßQuhÁL -´PÝ.Qù^ª[W¾ˆ{ðرcSLo=P[„&¢²]TUjÖ: èµ›Âv ôž{î¹´ÝvÛÕ}nV=F:è Ô—ÀK°µ¨\ÛìÏL„¾#ð·È"‹4z«uƒD ªºÅÏ6ñóÚ`¯p:%‡ì’K.I£F*~v,ÏûÍo~3=üðÃE…år]<ÇϾÇ{luUŠ_Š_˜,mši¦0—" 7`†BG @€èƒ@ÿ—èCgìJ€ @€))0„óš‹ßzë­Åôˆ¿üå/ëB;ï¼szâ‰'Ò•W^Y„wÂ1¦“=ðÀ‹/-#,-ö¹üòË›Ÿ Ë[Ê€^5T7óÌ3§Ã;¬îLçŸ~ºí¶ÛêÖU_D¦:Unu›eCI ¦ê*á¼?m³Í6©“ðXL“=©ÓGG% ˜ÎtõÕWO+¯¼r¯?NsÍ5W¯uÕ„è­Ý5D¥“Ön,–Xb‰N3Uö‰¿;¯¼òJÃsGˆüç?ÿyŸÂyq FÓ<Æúv•jO=õÔØ­m‹?›í¦Xn{;L«F¥6á¼îñß~ûíiË-·¬ çÅÑ_|ñÅ´Î:ë¤áV[TT­VÏ›gžyÒi§VÝÅrRX°‹—åP @€ r½A>À. @€“"ðç?ÿ9­·ÞzuSíÅôz§Ÿ~zñ˜sÎ96Â<Õ@\« \ÃLâÊ×Ü|óÍÅ»«½X±Ã;ô ÂÄÔ·ÕŠ€ÕÓ>øàƒi„ EuUéª2– ^¨f6f̘¶ø©O}ª&·Ý±ÉO>ùdŠªfÑMI[µ³Î:«Õæ±-*«¶j÷ÜsO«Íµmí‚|Õj©µ7 …20Þ¨;›o¾yš”pÉ{ï½×èpmzgžyfÓi“«üÇ?þÑ+ŒTÝn™ÀPxýõ×›^nüœ8|øð´÷Þ{§ò]|ñÅéä“O®í?/_pÁC&à^»p  @€ ÐR@@¯% @€z×_}ÚtÓMÓĉë.þì³ÏNQ=¯]‹/&ËÖnŠÂr¿É}Ž)#£rITnZvÙeëAˆN8¡.qï½÷¦fA—2XáêµÔÔ @’£¢Y»SêÅÔ¶}™–´ç1ï»ï¾ÚªE]´¶\.¬¶ÚjåbÃç=ßu×] · ”•í*Ó^}õÕuõÙgŸm¹_'½váÚmoÙ&#HŸ1ÍÚ¤~®Lj@/*ùwÜqͺS[¯ÚWÂâ—T–_~ù¦à;ꨣRܳ·Új«ZõÉ}ìc颋.Jk¯½vÓ÷Ú@€ @€CSàßš ÍëwÕ @€ PˆÛöÛoß«ÚΨQ£Šõ•]›.^wÝuµm“: dí.TCu*­²Ê*½ú¿ÿþû×MGVžêÆo,{Vâ+·{&@`ðÄt…Ûn»mÓŠšÕ+*{­Õ}›-G˜¸lzêˆ `«Ó.¶«.×êýý½-¦=ot.ÏÁ»ªC¹¾çó5×\ÓsUíõlæŸþÚëf ï¾ûn³MÅú¨¾úïÿ»å>}Ý[3¡Úo~ó›–U›U}ýìg?Û6LÓ2· éEˆ=ÿ'A쯰 “Ì7ß|E5ç-¶Ø¢ã÷Ø‘ @€†Ž€€ÞÐkWJ€ @€¶ûì³Ozþùçëö[h¡…ÒñÇ_·®Ù‹Då²­ºêªåb¿>—½V¡ºÃ?<Í<ó̵~¼üòË)7ÕˆfSåV÷³L€@þñ÷}›m¶I/½ôRÛ‹‰Ô~ô£¶ûµÛ¡Z=n±Åëµû'>ñ‰¢S¯ •O=õTZguÒc=VYÛx1®ñ·¿ým:餓ïðÿ×¶ ²½óÎ;-ßÛsãÒK/6ÜpÞ«ë^_zé¥u¯{¾xøá‡ÓO<Ñsuíõ¾ûî[[nµÐêºÊ÷õ¬[®ŸÔç™fš©e@1ªÔ>óÌ3µÃÿóŸÿL?þñÓ×¾öµÚºF ÍúÞ[o½u£·Ô­ûÁ~vÙe—"taüø üÃþV_}õ´Ç{ÔíÛèE'–Þg\^xáUKãs¢U›e–YÒOúÓ÷­)õ‹)­úc @€ L½9.zE€ @€).ð׿þ5~úé½Î¡”üã½ÖWWDµ À}á _Hå´¶1åW«À\õý“³ÊPÝZk­ÕôPóÌ3O:à€ê¶Ghå‘G©­{àÒ„ Òì³ÏžFŒQ[oÁ'pðÁ§²bf««‹) Ï9çœ4ÝtÓµÚ­å¶±cÇ•úþö·¿Õö‹{R£öÃþ°í¹zè¡"×ð /ô:Ìk¯½–Ž=öØ´ÄK¤/}éKi·ÝvK·ß~{¯ýÊqßkÖb*Ǿ¶Ñ£G· ©ÅÔ­ìO<ñĦ§Üd“M:þl7n\Ó㔞|òÉr±+Ï3Ì0CËê~x\qÅÓŽ;îX\G|Þüìg?k{îÔ5k6Ÿ~úé›m®­ÿÕ¯~ULa?묳¦¹çž»RÞzë­µí­Zýiõ>Ûä,÷ÿóÏ??ÅχgŸ}vÚ}÷Ý‹€t„#ôzæ™g¦¸Ïxà)þ^i @€ @ ™@û¹iöNë  @€ @`P œ{î¹éÃ?¬»¦˜j±Qå¨ä¡n¸¡¨À•ó¢"OÙV[mµâ‹ÌVÓ–ûNîsL•¡À9眳íô“{î¹g:ãŒ3Òã?^œö_ÿúWŠuQE(ZY‰oÍ5×l.)vö²ˆ{×!‡ÒQÿã~·óÎ;w´oÏ¢:Úøñãë*¦•û4 >ò“Ÿ,ÂQU­U‹cGÕ¦¸ŽxOÜ¿ã{VBàH³éycÿjp°ç9ïºë®â˜èê´EU¶¨ÈÁíF-î½1 d„—Zj©º]"èrÚi§Õ­+_DÕÁø¬ê¤ÝsÏ=©Ùt²Õ÷ÇÔ®'Ÿ|rŠ`]·Z#c*ßfí•W^)BŸÍ¶7Z•¼b\cÍž-οø|›ÔŸ…­ªåF 2*2N;­ß÷žTcïËW î±ñˆ¿g @€˜½IQó @€ 2øÒý‚ .èuUï¿ÿ~Úl³ÍRT©‹^TRŠÇ{ï½×kßX!ŒD¨oJ}‰ß—P];ÊvÝu×¥«®ºª¨*TVtš•ÿÊó{&@`Ê l¿ýöEبÓ3ÿå/ét׎÷kЋì·ß~é¾ûîKW\qEÛãEÓ{ï½·å~»îºkÝßqܨœAë˜r5îõÍZT|‹]¯]tÑ¢¢j|.ÄëV-‚ƒQ™õ÷¿ÿ}ÃÝ¢Êß²Ë.›b*ô¸ç¾ýöÛé–[nIwß}wÃýãÜq¯žm¶Ùn áN;íT"#„Çï¤EU¬ýÅçWL1Ç?úè£Ó’K.ÙÉÛîdËϦ†;ôXÓâF%®ïÿû½Â•å®Ï=÷\Zn¹åÒW¾ò•S.ǵV[¯¹æš¦ï¯î[]Ž aguVŠ`z«€^ŒM”bÜç˜cŽôío;-¸à‚ÕCY&@€ @€h"  ×Æj @€ %Dô¬¸T^«éï>ò‘SõÅöñå~ß:™f¯9½¨¸xÊ)§¤˜Š¸][f™eRŒÇ°aÃÒvÛm×r÷˜f6‚|@ìЛþù‹PàÚk¯"Ì×I‹€Ý•W^Y|Ž?üðÃmß•Ëj‹m´‘€^[1;Ü=+o÷ÇÕ¶ ÷Çù“ @€ý%  ×_²ŽK€ @€ŒÆŽÛ°·0øèG?Zl‹ÀHT|še–YRL—Ïèæ´€ ;Ñbeîˆ]úª‹*z1õaL³í‰'žH;ì°CQ%pöÙgO#FŒ(ÖûúK U½8gÜs<òÈ´ùæ›§ƒ>¸. ×®OqްWņÞn÷>oŸgžy:zÏtÓM—~ñ‹_ÓÙþä'?éSU¹8ÁÈ‘#Óþûï_„ª;:áÙ)®ûâ‹/.>Wî¼óΦ½ŠJ{1-mTЋϣ¨fÛIkæ¡Âx í½÷Þé׿þu¯ië«ÇÛË.»,•S—Ÿ‡Õ}š-Gø2BšCG`ši¦éu±}¹oôzs‡+ô:„² @€^@@oÀ‘ @€ @ ÿž}öÙ^'Y`RL‹8[L•"(ЗÊÒK/]T”:æ˜cj—Õª¢E%ÀF_BÖv´@€@ö÷߀‹ U„â¹|ÄëÇ•¯ã9^—¸?”÷ˆ¨VÅ#BÃås,Ç#¦ mô¼Øb‹uäSÀFÅÏßpà EÈ-BÕ¯¾új1kô7ŽS´Æ#¦J¡1ui³vøá‡§8 Å´ß=Q5®/‚ÕG„$âu\K_«Ë­±Æ)ªþãÿ(ÂÑcÆŒIO?ýtq 1mzô!ÂÑq/_d‘ERTDýܧ½ëé´-´ÐBéïÿ{ŠþW¯)^ǣ篸†¸–›x.¯­|Ž>Ln‹Ï™Ûn»­¨xwÓM7¥Gy$7.Í8ãŒÅTÁñùÓÁ—-úUüâÏOßËGŒc¬‹©†'NœX<7 èűæœsÎbÊÚÑ£G¼˜Â8*ê½üòË…Aë#¸çž{NåùWXa…Â/ìâœqþxŽ?÷1ýpLm_ZEpßô¶¥œgCC î]=[L%Þê~ÔsÿIy=~üø^o+?ƒ{m°‚ @€X@@oŽ® @€ @`J 4šoÅWœR§Ÿäó”ÓÛFø£¯_ÖE5§˜âñ¥—^ª;„B4·@L+Ú÷xDˆ©¿[ÀÛe—]&ûTÒj×"˜Øí ©áÝvÛ­Ýéû¼=‚eŒk9^ˆëï.¢Ša<:iÍB›1Qµ6¶QÆT½ñè´5ó++évzû 0ø¢2kÏSŸG(¼¿ZL]SzW[Týœm¶Ùª«, @€ @ þÿ×Ã,t’ @€C[ Q¸$ªÿ äÎ?ÿü¢‹+¯¼rŸ»ÓKvØa½Þ' ×‹Ä  @€!,°õÖ[÷ºúã?>Eˆ®¿Ú!‡RTð¬ßÏéU Ë @€ä$  —Óhé+ @€úI Q/¦Éˆí7ÞHwÞygÚl³ÍÒC=Ttñ’K.I1eeLOØ—¶ãŽ;¦•VZ©ö–˜bqĈµ× @€ u‘#G¦¨X]mÏ<óLÚh£Š©·«ë»±|Î9礣Ž:ªîPñ E1=¸F€ @€ôr5}&@€ @€@—Mk÷ÁLöYbúØ·ß~{²˜8qbŠ)cŠ­Ï~ö³éšk®©7Ây1%ïL3ÍÔë˼ÚN bªÃN8¡6=îšk®Y[n°»U @€†¤@T®žwÞyë®ý–[n)~¿ð S7þßaܸq)¦r5jTÝyâÅᇞTÐëÅb @€™LŸI?u“ @€úQ`ذa½ŽþôÓO÷Z×銨d·ýöÛ§ë®».­ºêªuaºNÑs¿7ß|3½ð i†fH1=íG?úÑ4ýôÓ§˜ê6ÎSl½ûî»Åúžïmõ:Â~‡zh:è ƒÒ[lÑjWÛ @€ I\°¨b?ãßtÓM5ƒG}4m³Í6i¿ýö+*S/·Üriøðáiã7nùsù‡~˜n¾ùæôøã§8F„ýî¸ãŽë«mŽ9æHgŸ}vÚtÓM««- @€ @ +½¬†Kg  @€ Ð?sÍ5WŠ/Ó~øáÚ |ðÁÚr_¢ÒÝW¾ò•tíµ×o›yæ™ûòö¦ûÎ=÷Ü]«Æ×ó$ûî»oЇF€ @€@céÝpà éÆoLgœqFºâŠ+Š_‰½£ú]<.¹ä’âÍGqDÚ{ï½è¿kãÿ5ÖZk­¦ÛW[mµ¢šÞ–[n™>ö±5ÝÏ @€ä  —Ã(é# @€¦€ÀqÇ—Ö_ýÚ™n»í¶ôÀ¤VX¡¶®ÝBTÝ‹ªcÆŒ)v]|ñÅÓÑGÝîm¶ @€ À4ÓL“ÖYgâñú믧ßýîwiìØ±éêŸS9IDAT¹çžKÏ>ûlQñ:¦»]j©¥Z^Mlßd“MŠ÷Æ/ -°À)€ñXsÍ5‹_jy  @€ ‘€€^Fƒ¥« @€èOõÖ[/m¶ÙfÅ—låy~üã•1b*ÙVí½÷Þ+‚x1Uì;ï¼Sìºüò˧?þñiÞyçmõVÛ @€ÈP`¶ÙfK;ì°Ã$õ|ÆgLW]uÕ$½×› @€ ›À´¹uX  @€ @ ÿŽ=öØ,ÊvõÕW•-ÞxãrUíù?ÿùOºé¦›Ò{ì‘¢RÞP„ó¦›nºô£ý(Ý}÷ÝÂy5-  @€ @€ @€ÀPh]a(Џf @€ aÚ=øàƒi»í¶K×_}!qÝu×¥9æ˜#-³Ì2iâĉiÑE-¦®zæ™gÒ›o¾Y§µÊ*«¤O<1­´ÒJuë½ @€ @€ @€ E½¡8ê®™ @€-bJÚ˜šöˆ#ŽHÇw\?~|ŠjyûÛߊw=ùä“uï^l±ÅÒV[m•¾öµ¯¥áÇ×mó‚ @€ @€ 0”ô†òè»v @€4˜vÚiÓèÑ£‹ÇóÏ?_TÕ‹Êz¯½öZZ`Ò /œYd‘â1Ë,³49ŠÕ @€ @€ @€¡-  7´ÇßÕ @€ @ ­À|óÍ—â±á†¶Ý× @€ @€ @€ÿ˜ö‹– @€ @€ @€ @€ @€n èuKÒq @€ @€ @€ @€ @€@E@@¯‚a‘ @€ @€ @€ @€tK@@¯[’ŽC€ @€ @€ @€ @€*z ‹ @€ @€ @€ @€ @ [zÝ’t @€ @€ @€ @€ PЫ`X$@€ @€ @€ @€ @€ÝÐë–¤ã @€ @€ @€ @€ @€Š€€^Ã" @€ @€ @€ @€è–€€^·$‡ @€ @€ @€ @€Tô*  @€ @€ @€ @€ @€@·ôº%é8 @€ @€ @€ @€ @ "  WÁ°H€ @€ @€ @€ @€º%  ×-IÇ!@€ @€ @€ @€ @€½ †E @€ @€ @€ @€ Ð-½nI: @€ @€ @€ @€¨èU0, @€ @€ @€ @€ @€n èuKÒq @€ @€ @€ @€ @€@E@@¯‚a‘ @€ @€ @€ @€tK@@¯[’ŽC€ @€ @€ @€ @€*z ‹ @€ @€ @€ @€ @ [zÝ’t @€ @€ @€ @€ PЫ`X$@€ @€ @€ @€ @€ÝÐë–¤ã @€ @€ @€ @€ @€Š€€^Ã" @€ @€ @€ @€è–€€^·$‡ @€ @€ @€ @€Tô*  @€ @€ @€ @€ @€@·ôº%é8 @€ @€ @€ @€ @ "  WÁ°H€ @€ @€ @€ @€º%  ×-IÇ!@€ @€ @€ @€ @€½ †E @€ @€ @€ @€ Ð-½nI: @€ @€ @€ @€¨èU0, @€ @€ @€ @€ @€n èuKÒq @€ @€ @€ @€ @€@E@@¯‚a‘ @€ @€ @€ @€tK@@¯[’ŽC€ @€ @€ @€ @€*z ‹ @€ @€ @€ @€ @ [zÝ’t @€ @€ @€ @€ PЫ`X$@€ @€ @€ @€ @€Ý˜¾[r @€èLàÃ?L=öXg;Û‹® ¼úê«]?¦vO`üøñî‘Ýãt$Y L˜0!ëþöοøâ‹î׃}]߀xúé§lßtŒ @€Þz½M¬!@€ @€@¿ |ðÁi饗î×s88r8òÈ#S<4Ø{íµWЇF€ @€´0Åmk[  @€ @€ @€ @€ @€À$ ¨ 7IlÞD€ @€¾ l²É&i™e–éû½ƒ~˜o¾ùúíØܹÀB -”FÕùìI€À˜uÖY‡Ü5Ä 6l˜ûõ@}Ò#GŽÒ×ïâ  @€ä 0͇ÿm9tT  @€ @€ @€ @€ @€@N¦¸Íi´ô• @€ @€ @€ @€²ÐËf¨t” @€ @€ @€ @€rÐËi´ô• @€ @€ @€ @€²ÐËf¨t” @€ @€ @€ @€rÐËi´ô• @€ @€ @€ @€²ÐËf¨t” @€ @€ @€ @€rÐËi´ô• @€ @€ @€ @€²ÐËf¨t” @€ @€ @€ @€rÐËi´ô• @€ @€ @€ @€²ÐËf¨t” @€ @€ @€ @€rÐËi´ô• @€ @€ @€ @€²ÐËf¨t” @€ @€ @€ @€rÐËi´ô• @€ @€ @€ @€²ÐËf¨t” @€ @€ @€ @€rÐËi´ô• @€ @€ @€ @€²ÐËf¨t” @€ @€ @€ @€rÐËi´ô• @€ @€ @€ @€²ÐËf¨t” @€ @€ @€ @€rÐËi´ô• @€ @€ @€ @€²ÐËf¨t” @€ @€ @€ @€rÐËi´ô• @€ @€ @€ @€²ÐËf¨t” @€ @€ @€ @€rÐËi´ô• @€ @€ @€ @€²ÐËf¨t” @€ @€ @€ @€rÐËi´ô• @€ @€ @€ @€²ÐËf¨t” @€ @€ @€ @€rÐËi´ô• @€ @€ @€ @€²ÐËf¨t” @€ @€ @€ @€rÐËi´ô• @€ @€ @€ @€²ÐËf¨t” @€ @€ @€ @€rÐËi´ô• @€ @€ @€ @€²ÐËf¨t” @€ @€ @€ @€rÐËi´ô• @€ @€ @€ @€²ÐËf¨t” @€ @€ @€ @€rÐËi´ô• @€ @€ @€ @€²ÐËf¨t” @€ @€ @€ @€rÐËi´ô• @€ @€ @€ @€²ÐËf¨t” @€ @€ @€ @€rÐËi´ô• @€ @€ @€ @€²ÐËf¨t” @€ @€ @€ @€rÐËi´ô• @€ @€ @€ @€²ÐËf¨t” @€ @€ @€ @€rÐËi´ô• @€ @€ @€ @€²ÐËf¨t” @€ @€ @€ @€rÐËi´ô• @€ @€ @€ @€²ÐËf¨t” @€ @€ @€ @€rÐËi´ô• @€ @€ @€ @€²ÐËf¨t” @€ @€ @€ @€rÐËi´ô• @€ @€ @€ @€²ÐËf¨t” @€ @€ @€ @€rÐËi´ô• @€ @€ @€ @€²ÐËf¨t” @€ @€ @€ @€rÐËi´ô• @€ @€ @€ @€²ÐËf¨t” @€ @€ @€ @€rÐËi´ô• @€ @€ @€ @€²ÐËf¨t” @€ @€ @€ @€rÐËi´ô• @€ @€ @€ @€²ÐËf¨t” @€ @€ @€ @€rÐËi´ô• @€ @€ @€ @€²ÐËf¨t” @€ @€ @€ @€rÐËi´ô• @€ @€ @€ @€²ÐËf¨t” @€ @€ @€ @€rÐËi´ô• @€ @€ @€ @€²ÐËf¨t” @€ @€ @€ @€rÐËi´ô• @€ @€ @€ @€²ÐËf¨t” @€ @€ @€ @€rÐËi´ô• @€ @€ @€ @€²ÐËf¨t” @€ @€ @€ @€rÐËi´ô• @€ @€ @€ @€²ÐËf¨t” @€ @€ @€ @€rÐËi´ô• @€ @€ @€ @€²ÐËf¨t” @€ @€ @€ @€rÐËi´ô• @€ @€ @€ @€²ÐËf¨t” @€ @€ @€ @€rÐËi´ô• @€ @€ @€ @€²ÐËf¨t” @€ @€ @€ @€rÐËi´ô• @€ @€ @€ @€²ÐËf¨t” @€ @€ @€ @€rÐËi´ô• @€ @€ @€ @€²ÐËf¨t” @€ @€ @€ @€rÐËi´ô• @€ @€ @€ @€²øñk+üŸ·³%IEND®B`‚upstream/docs/design/images/bluestein_fig2.png0000775000175000017500000012170314637212246020451 0ustar kaolkaol‰PNG  IHDR WV?†9&iCCPkCGColorSpaceAdobeRGB1998(‘c``RH,(Èa``ÈÍ+) rwRˆˆŒR`ÎÀÊ Á ÀÀÈ š˜\\ààÃ0|»T—uAfaÊã\)©ÅÉ@úg'•000fÙÊå% v-’” f/±‹€²·€Øéö °ûXMH3ýÈæK³™@vñ¥CØ 6Ô^tLÉOJUù^ÃÐÒÒB“D?%©% Ú9¿ ²(3=£DÁR© žyÉz: FFÆ  p‡¨þOF±31@ˆÍ‘``ð_ÊÀÀò!fÒËÀ°@‡*BLÍA@ŸaßœäÒ¢2¨1ŒL@; ññ•J?íCô¤8eXIfMM*‡i  W V‹qGU@IDATxìÝ ¼}c½?ðç*Ê’›Y’©¨nÉPI†R %u$**•Bƒ&) Ý+i4”KH2’!C !~ʘ®¿Ïêî_çwìéœ=œuöy?¯×~söZëYÏz¯}öš¾Ï÷ù·G+E!@€Œ Àþð‡rýõ×—Y³fU¯¿þõ¯ežyæ) -´PYxá…«ŸÏ~ö³«Ÿ#¸ù6‰ @€Cpý54j+"@€ @€’€{^C‚¶5ø7ÁÕ5Ü+šD€ @€ @€ @€ @€ @€ÀÐæú­ @€ @€ @€ @€ @€5\]âI @€ @€ @€ @€ @€ _@põðÍ­‘ @€ @€ @€ @€ @€ ®®áNÑ$ @€ @€ @€ @€ @€†/ ¸zøæÖH€ @€ @€ @€ @€ @€@ W×p§h @€ @€ @€ @€ @€Ã\=|sk$@€ @€ @€ @€ @€ @ †‚«k¸S4‰ @€ @€ @€ @€ @€á ®¾¹5 @€ @€ @€ @€ @€ PCÁÕ5Ü)šD€ @€ @€ @€ @€ @€ÀðWßÜ  @€ @€ @€ @€ @€¨¡€àêîM"@€ @€ @€ @€ @€ @`ø‚«‡on @€ @€ @€ @€ @€ÔP@pu wŠ& @€ @€ @€ @€ @€ 0|ÁÕÃ7·F @€ @€ @€ @€ @€j( ¸º†;E“ @€ @€ @€ @€ @€¾€àêá›[# @€ @€ @€ @€ @€5\]âI @€ @€ @€ @€ @€ _@põðÍ­‘ @€ @€ @€ @€ @€ ®®áNÑ$ @€ @€ @€ @€ @€†/ ¸zøæÖH€ @€ @€ @€ @€ @€@ W×p§h @€ @€ @€ @€ @€Ã\=|sk$@€ @€ @€ @€ @€ @ †‚«k¸S4‰ @€ @€ @€ @€ @€á ®¾¹5 @€ @€ @€ @€ @€ PCÁÕ5Ü)šD€ @€ @€ @€ @€ @€ÀðWßÜ  @€ @€ @€ @€ @€¨¡€àêîM"@€ @€ @€ @€ @€ @`ø‚«‡on @€ @€ @€ @€ @€ÔP@pu wŠ& @€ @€ @€ @€ @€ 0|ÁÕÃ7·F @€ @€ @€ @€ @€j( ¸º†;E“ @€ @€ @€ @€ @€¾€àêá›[# @€ @€ @€ @€ @€5\]âI @€ @€ @€ @€ @€ _@põðÍ­‘ @€ @€ @€ @€ @€ ®®áNÑ$ @€ @€ @€ @€ @€†/ ¸zøæÖH€ @€ @€ @€ @€ @€@ W×p§h @€ @€ @€ @€ @€Ã\=|sk$@€ @€ @€ @€ @€ @ †‚«k¸S4‰ @€ @€ @€ @€ @€á ®¾¹5 @€ @€ @€ @€ @€ PCÁÕ5Ü)šD€ @€ @€ @€ @€ @€ÀðWßÜ  @€ @€ @€ @€ @€¨¡€àêîM"@€ @€ @€ @€ @€ @`ø‚«‡on @€ @€ @€ @€ @€ÔP@pu wŠ& @€ @€ @€ @€ @€ 0|ÁÕÃ7·F @€ @€ @€ @€ @€j( ¸º†;E“ @€ @€ @€ @€ @€¾€àêá›[# @€ @€ @€ @€ @€5\]âI @€ @€ @€ @€ @€ _@põðÍ­‘ @€ @€ @€ @€ @€ ®®áNÑ$ @€ @€ @€ @€ @€†/ ¸zøæÖH€ @€ @€ @€ @€ @€@ W×p§h @€ @€ @€ @€ @€Ã\=|sk$@€ @€ @€ @€ @€ @ †‚«k¸S4‰ @€ @€ @€ @€ @€á ®¾¹5 @€ @€ @€ @€ @€ PCÁÕ5Ü)šD€ @€ @€ @€ @€ @€ÀðWßÜ  @€ @€ @€ @€ @€¨¡€àêîM"@€ @€ @€ @€ @€ @`ø‚«‡on @€ @€ @€ @€ @€ÔP@pu wŠ& @€ @€ @€ @€ @€ 0|ÁÕÃ7·F @€ @€ @€ @€ @€j( ¸º†;E“ @€ @€ @€ @€ @€¾€àêá›[# @€ @€ @€ @€ @€5\]âI @€ @€ @€ @€ @€ _@põðÍ­‘ @€ @€ @€ @€ @€ ®®áNÑ$ @€ @€ @€ @€ @€†/ ¸zøæÖH€ @€ @€ @€ @€ @€@ W×p§h @€ @€ @€ @€ @€Ã\=|sk$@€ @€ @€ @€ @€ @ †‚«k¸S4‰ @€ @€ @€ @€ @€á ®¾¹5 @€ @€ @€ @€ @€ PCÁÕ5Ü)šD€ @€ @€ @€ @€ @€ÀðWßÜ  @€ @€ @€ @€ @€¨¡€àêîM"@€ @€ @€ @€ @€ @`ø‚«‡on @€ @€ @€ @€ @€ÔP@pu wŠ& @€ @€ @€ @€ @€ 0|ÁÕÃ7·F @€ @€ @€ @€ @€j( ¸º†;E“ @€ @€ @€ @€ @€¾€àêá›[# @€ @€ @€ @€ @€5\]âI @€ @€ @€ @€ @€ _@põðÍ­‘ @€ @€ @€ @€ @€ ®®áNÑ$ @€ @€ @€ @€ @€†/ ¸zøæÖH€ @€ @€ @€ @€ @€@ W×p§h @€ @€ @€ @€ @€Ã\=|sk$@€ @€ @€ @€ @€ @ †O¬a›4‰ 0í¾÷½ï•‹/¾xÚo‡ 0J›o¾yùÿøQÚ$ÛRs¿þõ¯å€¨y+5é$°ÖZk•­¶Új:5Y[G@`ÿý÷/ûÛßF`KlÑøà?XZh¡ÑÙ [R{Üãʽ.…é-ðò—¿¼l¸á†Ó{#´~Z <úè£åCúдj³Æ˜ Ÿüä'ËŸ(\l&ìëºlãÏ~ö³rÚi§Õ¥9ÚA€ÀžûÜç–í·ß~ˆkìïªþí±ÚGû[¥Ú @€vÜqÇrÔQG @ F_ùÊWÊÎ;ï\£iʨ ÜvÛmeñÅõÍ´} Q`§v*GqÄ×hUJYl±ÅÊí·ßŽ‚ \ýõe™e–©Q‹4eÔ¾ùÍo–·¾õ­£¾™¶ÀÈ ì³Ï>åŸøÄÈo§ ¬@BQæšË`êõÙ#ZBàŸ÷ß™wÞyqš@:îä#Úú¬ˆú$ùÙ 'œPŸM°%Îd'fv @€ @€ @€ @€ @€FSÀ8£¹_m P#c=¶,¹ä’5j‘¦˜9§Ÿ~zÙwß}gÎÛÒÚ ¬·Þz%Ã-*˜¨À7ÞX¶Ûn»‰.f~}Xb‰%ÊqÇ×÷zUH€@wûí·_9õÔS»›Ù\(°çž{–Í6Ûl€kP5ý¸øâ‹Ën»íÖÏ*ÕE`R«®ºjùêW¿:©e-D€@ïï{ßûÊE]Ô{Ej УÀX^üâ÷X‹Å ¨³À¬Y³Fæ¾àê:Ò´ 5×\³,¿üò#±-6‚Àt¸ùæ›§[“µwD^xá²öÚkèÖÙ,)ðô§?}Õ«›@×O~ò“˺Ö2#þ ,ºè¢ý¯T&!°Â +8LÂÍ"¦JàᇞªU[/9žúÔ§:~Ì!âÃxÚÓž6ÜZ+¯¼²ãA o;ï¼sT6¥Ì52[bC @€ @€ @€ @€ @€ Ѓ€àêð,J€ @€ @€ @€ @€ @€Àè®}iK @€ @€ @€ @€ @€èA@pux%@€ @€ @€ @€ @€ @`tWξ´% @€ @€ @€ @€ @€ô ¸º<‹ @€ @€ @€ @€ @€ 0:‚«Gg_Ú @€ @€ @€ @€ @€z\ÝžE  @€ @€ @€ @€ @€ÁÕ£³/m  @€ @€ @€ @€ @€=®îÏ¢ @€ @€ @€ @€ @€ŒŽ€àêÑÙ—¶„ @€ @€ @€ @€ @€W÷€gQ @€ @€ @€ @€ @€FG@põèìK[B€ @€ @€ @€ @€ @€@‚«{À³( @€ @€ @€ @€ @€£# ¸ztö¥-!@€ @€ @€ @€ @€ @ ÁÕ=àY” @€ @€ @€ @€ @€Ñ\=:ûÒ– @€ @€ @€ @€ @€ Ѓ€àêð,J€ @€ @€ @€ @€ @€Àè®}iK @€ @€ @€ @€ @€èA@pux%@€ @€ @€ @€ @€ @`tWξ´% @€ @€ @€ @€ @€ô ¸º<‹ @€ @€ @€ @€ @€ 0:‚«Gg_Ú @€ @€ @€ @€ @€z\ÝžE  @€ @€ @€ @€ @€ÁÕ£³/m  @€ @€ @€ @€ @€=®îÏ¢ @€ @€ @€ @€ @€ŒŽ€àêÑÙ—¶„ @€ @€ @€ @€ @€W÷€gQ @€ @€ @€ @€ @€FG@põèìK[B€ @€ @€ @€ @€ @€@‚«{À³( @€ @€ @€ @€ @€£# ¸ztö¥-!@€ @€ @€ @€ @€ @ ÁÕ=àY” @€ @€ @€ @€ @€Ñ\=:ûÒ– @€ @€ @€ @€ @€ Ѓ€àêð,J€ @€ @€ @€ @€ @€Àè®}iK @€ @€ @€ @€ @€èA@pux%@€ @€ @€ @€ @€ @`tWξ´% @€ @€ @€ @€ @€ô ¸º<‹ @€ @€ @€ @€ @€ 0:‚«Gg_Ú @€ @€ @€ @€ @€z\ÝžE  @€ @€ @€ @€ @€ÁÕ£³/m  @€ @€ @€ @€ @€=®îÏ¢ @€ @€ @€ @€ @€ŒŽ€àêÑÙ—¶„ @€ @€ @€ @€ @€W÷€gQ @€ @€ @€ @€ @€FG@põèìK[B€ @€ @€ @€ @€ @€@‚«{À³( @€À`}ôÑrá…–/ùËå®»îìÊÔN€Z\vÙeåàƒ.7ÝtS‹9¼=ÊþóŸËÑG]N?ýôQÞLÛF€ @€ @€ ðO$A€ @ N³fÍ*gŸ}v9餓ª×m·ÝV5ïÙÏ~vyÍk^3´¦Þyç娣Ž*×_}™k®¹Ê2Ë,SvÜqÇ²à‚ ­ VD€S#pß}÷•óÎ;¯:xâ‰åÆo¬ò„'<¡¼ûÝïžšFYëÐþ÷ÿ·üæ7¿)§œrJÉþOG¯¼·îºë– 6Ø`hí°" @€ @€ @€©\=5îÖJ€ ð˜Àu×]W®¾úêrÍ5×T?ùË_–+®¸¢ `šJ sÎ9§l½õÖ%™*Ç–/|á U Ýk¬1öm¿ @€À4x衇Êï~÷»rà 7”ßÿþ÷Õq)´—^ziyä‘G¦éViöD²ß³ÿ¯ìûóÏ?¿¤³—B€ @€ @€ 03WÏÌýn«  @€µxÞóžWØV§rï½÷–m·ÝöqÕiã-·ÜR¶Ûn»rÉ%—”yæ™§NÍÖ˜„À©§ž:ÔQ&ÑD‹ X`¥•VªÝ¹È€7Yõ @€ @€ @€Ww2™ @`f œuÖY妛nj¹ÑW^yeI†íµ×^»å<& @€ @€ _àá‡._|qõšwÞyËòË/_V_}õò”§û”C9¤<øàƒs´àˆ#Ž(;í´Óïuú£ßõuZŸé£#ð¿ÿû¿å7¿ùMP}â‰'– /¼°ä½u×]Wpõèìf[B€ @ 6®A·+D† ÎVÍ @€>ó™ÏÌž#¿vÞyçÙOÕ/ -´PÛU?òÈ#U^Û™L$@€i!°ÒJ+•ƒ:hv[O8ᄲÅ[ÌþÛ/£/ðéOzöFÞ~ûíå…/|aùãÿ8û=¿ @€Ô_ ö<}ýõ×÷¥±ý®¯/RI-~ÿûßWI#’8"¯K/½´œþùUçÍZ6X£ @€˜Ö®A†»ûW×{JÖfȲ‰³K?q3K @€^6ÝtÓ^«èËò/zÑ‹ÚÖ3×\s•µÖZ«í<& @€ÀôØd“M¦gõº/‹.ºhyþóŸ/¸º/š*!@`Pƒ¸×;ˆ:µýê%@€Àx/~ñ‹e÷Ýw¯2Ÿ6™¿û]ßdÚ`™é#»=ôÐôi°– @  A\ ¢Î.6Å, @`ä\ƒ w— ®®÷P×fȲî¹gbzü\Àdh¼§<å)ÝC™“ P``íÝW½êª«VÙŽŽ<òȦ ½ÿýï/K-µTÓiÞ$@€é-0Ï<ó”'=éIŽOïÝØSë]#÷Äga(0ˆ{½ƒ¨s€ª&@€Àÿûß«Ð?üð9ÞŸìý®o²í°/pçw–£Ž:ª¡!É_–Yf™²ãŽ;–\pü¬=ý=ˆëƒAÔÙÓFZ˜ 0)®¾å–[Êå—_^æž{î–¯'>ñŸÍüÇ?þQ2 {~Ž}}oìïÿöoÿV¤óÔ§>µzåY^yÔ‹!ËZïá™–ÿÚk¯-çž{nùÃþP²íyå÷üï%¨<ÉεØb‹U?—\rɲÎ:ë” 6Ø ,¼ð­!M!@€}È1©.åC)‹,²Hùüç?_òp-åÉO~rùð‡?\öÚk¯º4S; @`ù¾—yl°Ó¤Ê:L2Í$@`ƒ¸×;ˆ:‡@a¨îºë®²õÖ[—³Î:«/"ý®¯Ñ¨<ƒ9á„ÊyçW>ðTÏaÓü ÝvÛ­äó3kÖ¬rÚi§•{î¹g46ÌV PsÎ9§:æeäí±å _øB9餓Êk¬1öíIÿ>ˆëƒAÔ9é ´ × ÃÝ‘S\ý³Ÿý¬l¿ýöCÛêù矿¬¾úêÕ0¯ê5þ?ç9ÏÚú‡±"C–µWž)éñTýÉO~²{ì±Ug„V* øãÿ8Ç°Ç (K¯×\Œm´ÑFeóÍ7//~ñ‹[Uá} Ð{êR’¹ôSŸúTÙwß}ËUW]Užð„'TçŒNui§v @€@ÿfB‡ìþ«NöÿèìK[B`Tq¯wuŽŠ·í @ þyž‘ä0y2¶¬»îºå°Ã«²yþîw¿+ßøÆ7Ê¡‡Z’1³]º~×—6Ý}÷Ýåè£._þò—«„7y/ÉltØÄh•OúÓ³7èöÛo//|á çxÞ6{¢_ 0 {ï½·l»í¶e|`uªJ"µí¶Û®\rÉ%%Ï3z)ƒ¸>D½l£e  @€À¨¸îžœòàêánn)ûÛßJz÷åÕ(/}éKË{ßûÞ²ÕV[•é0cȲÆÙ?¯¿þú²ß~û•o}ë[mƒª;)%£ÂÅ_\½¾ô¥/•¿þõ¯U`Y§åL'@€£$›’«­¶Ú(m’m!@€ @` â^ï ꜔šH€À ddÎõ×_¿ÜxãslÕ¼óÎ[~üã—ù曯zå•W®F#Û{ォà³V÷vúYߣ>Z’Pêˆ#Ž(?üá˃>8G:ÇHþ‘Qb“Ø+û ú!nºé¦–U]yå•å—¿üeY{íµ[ÎÓn ®Qg»m0Ìd× ƒßûS\ý’—¼¤ê¹Þv—]vYuã!ÐÃ,çž{nÉëYÏzV9òÈ#«L½Ã\?Ö5¨!ËúѶºÕ1¬ôøS1Ü[nÜm¼ñÆåá‡nÊžŒ éLlíË,³L5TYþïòJúVeî¹ç®2Y·šÞîý©ph×Ó @€ @€ÔY`÷zQ§û~uþiÑÈwÎÞð†ÇVgK_ö²—ͬ»åOúÓK^ÍJ¿ë{Ç;ÞQ?üðf«ªÞËCoeôžò”§ŒþFÚB†&Q5Û•^âú`ufû]w´û˜F€3]À5È`?S\Ï]vÙeöVþö·¿-«¬²JIïV%Ëd8¯§=íiå©O}j5[†DÉkÖ¬YUà‹.º¨Ê¸›úyä‘VUÍñþÍ7ß\6Ùd“òÑ~´ì³Ï>“&£Ò!ü1ˆ!ˆÐì)[Å ÓãOÕpo¹¸JöõVÕÉæð…/|¡¬¾úêMí“¡!ÜO>ùäÇMÏÿäD‡Fž*‡Ç5Þ @€ @€¦‰À îõö»N÷ý¦É‡I3 Œ˜@²A';g³’äI-ý®ïÿøGÛ&®nË32Ÿô¤'̶ئ^`¡…jÛˆÄÁÜzë­mçi6±ß×YÇ êtÝÑlïyÌ)àdN~ÿ5åÁÕã7(Cu-·ÜråÚk¯?iößÿþïÿ^Ö]wÝÙ·ûå(?øÁÊ¡‡ZÎ9çœv³VÓÒëíãÿxùÍo~S¾ûÝïN8 ´ã úó™Ï,ó·œçõ³œvÚiå‚ .¨nÜeô“Ï2BC^éôÿþßÿëËêzè¡ræ™gVÇÊlÏG>ò‘I×›ãuœÒö¿·Ùf›9êºôÒKË)§œRò3CÆ®³Î:Uf¦9f÷ÇwÞYµíŒ3Ψê{õ«_=nŽRÕwÉ%—T#VÜvÛmÕ9w:]ehÜe—]vÚt,|܆yƒ®êô]ñ‡?ü¡ä;é†n(×_}IÛ–Xb‰êû(ÇŠ\ƒ,¾øâ]o[·3æÓÿüÏÿ”ë®»®äzzžyæ©F±zîsŸ[u¶^pÁ»­ªÖóÝqÇåÄO¬Ž]é@¾âŠ+ÎÑÞ +›{$yð•k¿t\_j©¥Êóž÷¼êؔыZ•ïs¿äꫯ®†¦Í1rþù篎'¹W“ãoFMêµdÿüèG?*¿úÕ¯ªð+­´Ò„ª¼ï¾ûª}ý“Ÿü¤¬µÖZåï|ç„–73¦B`÷zûU§û~Sñ‰°NÆ ´{Æ—sÔ‰–~××)[Ø3žñŒ‰6ÑüÓP`¢‰Š¦á&j2CxÑ‹^Ôvm¹ÿ’{)ýº>»Î~Öéºc¬¬ß  @€@g× z™£vÁÕÙ˜%—\²mpõD6xÞyç-ozÓ›ª×W\Q=L;÷Üs;Vñýï¿ì»ï¾U uÇ™§h†~Y6E›1å«ítë›NõpoŸÿüç«À€fmM@ÖþûïßlRÓ÷vØa‡²Á”VX¡äatÊòË/ßtÞñoNµÃøöø›†#®kÒAglçœfkOðóÛßþö²÷Þ{7›Üõ{dÇw¬®Z-´Øb‹•ƒ>¸¼öµ¯m5KÛ÷o¼ñÆòóŸÿ¼$(+¯¿þõ¯ÕüEe"ÁÕÉ\tùå——_üâÕ hûûßÿ^Õ•iàê<Là×ÑG=G»rîšaoÇ–,—àë³Ï>» žKÝ I ,°@\}Ùe—•]wݵږ±uŒý=Cä~å+_)¯ýëǾíw¦¹@¿+ò˜u2bN–´* z~÷»ß]ÕvÊÒÓªŽ±ï§w:œþ×ýWËÑ~2”j‚ƒ[uZ[_Ý~e:‰'pñ‰’Þ€yˆWÇÒï!Ëê¸ÃhS?Òã7‚™ZµwÐ7ÍŽ?þøV«®‚Êžð„'´œÞlB2ræzz™¦tº)بcªíð“†#ïýŒ²Ï>ût ªn´(Ç–dÐì%¸úÛßþv ÝèÔ¨{üÏdg~Ýë^W¶Øb‹*ð»Ýñ,AW H˃µdÞNpV²yN¦$`-aÉ虺ìÖÌnU_‚¿ÒÎSO=õq³äÜ8Ar©7Aqi[΄֮$“èî»ï^¾öµ¯Í°k5²Œ'€; 9ä’`k…é'Pç2ðÞ÷¾·m§˜±â?üpùÒ—¾TÌ›@ÝM7Ýtìä®Ïð¨x`u ß‹íJæÍÓ¡|ç;ß©²oçø•ckŽíž³MÝ?SÏ»Þõ®*+u££nŽS»ì²K9âˆ#:ò$;ø†nX]‹¦žVå{ßû^u<Ëñ2¯ß:]SŽ­+Ç׌‘cm–ÏßÓ10~ì6ù™-0ˆ{½ý¬³Ówô ïÎìO‡­'@ î­LT©ßõµ=m¾ùæëÛèjÝNó @€ÀôÈýúEY¤$ÙZ#iË“Ÿüäòá¸ìµ×^Ú¸~^4VÜï:]w4dý$@€ê PËàêœ ª$Õ'?ùÉjxáwÞ¹íj’õ(?u ®î÷em1Fxb?ÒãwÊ~=ÈáÞ’Ù+Cj7+¹™·òÊ+7›Ôñ½d‡kW·»)8¶¢©tÛ¿ @ ™ÀÅ_\€”`[¥w1m·Ýv%®-Ï~ö³'ºH5혗lÔ)É~¹Ç{TAÆ­–;å”SÊ{ÞóžV“'ô~2@'3j·%7D·Új«¦Õ©#Çád[=ñÄ»­²ÜrË-U9O˜Hùîw¿[’);Y±sÃV!0ŠùÙrË-˳žõ¬‘Û¼:~W¤óFô|ãßh›©ºÕÎH°lF×I§’tHIæþ·¾õ­å׿þõD›ó¾ùÍo.‚Å2Ùãg:¥¿ò•¯,ɸõÖ[OÈ1Â’=éU¯zUµ|£-c&3yF¿˜lIÐýg>ó™É.n9ÓZàØc­î÷¬¶ÚjÓz;4~NAÜëígîû͹¿üE`ªî¿ÿþjªÜ'ä3µ©ÞαëïÔÉ|ì¼ÝüÞïúÚíPºÙ#æ!@`X‡~x5ÚÔrË-7¬UZOÝíSŸúT•8ફ®*Iªöœç<§$îe¢¥Ÿ×u÷»N× Y? Vàæ›o®F—|ÛÛÞ6©ï“Á¶Ní¨ÀÄϸ†ÐöN'‚ý†ÍC¼ŽäaD»’‡±~²Y²ÚÕÝë´~YÖk{fòòí2aÆe7Î’a²UYz饫 ¬VÓÛ½¿ð Ϟœ¬ Ý”©tè¦}æ!@`f $‹ñ6ÛlSJñÙÏ~¶¬³Î:3¤‡­?ì°Ãª€©<ÈkVæšk®¶#„d4‘É”ì¿É–Ü0Îè%ƒìð4™¶e$•·¼å-%ÁÝ­J·œËçfæšk®Yò™ŸLùÓŸþT±'éB`’ñwÏ=÷,»îºkôûÔ§>u7³ã6 ë»"#´º´c#ÿo†;ï¼³ú®L–ânË¿øÅªcM²Q/éä“ïÉÅ_¼êPšz™ÆÏ; ÷rüÜl³Íª›ëwß}÷„)’í:AõÿøÇ'¼¬h/pÞyç•í·ß¾êì˜$é¡LAÜëígîûMÿϘ--tfËuM:xçXãBîÇŒré÷9{¿ëk7‚h§ïкí·t‚L'ÿ3Ï<³nÅWœ£‰%8×”õ-× ¹®^j©¥Jîù¥ƒåÜsÏ=ÇücÿÈuB:÷'qÃM7ÝTuþùK:­»îºe•UVéËg9Ƀ’páW¿úU5êÞJ+­4¶ψyå.ψ×Zk­òÎw¾³ã2f 0]r/:Ièþó?ÿ³ú¯Û=óéâ8ìv&Ⱥ×¶ý¼>hl¿ëìtÌdÜEc›ü$0r®“Q3zd:p$!ÍL*Iêòãÿ¸1ç¯ãï¡' UŽ—ù™Ø©ÄdànÊí·ß^ÕóÑo¼±J:³Â +”œS§sÌòË/_2jq/%Ïr¯¿þú’ ùœSçgÖ›gÊ9®ç»2ÉjÖ_ýÆ«õ² ã—u RŠkñŸŠzü]ËàêaÑdˆô“O>¹Ü{ï½mW™ƒIƒ«û=dY[Û ´ zôpoìÒ͘¬•“-K.¹duC,Ëwê!ÚXÇT:4Úà': \xá…Õú 6Ø 6m½õÖë´ˆéÿ'‡No|ãK³Ž=K,±D•Qú%/yI•ñø·¿ým¹à‚ J†„8œ‡$½–›–]vÙ2kÖ¬j‡)·+yè˜vçFA³’‘JŽ9æ˜êPqÆÍfëê½dzùË_^îºë®òõ¯½´»¹ØMsޝ»í¶[yÍk^S’6ŠíŽÿ¿ùÍoºjg»™Ž;í¶Û–W¿úÕíf3À´xઌðé,²Ë.»TÖ¹eº—WÔý»"™7ÜpÃ’ãF®i“q'ï¹Ø®üô§?­nDv3úA:Ó4 è}Ò“žTŽ:ê¨ÇÝýóŸÿ\>ñ‰O”¯~õ«íšP«iÉÚœ óŒÊ‘lì­:÷\ší‹¬÷ˆ#ލnø& ãË_þrÇ{3ãÛš¢H$˜##FLf4ñuú›ÀtÈùoÎ_óšÎz9¤“½2}ÆçöcKúY§û~ýØ#ê Ð?Œ¼š’é9d›ŒÄškùN Œú× 5Š@>O¹Ÿ”`â“N:©œþù%÷ÒRhÒ®Îý¾vÚ©äþj«’`Ž\“5 FÎõXÎYr Öª¤l®w2‘’LäÁíç?ÿyTÑ%÷ý:WÇàÊ+¯¬‚Æh“kЂ§äº«Ùö4ê÷“ÀtȽþÜÉÿe>ßøÀªNèÓq[´¹{~^4ÖÚï:]w4dý$0k®¹¦ ÷/xAõÜ|‹-¶(ýHx:œÖw¿–œÛæüð¿øE'˜Î‚Ζ™Ö®Îù_Ž‹G}ô•'yk§àêÔŸë²<Îh’­J«?ô¡•~ðƒ%Ï,&RÎ>ûìò¹Ï}®Ú†Æ5a»å³/_øÂV#|¦SUŠkRÞ»©Ó§²y[ftpu‚rvÜqÇrÐA5×ù¿wóåš/¾ô¬Sé÷euÚ¶éÖ–©î-[­Jz[%@lóÍ7o5KË÷ó ;¯‰”©t˜H;ÍK€œ~úéÕ+ÁÀyø”^©£x±ØÏ½ýö·¿½i`u‚¥@@¹FÉ{ye™øÃå€hL*Ï}îsgÿ>Ñ_žö´§Up ’n<,ÌÍ»\€&H²]I¦šVÁÕË,³LÉ+%ÁÑo¼q9õÔSÛU×rZ²Ùä•òž÷¼§¤ÃRzÛN¶äfb>§y¥¤ÎEY¤c ]|e8¯,·ÆkTA‰1ˆSnt*±ÊÿŨg¾êä`úh $(3Aµ_øÂªféÈ0ö»lºm}Ý¿+^ô¢UëÇ»î³Ï>åïxG•½lü´±ça§àêÓss|I¹û×^{íñ“ªLÉø—‡÷éDÔ¸©ú¸kôÆÖ[o=»5iwŽ[íJ2É%Iœó½ž— H€r®;•³|nÀNdùì³€6;–Œ½NÍñæ?þã?:5cŽé+¯¼rÉ+%$V_}õrÅWÌ1?̇z¨:¿Ë(ùÛc=ª¬‘3aÛGmq¯·Ÿuºï7jŸ8Û3jÉüæ7¿¹êØ–cA~ï5ZŒN8á„*Kr«6% 8ör.Ÿ€ˆt>L§ÚüÌý£Ü[ú]_êþÃþ0vsüžç8 Î=¬$|jüL'ß\5;_ž£‚>ÿ‘ÎY×]w]4œŽ— šnðœÕ'A@îó%›Z»’zrO)ÿûï¿5köIÎÙ»yætë­·VrÓ‰´Õ}¼Tú½ï}¯ Ég?¯%4ÂÛµ¯1-×*Å(EY>wsmÔXÞO£"ÿéÏþóÕHo}ë[Ëî»ï>û>ù¨l£íø—@?¯µö»N× Y? W ‰+¶Új«ênþõ¯o;Ép[7ñµ¥3`:ÝåWF.Ê}À\kýú׿îzñIç~b:a¶º˜à’N÷ÛÚ­0× ŸùÌgÚÍb%úÄùßËÿ}FÿTê'}uæ™gV# <ó™Ï,ùÈGºnd?¯+íw®;²~˜ªÊÐ}3Œÿ«ü¿¦×Ýt¾¯áå'a ä\`×]w­nDåaG†¸ÌHJ©2×$[e³’ãP§›¬ ðMt£Gp29O¦¤ÃÐØÀ®ñuäaT#[eJ˜È…m«‹äñëìæïNuåbýøã/›†¹I‘íLOådûiHØ®ÎÇc«Ç:¬¶ÚjÕ Üv ¹Þêø›Ž¼9^¥l«’cY§àêNÛЪî±ï÷£Ž±õùÀtȃö¼ò¿—Àºt ÍCw…FG Ýs›le2%ö³Ÿ­²‘¦³MŽí¤qï¡îÉîü| Êæ–ë´^JÎYû]_ÚÓ¸¯’ÑÔRÿdJFˆ©ûõfcˆôÉl_îi%sxFšhIâ«o|ãÕuâD—5?“ÈèS ¯Œ@™Œõ ²›Äcòµ[r²鸞 ¬I¨’WãYKFãœHpõd×o9fž@îçìc«F"yÝë^W=7_o½õFrèûrï, JZ•f÷ž3²KF}ÈyëØ’d`Ÿüä'Ëšk®YMË3ÛüÝêº&÷ß“ì§Y{¾ï[V§#TbõXÝ(‘"ñ{íµW¹ôÒKo—:WÏnL¿¸éÉ,C˜ñÁÕ‘ÎÐÄ‚«sÂÚMFºÿ´cC–¥ÞôªÉðø!Ò2­YIõ1õØpéåÝn¸‚Q¢k*‡{Kv®N%7öró4'?¾#¯Èûhݫè ÍÑØ/ƒü¿…¡3N~¨‹@.R’¡!¯%È:™~s!4SKpsÌoVÜ*[ÒØùð› B9öL6°£SÙtôʃœs4+†-»L:õ«´«+¼9‡ÿp3YBÓª´«3½œ[V­/7]s×.³D‚«ÓãZ!0’U+™’-뵯}mÕQquÖéû9ô mëð]‘ ÞV½ã·}“M6ÿÖ'»UÉ5}2<4+ÉT—,®£ZÚíçls§Q¸ò´]pu7Ë· ®îæø›ã`Îr#{²¥“Ãdëµé,ì¥É\š¡Isžœ>9WÌè*J½q¯·ßuöz߯^âZC`æ$IF Í+÷c4é,_ç’óû#Ž8¢/MLpu¿ëKÃþò—¿T™üziäRK-ÕËâ“^6÷¢2RmF,KFêûï¿¿«º”ñìg?»qhÖ¬Y—iÿ5fœèò¹NI'Üf%Ÿ$J'Ü §>Ñ÷\­´ÒJUàw²^|ñÅÍVã=3Z Ù/“ÐdÏ=÷¬FËõD­¤Ó„28|7'‰VF[¸æškª,¤tÓš~_dƒ¨ÓuG7{Ó<†'çÃ9wÌ+:r,ÈyT«ÑE†×²ökJÜ[ž ¤ÓiFòÍwj«’ éNe|puÎEÓi|Yl±ÅJF{Ç•ûëIˆ™ õ\Ÿ/%uxâ‰ã'Uî9ç_rMwÐA»Ì7ß|Õè1y¦¾ì²ËÎé%:§º¸)ÕÿŽk©þ$N|ýÎ|3˰éù'nWò…Û© #Ýÿ †,Ëvå&Pz®¤x§ŒÍT¾Ã;”dìn05ŠCtÕa¸·UW]µÙ.iú^Ú› Öy%-'É$—×òË/ßt™nÞì—Ã( Í·aü_ÂÐÝ|ÆÌC`ª.»ì²ªÓQëò]™ì¥ 4­{F™~{%·UYb‰%ZMšãýœ$si.¼'{óµ› vY±» Ñ}ú£]ðy5H=ÑÒ®ÎnëÊÜÉù_«rÕUWµšä}3F ™9æ˜ê•ï¼ô˜Ïñ`­µÖª½Átû®HG¦B·zHßîû27@[涺F­ýì²ös§ãg‚#Ú•^—àÚU?{Z§í˜=c‹_z]¾EµÞ&0yp“Nyå{6$Ó4×8ã;ùÄO£ĽÞ~×Ù¯û~Óh·h*‘øÓŸþT8à€ê•‘zr]“D,íî¥LF8'ƒ[‚9 ÜtjUr#ϸš•µ×^»•«Ÿõe=iWüò ¿ÿûßo¶úÙï½ç=ï™ý{ã—d…Š2vT¡õ×_¿ê„Õ®9Ȩv1L§ÈôÐCË‘G9ö­ ÿžH²œvÚi^vº-P§áÞ”•›FçŸþ„ó™NfÏFvÏÜ|ÌCµ„sPïæbx*ê<4GcGÔíÿÊÐ=ã'‰ d˜žSO=µzå!B:¼æ5¯)¹‰?ê[ ÄHÆ»V¥ÛíOpõ0ʳžõ¬–«it×r!L˜wÞy‡°–Ö«È…l»àênÎZ×n ÑÈMþ/~ñ‹Õ+©9ä•sòÉv™Jƒü®H u2„]tÑEÕ«]¶±vA¾ý8VM‡}1ˆ6vÛQªÕº;-_Ção«mñ>™ ïÝo~ó›Õ+C{o¶ÙfÕ±,CŒ&»2AÜëíwSyßo8{ÁZLo^¯×3RO^ HÐAîqåÚfüÃü©RÊý’Ü_[Ž?þø–Á¯Ûn»my×»Þ5vöÇýÞïú2²ÎqÇ7{=±<ðÀgÿ=ö—ŒŽ— Ñu,öy:¹&Ðolðr>9‡8묳Êk¬Ñ6iScù¿øÅ³7¿±|²ú%Ð?Çœf%Yx饗n6yö{…ÙoLð—t6KŒàê ™}F $0íC©^‹/¾øìãGzµ»o3#±¦x£û}}ÍD®;¦øƒbõ&!ÿÛsÏ=·zíºë®U uãYÉrË-7‰»HŽOiW§àꜧ3j´&ùÔ§?ýé*6 ­\ý±}¬ýd|«Ó!0×%­J‚Ñ32A«’¤ocÏÍŸ÷𕫝¾ºcÉdË®SpõØí»cßoüÞ¸†p Òñs*W?¦Ÿ×J»ž×ÃH÷?ˆ!ËÚmó+_ùÊ*À6I3lWßùÎwJ2´+?ýéO«Í2_Ò]uîíóŸÿ|uÂÒnßtš–á„×2ÝN;íT éÑ.h®ß£24G+ëAü_ÂЭ¼úù~>«9¡Wf¶ÀÝwßÝ€‡z¨ºðÉÅO†“Õ$Cáåõ²—½¬Lu°l_6rL%·ÞzkÓ!г´Ë"Ú˜g˜?“ Gé^ Óð³¾j”Ê™gžYŽ=öØQÚ$Û2 ~=,ÍÞ =œW¾{Òñ¦q<¨ûÛeëçwE:åæñŒ3ΨÄ'ÓM«éãÛ9Ï<óŒkößí† Í¨=Jk^½.ߺe¦4¸é¦›J†’Tf¶@»%‘ÉwòÑG]½r^ŸŒ69–¥Ó}®sše‰œHýæm-0ˆ{½ý®³ß÷ýZk˜2dYm÷Ìb2uZfz 4Nz2[sñ_þò—Õ+YèrîŸã@Ž ”{úÓŸ>™j-3ÆŠ4kv²M j;O‚Æ“5úä“Oûö¿'QÅØÀê±s¾±é¦›VY°Ç¾?ö÷?ÿùσ«;mÃØúZýÞ:ZÕ]§÷ ãz¢N{djÚrÍ5×ôeÅyvpØa‡U¯ù矿¬»îº³ïu šêKF´’M6Ù¤A/Ï´N<ñÄêÚd6µß×ià êtÝ1™½Ûû2ùœýýïï½"5L[|°/mÏuÉ/~ñ‹êµûî»—W\qöõÄzë­×U<^_Ò¡’Nçzå-9#¼-ºè¢Õ9lÎUs>šŽ…)7ß|óã:6Ví’€å«]ɨ-cËí·ßÞr´Î$OÍ(÷í2R¯ºêª³)Ôíº®Óþp 2ö“à÷©\ýØèæA`«‡½ÃJ÷ßï!ÐÚ}ð^ô¢5í-“¡r$Ãþ´+ùÂo\=JCtå3S§áÞrckçw._ýêWÛíš®§%0!ûû³ŸýlùÖ·¾Õ2(µß£24G3èAý_ÂÐͼúý^2\œwÞyý®V}ª`°K.¹¤ä•Ì4ÉX“ïäœðJ–Æ\$¶+ÉâZ§Òkæ¦:mË0Ú’‘+Ú•Q ®Î¹»ãA»=nÚdØ’ ,ÉÜ“ó´ ‰7 ¥×ïŠdXȰvÉwúé§OúÙ.¸:Áî­ŠÀ£V2ÿ|ß±³½Oݦ>ðÀŽeuÛ)#Òžöï{ßûšNJ—P2\s» 9ùÂoôÚiZÑcoN÷!ºê8Ü[NNÒ{ê£ýh+ö ¿Ÿ¬å[n¹eÉç<õŽè>H‡é:4G+äaü_uêž“¿ºß×ʯ×÷óÙÿùíµNËOOV+½lÍŸøÄ’émúŠW¼¢J5ÿo J˜ ¥]¦Ð™°ýÓ}“Q$Ÿ×V=Ô :jÅñ`Ôöèä¶gǃtFȱ ¯dêY걌o·ÝvÛäX³¥zù®¸ð «q®¼òÊ–[•ciÌ6Úh£òµ¯}­´:¶d¾V¥Ý(K: µªÓûê*àXV×=3Üv âX–Ñý’U(DzŒÈ‡4>oƒÙ¯ƒ¸×;ˆ:;î¸Ù{ì±GÕ©xöc~IæÒƒ>xÌ;~´@þ7ýZ¹þõâX­Î󛯵MJçÙPF½TFS ÓwIžÕ´+Í’-¿×å» üê´cÛÔì÷^—oVgßËvΔm­£Ú4ˆcÈ /\=#É1$×Ë/¿|˲:YÔ½-9.Ÿzê©næ ®Qç ã &Œ6ƒp<˜A;»Í¦âXÏVªs,Èsó$%[`Êþûïߦ%ƒŸÔîüguÖ©ž•vÓŠñÁÖc—é\çe3‰ š•ñÏ8R_–idçOI˜™WæË½™Œó‡;µ§Y†õ^»ý‘6ôz Ñëò®A†õI˜ëiýdrz´¿/­LOÊNå™Ï|æãffºÿÇ­¼Ïoä víµ×– ßÓ®çJNn?þñWà ´jB73:¥øoU÷Ø÷ûQÇØúFá÷d›NOÖòÞqÇ}Ù¤œPeŸ§ƒA²c³tÚÇušc¬OÝþ¯¦ÃÐcýúù{‚{ÉZÑ϶¨kêÒû°SçŸn[— ŸÔ•Q–‹ÂQ-K/½tÛMk×v!k#ž·­«ÓÈN={k³!]6$Y„Ï:ë¬.ç6Û¨ ¬½öÚåÜsÏíyóróç/xA•…'ǃ ±6ªe²ßÿó?ÿS Ýê<,‡vÝu×’óú<€KùÉO~Ò2¸:™ÈZ•¶ú>\ÝJmú¼ßjßNŸ-è_KWXa…röÙg÷¯B5MK÷¼ç=}½,É(—W¾ŒÏÒ3-4šÀ ÈÝFFð¹6±‰@²Ïæ¸%®s/;×5¯~õ«Ë(v´î‡“:š ô´ÑiùVדÍ[ãÝN¹'Ò.(¨Óò¦†@îÃ4F_ëu‹VYe•Ù×¹OÖ)Xª×õÍÄå;=/Ÿ‰&¶¹w¿üå/]%¡ì}Mj¨«@²K?ç9ÏéKó’œå•¯|eu=‘QÐZ%/íËÊPI:Žt[®¾úê–³~ûÛß.çœsNõ¿5ß|óU?“å:ºW«Àêf•&`:ÏéËש$ûâ‹/®^_ýêW«ÙŸÿüç—w¼ãeÛm·-ÙG£T:]CtÚÖNË»é$8³¦ ®~lß}÷Ý÷z³àêa¦ûïØÀgÈÓ¼º)›l²IÛـݩ¢«“Ðä§¿éMo*¯}ík˱Ç[:è rÙe—M¾²1K&{uêÎÐ<Ã*톂¨ëÐcm†ýÕÎ+íê W‡áûÆúù@Ýô•HoxÃJz°Î” ƒŒŠ ®VÑ®»îººí*홀@»€ÃW\ÑÃú Xšuæ¤çŽ9çîtfTT&ó]qË-·”œ_¶º µ÷Þ{—}÷Ý·tsýØpl5Ü^¦/»ì²åöÛooÌ:ÇόȓÎ@K=–M\™žwÝu×ôl¸V¨©@ÎóÞøÆ7–m¶Ù¦Ö¹¦ÍÔ, @2y%ð!ǃüÌÃ…Àdz}fÔëò“i³eèM`õÕW¯ŽIdÑ)9Kok²t:=ÿ¥D€©ÈõC:êçYI‘uÊ<mÄ:ÛGßÿý%Aëý,o{ÛÛÊž{î9©*ýë_WÁÕI¦ù­o}«êP;©Šj¸P¯×½._CM €àêÇpï¹çžŽÄÍš·ëÙÛlþ±+™hºÿ±ËNõï‹,²H•aÖ¬YM›’,Ý”^{®öº|7mœ®óäÄeÇw¬^\pA9á„ÊÉ'Ÿ\®¸âŠIoRN2TÇ8é:&º`»}\ס9&ºùûñÕÎ+ëétB[—áû&~¨ƒ@þo¶ÜrË*8là 7œPXÚ߯6$Öå—_Þ´º_ýêWÕ´QÎØÚtÃGäÍ›nº©å–ä«B€À?’Õó-oyKõàh&>4šÌwÅÁ\’¥Yɹ|®-:¿Ž_öá‡ÿÖì¿s¬:ï¼ófÿ=þ—C=´|úÓŸÿöãþÎÈ=ÿûß÷¾7¦V ›àêܼVh-ðŒg<£ì°ÃU¶š Ϫ @€ÀÌÈù÷ºë®[Lšû]ýÊ|=ó$mñX‰^×]ÖïLÿ÷¯ŽÉ~Ù¯L§Ógëµ”"ÄcÉPûKñf&fÖoõÌ£—OHâž÷¼ç•f‰NwÞyçò•¯|¥´{FÓiÝ·ÞzkŸQ¾7ÞxãN³O‹é®A¦Ån™F ®~lWÞpà wh†I_†™îüº‡ýw©3„ÀE]T½2ä\«Ò)€³ÕrÞŒ@2ëåõ©O}ªüá±lm'tRõúùÏ^Ú'4kM†õfpu³64Þ«ëÐöuó³nÿW:…tÚ¦NË·ÊZØ©^Ó L…Àj«­VvÚi§*cÿB -4M¨Õ:“ ´Upu‚ÐöÛo¿rüñÇתÍÓÀgœÑtÆ<`Ýu×]›Nó&™"ƒÉNãA‚gr™ÌwÅYgÕ’,“¹ùõÐCµ¬3ÁÕíÊ‘GY’¡¡Sgà«®ºªÜwß}íª2m þô§?u\륗^Úq3˜iyè•3o}ë[Ëf›m6c;‹Î´ýn{  0^ ÷mßüæ7WǃÜãQ @€@7ɘœ¬¤¹7–ä3sÍ5W7‹™‡FL ×Iî˜4bBFlÓ·9³¼þõ¯Ü27K—ŒÉ .¸`õ3‰VYe•jT¹$hmV’!üì³Ï®‚Ú÷»ß5›¥«÷«“@íßþö·Ÿ‘tU¡™Ì æÿ3 ›zþùç·ÝâdsM/‘ñeØéþǯgÛ’™;ÑóP<_Ò žê¦š¦¥©™'Ã`ï²Ë.Õ+CcŸvÚiå¿ÿû¿Ë‰'žXÚ ±Ýhíu×]W ë=Ý.žëò¿Z÷ÿ«^‡¾èuùÆçÌOS%‹–­·Þº¼÷½ï-/yÉK¦ªµ\o§oßÿþ÷K‚ÑVZi¥Z¶_£š <ðÀåë_ÿzÓ‰{íµWuaßt¢7 Œ¸@Ιs“)Žr“k¦—É|W$:s[•É^Oô\Œ_üâËÞ{ïݪYÕû‡vXÛé&N rÚ¬¤£o®Y(Ú¬¤XÎEþ)¢64dzd˜S @`ôºé¬ø²—½¬zέÖžŒ-"ðx|ðñoz‡–‹/¾xyç;ßY½ø¥ @€ÀÌÈõF²Tç¹y²wsý1”’œªÕ¹ež{sÌ1¥ß1syfuÁ”Œš,ÖwÜqǤ¨“xö?øA5ªÝ¤*°Ð„Z}N&T‰™k! {ác»¡Spõ«^õª¦;kPéþ_ð‚TC(4]éß|ä‘GÊøÃê™ ¥m¶Ù¦äÁò5×\Óu`uš×ïÅ7yFW½À TA„9xæ ºÅ[tôÈ—ÿ7ÞØq¾ºÍ0•ÿ«ÓéÿÊ qÝ>¹Ú3,\%Ð*Ùý;î8ÕMà7ÝtÓ&ïþë­Fo×VQÿšÓoÃÈÐPÝtŠËþ]wÝõ¸&% É{ìñ¸÷½A`ÔÒ±&×C×_}Ù}÷ÝG>°zß9ä<¸U9ýôÓ›NÊq8#ì´*íFBÉh=‚¶?ô¡UÖ­êÿò—¿\òši¥›cÆ0L–[n¹–«É5]:7+÷ßyãßXun6½ŸïÕŪŸÛ¤®ÑXa…ªÎs7ß|sùÌg>#°z´v¯­!@€À­ÎKD½ýöÛ—_ÿú×U†³×½îu«çóÇLhvÿk&:ØfV_}õ*)Wžô£-«;‰™N€ÑÈÈïz×»ªD§œrJ5"š8’íçv óüâ÷¿ÿý¿fîãoI”cóÿøÇò£ý¨}x5ÖèøLd|$(/2¸¿]ƒ ÎvØ5ÏøÌÕÉÈ|Ë-·´uOËfeØéþ›µ¡_ï]xá…ÕpW^yeË*sSnÝu×-m´QùÚ×¾V¡5›Y„f*õ~/YŒd^gééÔ®äÿe饗n7Kí¦MÕÿªÿ«Ú}4ˆÀ‹,²HÙm·Ýªln °VZ ¬¿þúÕ0â'Ÿ|rË™~ö³Ÿ•¶tÒIe™e–i9_» zpvšÞ®îL{øá‡« ãnn´[W»iÍÚÐnþ„õ»d?eè÷#Ž8¢e†Ï£d8¾¬¸âŠåØcðÅøøzüM`: ¤CAþÖ[o½éÔìžÛ:ÈïŠù矿ÊdÑ*à##$%¸{É%—¬¶ãoû[¸ß~ûµÝ®dÑnUòýõ†7¼¡úk5OÞÿûß_ÝÞ|óÍK2ù%vO²îsÏ=·Ý¢Õ´vßéÐ ÚÔ.ãwšÔiz§ú;mV·Ë/¿üòå’K.iYݶÛn[g¬½öÚ³çI0þÛßþöríµ×Î~¯Ù/ݶ!˶›·“U³u{À0V]uÕêX–ºNM†Ñë @€Á Œ¿·‘!¦3Tw:KO·û÷ƒ×²†™.ÐM`à îÑÍtwÛ?}’pàÃþpé”deúl‘– @€ÀDrO?£|à(‹-¶ØDŸ1ó'¾ J[•sÎ9§äYE¿J¶ÏY2ªc‚ß_óš×T¯¬cÖ¬Y%ëÌsúÄ|u în×ö~µY=ÿp 2:Ÿ„\Ý)tuÖ)/ùË›îñ©H÷ß´!=¾™áu“»U°•W^¹êõ²ÕV[•…^¸ZÛO~ò“–ÁÕýxˆÓîaf›;r‹ÿêW¿*{íµWÉŸd¤x÷»ß=©mÌÍØÏ~ö³ÕÃêvÙ©sR5ÝÊTü¯Öñÿjºí7í%0(|'ä{3J2ü¼ÒÀXòÝÖ.i:i½ð…/¬²‚&H±4×XCŽ/¹°Kç¶Ï}îse¾ùækLª~ÞsÏ=sü=þNÓÇÏ?þï\|æ"³]¯âÆ2™¯Ui7­Ù2íÚ}÷Ýw7[¤ç÷Ž:ꨒº?üð’ ý±åÔSO­F¯¸ï¾ûƾ]Ç'زq¾7ÇDA\ë%«g:†ÌÔ2¨ïŠÜà[b‰%J2§6+¹vyþóŸ_]‡fäˆóÎ;¯tê™zþüç?7«nö{ûî»o9þøãÛ«2s¾óšLi÷>™úú±L§6Ý{ï½mWÓiä‰Nõ·­ü±‰Ý7“q·]I;ŸÎÁyåFq§Îòú&² íæm7­±.? Sà9ÏyNùÔ§>ÕÕhdÃl—u @€ÀàÆ>`ßi§ÊÇ>ö±òÌg>sð+¶ÓPàOúSÇV_zé¥ç1QÈhÚ¹7öŠW¼bÔ6Íö @€@—餹Ë.»T£Ì ì±Õ æo—$»’-½SIõŒîЪ$CvÖ¯˜jœgvØaÕyæ[l1Õͱ~˜$KIƲÜsÈ1A`õì„ ¬Rð&9k»{½IÑîÙåå—_^p4ÉU[ŒÀ´ÈÈ“ßþö·Kx ¬žv»Oƒ  Ð$ÍL¬Xî›&Á–ÀêîX3ÚC»’{íyöÛ¯261JžÁw*Ûl³Mµ?[Í·à‚ ¶šäýI¸™Ú4\dFg®>à€Jœµ*ÉÔÜîaû°Óý·jg/ï|ðÁ%µ›•drÛÿý«áœ›MoõÞÃ?ÜjR×ïw\ífÔ?9Ç:ÜqÇ]·š±Ý™–‡ÖÓ­ ûµ®ÿWÓm¿i/~ l°Á%/eòÉš«\hwSn»í¶*Ûõøy×^{í²ÒJ+UoâŸ(9î§ÎNÁWßýîw«ÌØk®¹f•}:ÃS%såDJ·Ì·ì²ËVmHïÝ”‹.º¨|ï{߫΋~ñ‹_tÜÆ6Ú¨ä•õ'p¼QOêúáXÎ=÷Ürë­·V^íÎrO¦Î\ŒgØÞ§<å)eóÍ7¯þN]½–d²JFñ¼š•t¬:è ƒº œo¶¼÷LG(s â»âõ¯}9묳æ\Q›¿2BΑGYu„j@`ÚÇo½õÖÕµz:ŽŒ/ÿøÇKFZjUÇøùçû0Y%rü¥/}©ñöã~&¨wµÕV«¾§Ó!å]ïz×ãFjxÜBxc"ÇÏlW:—Ån•UV©†wLGótÎMæðÓN;­m “í"Y¢Ç߉nów¾órà 7TËç&}†ªÏñn|Ipt:ÂeD¥‰”‹ô¾ãŽ;¶’1ñÉ*’sÁg=ëYÕÃãt6JɹMFuHfôÔÓ®ÓSêIf¯Ô“kãµÖZ«ä³®¶@†ìV @`f ,°ÀåG?úÑÌF˜F[?¶cà4jvWMmdQïjæΔζ—\rIÓ5ä9h®{’b|ɽ¹7¾ñCéD[«ñþžyÉl© @€ÀÌX~ùåË1Ç3³&±õ믿~™gžyJ»g¿ p¾øâ‹û’Ðjlpuž§5ºÙ&¤­J2aB©ËyµkQø4uÞ†\Leí.póõ¯½­à°Óý·mÌ$'¶{ؽå–[N8°:Íx衇&Ùš-Ö̓pCtýÓklæÉVòÿ’íüÛÕW_Ýr¦œ¤Ût+Ãþ_­ëÿÕtÛoÚK€@ýÒY%=n“¯ÝGZþÁ~pö,éì6‘s‡dÓÈ+%D&\ýàƒVk³|¢AÑÉRÙnÄ’Ì?¶äböûßÿ~õV‚Íõäüàå¿þë¿ÆÎÞö÷{ï½wŽ ôëuêùܶÂ.'®ºêªåØc­‚íº\ÄlÌ@É~Wd´ƒ¯}ík%À:•t¸Éwg:«$[F»’¨„ ÂÍ‚«ðšóñÜ@ì6€!7$˜’ë†ßþö·íV_MKvÀƨ›nºiW74;V:Á&züL¦ö¼œÎIGuTÛQÆ7gìñw2Ûñ‰O”ÇéÅT"@€À8Õî+r¡.=vØaã²_z/©&/Õ=öXãÞÂêö•¹ •ƸéÉ8Ã¥×¥ôÔs V'¥ MÓ6Ûl3õºAÓ¼æ5_åV=/w½Ûn»m«ÛœÌSwçw^‘ÏP]Jà‚!XTw¡.Ÿ•ZÔåiÖ›ÀZ^÷[oÖ¶—ÀzHCËIi8è`Ò<£ïÏ:¿ŒHV—2šZFqùÊW¾Rdt¸#Ž8¢HpÆJÊ^·žåN«sH^é . ÷¦7n¾iù[fø½‡~xøåÄÿ§®¤GydyþçŒhsôÑGO ¬Î —³ uóÖM›¸a& @€À’€óŽ% ÿ °Lºã°‡zhY¹½ýíoŸzí:q‰éè$#PŽ vN/Ô_|q‘žÓ©ÙÙgŸ]Ü|óÍ[”cøX;qŽ…qZšÔ‘ì1ÇS¤#µEHuõ‘ò9q²ŸÓª ë®çêK/½´8çœsŠýë_•ÁfÏÛo¿}Ùz$7v§¥yw÷?­<Ëžp]kþôÞ«Ñ”žžrsRJËš&i½tß´·´&fãæÉn•Òº>AkÇ|õÖ²ž¯ºêª‰=ËåÂàᇾ¬ü†gnÛax]£ÿÏó»ºÖß«Ñm_ÎëI N–“‡y X v:óÌ3ËÞEoºé¦âÚk¯-Òóóƒ>X¤¡O޳¸¼ÿþû—ð&°zÏ=÷Ü(TÉ/Á×£peß”@·<Ò€%=M¦µhžÇw§7ͻᄏÌóIOzR‘Ç;ìP×eù,›GUƪ@§œrJy3%ËÆ•#eܸqcYެ»*GòLpÝpúÈG>R\pÁ[ä“|³M ô«¶©zN~ù?ëHPàjRzô~õ«_]öHšXÓÚ9å<蠃ʒ^ÿú×»í¶ÛjVaYz 0}Eö;itœ9× æd”œû ‚ÍùEzPNïaUÊ>òŽ;î(¬³ï®Ù—ç<37P²ÿÎ󴀨üæ\vÙeÅ{Þóžr¤\tÌ9É×NF=’ßJº«u{&@€@_Öòº__ mõ¿úÕ¯Êc½Iså|#A»ï¾û¤Y6{Öù%óiÁÕ9&7bÊ]wÝUäç¾íÁ¼Ù{Õ½ˆêÍ܇?î¸ãÊ€íÜ÷í05×ÁszÜèʹ®þ™Ï|¦ÊjÍŸƒ…s5ÿ6.Àæ‘ kwÆá–³ZÓõ×__|üã_~\¾ùàf(â¼àã&oñÞ¼»û-@näMJuAÓÕ2¹PàžI•Ù™%§ºX.Läl]š¸>ºLŸºÇ_ËáÞî¹çž%ÚÔ{‚ÃRwÓzˆ[Zèÿ$Ÿ×½îuc?ùQÎÍíiiÖuŸñ\ˆlšæù]]ËïUW¬rðU—¦MŸ–]Þ™¶Úå§åo:óHÒу^òXijÒ -Í ÒJ/ªMÒ0v¶‹%lRJﺔßÃló´@ªSNKÕ6M›o%ÓÔžÖËyH˜$0¯}Eöf9&iÒ><ûÍÄÅ6É/çï~÷»ËG“ù3φ ÆÎšàáEIÓ~?ã•߬ü~î²Ë.[;¿euy²|ÓߪÔñ$³üöÆ­éoøpAÓ›xÕ£øðû“þŸôùHê~»s]$ êRt]wQ·¼ih"PwݤɵÞqë˜ež³¾î7®¼Þ#@€@%ð‹_ü¢¼_—û…¹wQ×áO–ICÎ\×Éñcžsü™žŒ« ÜYçW•sø9#«dT•4 lš2bËZV§½tÜ“ á/×¥4ZMGé<á¹Ï}nÙH65ýë_÷ Ïþà?¨[¼ÑèÞ{ï-=ôв±IV÷küßÄ+®¸¢ü dù=öØ£ìý{Ü9NŽéßö¶·ûØÇšd»4Oô£•#M ®NãÕ|¶Ž=öزÇëôú—šI¹—NÒ7ù$XfRJ>éÁ0ùäúáóŸÿü²Ñï¤ù½O€ÔÓ×M§W7ÿ"œs¤ÌÎ;ÆÕœ÷X®À•W^Y¤ƒ°ôãµOJ‰=ʹà _øÂ²ÁdŽ5O>ùäòõ¤eÞûÞ÷i°™õLKÙ¿þò—¿¬-ÑŒ^C7ÂMŽ'ÓÁWmæø8ñéÌ$±}ßüæ7‹w¦\Ÿÿö·¿]ž'N›çkç Oh;y¢ ÿ-dpõp°è8ÄIÁÀÃó&À7=9¥Eõ¾ð…â·¿ýíðä-þÏPÀÙÑLëýjtÁt÷ÿÕ¯~µìitZõºêîÿüóÏ/o(öÒ” )ã%—\²´£ËÉu†«KuÝäÛÉŽæ•›¬9y¿ÿþûG'•¯ó’‹é*HÒú|Z‹ï,˜ MRÓ!º²SÉ#?M[#ÖÙŒ–­nÞºiÃùLë‘ -æßüæ7—­‘Ò Á÷¿ÿýòbT>ë Æ]MÊE­á”ÞÔ2¤Ù;Þñ޲¥Tzìš–R·éÅrÒö¦aÂË_þòiÙLí™a¹“Ê“‚¬dhŽy|W×ò{Uç³® ‘²J @€ @€õ$Pw]§ÉµÞqV³Ìs-¯ŽÛ6ï ÐoôþûáxY™{m ®‚…hPWÏ:¿qKÐBzŠ{Ík^3nòïeôµq=Yo1c o|èCšÚËðjÜžGî]&ð#÷4Ó©UÓôóŸÿ¼È#)Û½œàê,3¼|zÁ\ùÞ÷¾÷•.é!ºIJãÞþð‡ePüÃ?\»HFëËȾI Ω‚«óy[N=ç“ûnQI"@€@@Ýþ©nÚ¸­K š>餓ÊVk dÌNc¯½ö*ŸóZ…ßzë­å°­uùdZz:û쳋|‰óÿrÓ¼ºû-׬†,K þIÁÕYgNò¿øÅ/Ž®¾öuu3„A5Üð¤™û4D×´ƒü¶†{KCƒqŸ 9X8í´ÓŠW½êUe`÷p]¤ESZágН}íkÓ6û?ß´öo’féÐÕ¡9â´Vß«iN]¾¯ÉgÍ< @€A >sÞU×óÅ,˙ޤÏ=÷ÜYf)/XY]ëÞ”Yç9Ëë~Ãåô?Æ ,·#¤iyÌ:¿qëË{ ”Í}ÎŒºqãÆI³ rÈ}™et)UŽÕórËžÑuêz$]n~£ó§3£ÜCNu‚Hêê =÷»ß-{¢N>u£ÝŒ®gøõJ-†óð?& äž]c–tºöÓŸþ´H`Þ´4ë󃬯ð”ëèÚ9ÇèFUuQ=NŸöÚ9È4!Ó§ ,Tpõ÷¾÷½"ÃAMr$­ß®¾úêiÛV;=_žôÔ›®ú—ÛBz4ãyt÷ßÖe¹ÐsÝu×nÒÄ×¹0ñùϾ ¶Mõ¸”Þ¥3<Ø)§œRÈW=ŒÎÛ§!ºÖj¸· a‘^Ú'¥ ý†y¤B>ë¹p•åÒrÿÑG´h‘¡‘Ó:êâ‹/ž8Ïè„Õ:ôahŽ˜Ìó{ÕÇ¡3F?W^ @€XT -8®W‰¶Ê›Fá‚«ÛÒ•/æ'ÐÆµÞ6òYíu¿á¼üO€i¹¿”{ È1ðð#÷÷Òè0Ï$èlÒ##·ViÖùUùŽ>ç¾ÊG?úѲ÷ê+®¸¢øÙÏ~VÜvÛmå}œP<õ©O-Ž>úèâ]ïz×Ã~æÕæë Çpصú›m¶)Ò9O“Óˆô‘G)ï%å¹êTéòË//.ºè¢"½uç‘e󜺩–O½dùjUä9\†tb”ûa££:%H:Áí§žzjy/ùöÛo/~ÿûß—såþØ3žñŒrÄÞÜNY«tÍ5×”Ne4عÓÛ²0ãM=üðÃc´Ù¥´4Iëå´l{å+_Y|îsŸ+î¹çž²¥û§>õ©"]àÏ2UÝýÿä'?)ÒBy9)­ÕÏ>ûìâ7¿ùMqóÍ7mu½Òîí‡Ë1.´LÿÆ7¾QvØaónñzâM û´vLë´Zo’Æ­sx¹jˆ®óÎ;¯l%?øàƒ‹Z<å"@€hãZoyŽò­æºßh^^ @€@Q<ôÐCÅ—¿üå"CÏ#¥c˜sÏ=w«²X¡@“ãúÁHÍe‡xùýÈã‘G)}ôÑ2V¦Zm“|ÝH–ù ÇÝ ÿÿ”§<¥Ê®|n#ÏÍVð¿Î;Æ©x³p2G¹¬… ®Nph‚§¥œøo¿ýöÓf[óéÏ|æ3‹<Þð†7̬,ÉoZŠO—›Ò›ïYgU>š,{ÀŒ-ëOoÊy,7%È7¦iÒzòYªëÕ:ãÓ>ké},wܱiq–æ;äCŠ<Ö*%øý˜cŽ)kU†¬w%£'hãÊ¿ÒÏø¸¼ò^ßÕj]óø^¥7ùº¯|&²_Øe—]¶˜5ß•-ŸÌÓô>pæMj#Ïÿæ¼å_ç[šx‡«p²ZA˯7­ÖÛÛ^ @€]øç?ÿ9qê¦M\Èz)P·?¨›ÖK E€ @€ @€ @€† ÓsuÃòš @€ÀºøÛßþVÜu×] n¹å–"Ãùe´‰õ+`_±~ëÞ– @€ Ð ô$½çž{έ°ûí·ßÜÖeE @€,ž€sÅ«%ZlÁÕ‹]?JG€ @ ¸öÚk‹Ûn»­øÓŸþT\yå•ÅÆ'ª$ðú%/yIñÒ—¾´Ø}÷Ý‹vÚ©8ýôÓ‹œ,Kô[À¾¢ßõkë @€è—ÀqÇW<øàƒýÚ([C€ @€À 8YتQ°\½ £X @€£Çë!üIDAT*K.¹¤¸êª«ª—SŸo¸á†"*½â¯(öØcê¥gz*`_ÑÓŠµY @€ @€ @€ÌU`«¹®ÍÊ @€X¶ÀÎ;ï¼ìeªvØa½VWž ô\À¾¢çló @€ @€ @€˜‹€ž«çÂl% @€V.ÞhÏ8ãŒ2Hz»í¶ÛìyÛm·-3~üñÇ‹ÿûßÅc=V>Wÿï½÷ÞÅN;í´ò•[’ÎØWt¦ª” @€ @€ @€\½À•£h @€"°Ûn»Ç{, Ô ØWÔò˜H€ @€ @€ @€F[5šËL @€ @€ @€ @€ @€è¹€àêžW°Í#@€ @€ @€ @€ @€ @ ™€àêfNæ"@€ @€ @€ @€ @€ @ ç‚«{^Á6 @€ @€ @€ @€ @€f‚«›9™‹ @€ @€ @€ @€ @€ž ®îyÛ< @€ @€ @€ @€ @€š ®næd. @€ @€ @€ @€ @€z. ¸ºçló @€ @€ @€ @€ @€h& ¸º™“¹ @€ @€ @€ @€ @€è¹€àêžW°Í#@€ @€ @€ @€ @€ @ ™€àêfNæ"@€ @€ @€ @€ @€ @ ç‚«{^Á6 @€ @€ @€ @€ @€f‚«›9™‹ @€ @€ @€ @€ @€ž ®îyÛ< @€ @€ @€ @€ @€š ®næd. @€ @€ @€ @€ @€z. ¸ºçló @€ @€ @€ @€ @€h& ¸º™“¹ @€ @€ @€ @€ @€è¹€àêžW°Í#@€ @€ @€ @€ @€ @ ™€àêfNæ"@€ @€ @€ @€ @€ @ ç‚«{^Á6 @€ @€ @€ @€ @€f‚«›9™‹ @€ @€ @€ @€ @€ž ®îyÛ< @€ @€ @€ @€ @€š ®næd. @€ @€ @€ @€ @€z. ¸ºçló @€ @€ @€ @€ @€h& ¸º™“¹ @€ @€ @€ @€ @€è¹€àêžW°Í#@€ @€ @€ @€ @€ @ ™€àêfNæ"@€ @€ @€ @€ @€ @ ç‚«{^Á6 @€ @€ @€ @€ @€f‚«›9™‹ @€ @€ @€ @€ @€ž ®îyÛ< @€ @€ @€ @€ @€š ®næd. @€ @€ @€ @€ @€z. ¸ºçló @€ @€ @€ @€ @€h& ¸º™“¹ @€ @€ @€ @€ @€è¹€àêžW°Í#@€ @€ @€ @€ @€ @ ™€àêfNæ"@€ @€ @€ @€ @€ @ ç‚«{^Á6 @€ @€ @€ @€ @€f‚«›9™‹ @€ @€ @€ @€ @€ž ®îyÛ< @€ @€ @€ @€ @€š ®næd. @€ @€ @€ @€ @€z. ¸ºçló @€ @€ @€ @€ @€h& ¸º™“¹ @€ @€ @€ @€ @€è¹€àêžW°Í#@€ @€ @€ @€ @€ @ ™€àêfNæ"@€ @€ @€ @€ @€ @ ç‚«{^Á6 @€ @€ @€ @€ @€fÛ4›Í\ @€+ظqc‘‡D€ÀüþóŸÿÌ¥ÖH`ŒÀ¦M›üŒqñÓGN72Çü|çgmMFr<)Xœgû=X„šPÍ\kæd®ö\kߨÔ 8Ÿ¨Ó1mžÎ'æ©m]ÖF O× W¯ÍgÈZ  @€u$°aÆu´µ6•Æ |ë[ß*¶ÙÆ)ø8ï @€@7î¾ûn¿eݨ*¥$@€@«guV‘‡D€–#pã7:ŸX˜y  ÐSO<±§[f³è£ÀV}Ü(ÛD€ @€ @€ @€ @€ @€å è6k¹bæ'@€48è ƒŠ£Ž:ªÁœf!@`^ûî»ï¼Ve=Jí¶ÛÎoÏ3È1¦D`ÞGqDñücÞ«µ>jvØa‡š©&˜½À“Ÿüdç6³g•#¹ ì¿ÿþs_§pŸÄg€Àâ l½õÖ‹W(%êµÀ~ûíç|¢×5lãL8øàƒ'OìÀ”ÿÛ4H(§" @€ @€ @€ @€ @€ @ U­ZÍ]æ @€ @€ @€ @€ @€舀àêŽT”b @€ @€ @€ @€ @€ Ю€àêv}åN€ @€ @€ @€ @€ @€@GWw¤¢“ @€ @€ @€ @€ @€vW·ë+w @€ @€ @€ @€ @€:" ¸º#¥˜ @€ @€ @€ @€ @€´+ ¸º]_¹ @€ @€ @€ @€ @€ ÐÁÕ©(Å$@€ @€ @€ @€ @€ @ ]ÁÕíúÊ @€ @€ @€ @€ @€Ž®îHE)& @€ @€ @€ @€ @€í ®n×Wî @€ @€ @€ @€ @€tD@puG*J1  @€ @€ @€ @€ @€hW@pu»¾r'@€ @€ @€ @€ @€ @ #‚«;RQŠI€ @€ @€ @€ @€ @€@»‚«Ûõ•; @€ @€ @€ @€ @€\Ý‘ŠRL @€ @€ @€ @€ @€Ú\Ý®¯Ü  @€ @€ @€ @€ @€舀àêŽT”b @€ @€ @€ @€ @€ Ю€àêv}åN€ @€ @€ @€ @€ @€@GWw¤¢“ @€ @€ @€ @€ @€vW·ë+w @€ @€ @€ @€ @€:" ¸º#¥˜ @€ @€ @€ @€ @€´+ ¸º]_¹ @€ @€ @€ @€ @€ ÐÁÕ©(Å$@€ @€ @€ @€ @€ @ ]ÁÕíúÊ @€ @€ @€ @€ @€Ž®îHE)& @€ @€ @€ @€ @€í ®n×Wî @€ @€ @€ @€ @€tD@puG*J1  @€ @€ @€ @€ @€hW@pu»¾r'@€ @€ @€ @€ @€ @ #‚«;RQŠI€ @€ @€ @€ @€ @€@»‚«Ûõ•; @€ @€ @€ @€ @€\Ý‘ŠRL @€ @€ @€ @€ @€Ú\Ý®¯Ü  @€ @€ @€ @€ @€舀àêŽT”b @€ @€ @€ @€ @€ Ю€àêv}åN€ @€ @€ @€ @€ @€@GWw¤¢“ @€ @€ @€ @€ @€vW·ë+w @€ @€ @€ @€ @€:" ¸º#¥˜ @€ @€ @€ @€ @€´+ ¸º]_¹ @€ @€ @€ @€ @€ ÐÁÕ©(Å$@€ @€ @€ @€ @€ @ ]ÁÕíúÊ @€ @€ @€ @€ @€Ž®îHE)& @€ @€ @€ @€ @€í ®n×Wî @€ @€ @€ @€ @€tD@puG*J1  @€ @€ @€ @€ @€hW@pu»¾r'@€ @€ @€ @€ @€ @ #‚«;RQŠI€ @€ @€ @€ @€ @€@»‚«Ûõ•; @€ @€ @€ @€ @€\Ý‘ŠRL @€ @€ @€ @€ @€Ú\Ý®¯Ü  @€ @€ @€ @€ @€舀àêŽT”b @€ @€ @€ @€ @€ Ю€àêv}åN€ @€ @€ @€ @€ @€@GWw¤¢“ @€ @€ @€ @€ @€vW·ë+w @€ @€ @€ @€ @€:" ¸º#¥˜ @€ @€ @€ @€ @€´+ ¸º]_¹ @€ @€ @€ @€ @€ ÐÁÕ©(Å$@€ @€ @€ @€ @€ @ ]ÁÕíúÊ @€ @€ @€ @€ @€Ž®îHE)& @€ @€ @€ @€ @€í ®n×Wî @€ @€ @€ @€ @€tD@puG*J1  @€ @€ @€ @€ @€hW@pu»¾r'@€ @€ @€ @€ @€ @ #‚«;RQŠI€ @€ @€ @€ @€ @€@»‚«Ûõ•; @€ @€ @€ @€ @€\Ý‘ŠRL @€ @€ @€ @€ @€Ú\Ý®¯Ü  @€ @€ @€ @€ @€舀àêŽT”b @€ @€ @€ @€ @€ Ю€àêv}åN€ @€ @€ @€ @€ @€@GWw¤¢“ @€ @€ @€ @€ @€vW·ë+w @€ @€ @€ @€ @€:" ¸º#¥˜ @€ @€ @€ @€ @€´+ ¸º]_¹ @€ @€ @€ @€ @€ ÐÁÕ©(Å$@€ @€ @€ @€ @€ @ ]ÁÕíúÊ @€ @€ @€ @€ @€Ž®îHE)& @€ @€ @€ @€ @€í ®n×Wî @€ @€ @€ @€ @€tD@puG*J1  @€ @€ @€ @€ @€hW@pu»¾r'@€ @€ @€ @€ @€ @ #‚«;RQŠI€ @€ @€ @€ @€ @€@»‚«Ûõ•; @€ @€ @€ @€ @€\Ý‘ŠRL @€ @€ @€ @€ @€Ú\Ý®¯Ü  @€ @€ @€ @€ @€舀àêŽT”b @€ @€ @€ @€ @€ Ю€àêv}åN€ @€ @€ @€ @€ @€@GWw¤¢“ @€ @€ @€ @€ @€vW·ë+w @€ @€ @€ @€ @€:" ¸º#¥˜ @€ @€ @€ @€ @€´+ ¸º]_¹ @€ @€ @€ @€ @€ ÐÁÕ©(Å$@€ @€ @€ @€ @€ @ ]ÁÕíúÊ @€ @€ @€ @€ @€Ž®îHE)& @€ @€ @€ @€ @€í ®n×Wî @€ @€ @€ @€ @€tD@puG*J1  @€ @€ @€ @€ @€hW@pu»¾r'@€ @€ @€ @€ @€ @ #‚«;RQŠI€ @€ @€ @€ @€ @€@»‚«Ûõ•; @€ @€ @€ @€ @€\Ý‘ŠRL @€ @€ @€ @€ @€Ú\Ý®¯Ü  @€ @€ @€ @€ @€èˆÀÿûŽNsïgì"IEND®B`‚upstream/docs/design/images/bluestein_fig4.png0000775000175000017500000022077514637212246020464 0ustar kaolkaol‰PNG  IHDR ®r&"Å&iCCPkCGColorSpaceAdobeRGB1998(‘c``RH,(Èa``ÈÍ+) rwRˆˆŒR`ÎÀÊ Á ÀÀÈ`˜˜\\ààÃ0|»T—uAfaÊã\)©ÅÉ@úg'•000fÙÊå% v-’” f/±‹€²·€Øéö °ûXMH3ýÈæK³™@vñ¥CØ 6Ô^tLÉOJUù^ÃÐÒÒB“D?%©% Ú9¿ ²(3=£DÁR© žyÉz: FFÆ  p‡¨þOF±31@ˆÍ‘``ð_ÊÀÀò!fÒËÀ°@‡*BLÍA@ŸaßœäÒ¢2¨1ŒL@; ñ)PJ[Ô“ù8eXIfMM*‡i  ® rƒþVú@IDATxìÝ Ü•sþÿñO›¤H‹¤I!ýdm eɾNvCƒ±deFÖÉ2ö-[vcÊ’*‰ ¡HJ))þÞßù_·sÎ}-g¹Îz¿¾Ç¹Ï9×ò½¾×óº®sŸë:Ÿëó­õÛïÅ( € € € € € € € € €H v–Ãb@@@@@@@@@p®±# € € € € € € € € €T€Àµ‚r³0@@@@@@@@@ר@@@@@@@@@ *@àZA¹Y € € € € € € € € €kì € € € € € € € € € p­ Ü, @@@@@@@@@€À5ö@@@@@@@@@‚ ¸VPn† € € € € € € € € @àû € € € € € € € € €@A\+(7 C@@@@@@@@ p}@@@@@@@@@  ®”›…!€ € € € € € € € €¸Æ>€ € € € € € € € € PP× ÊÍÂ@@@@@@@@@\c@@@@@@@@@(¨kåfa € € € € € € € € €®± € € € € € € € € €T€Àµ‚r³0@@@@@@@@@ר@@@@@@@@@ *@àZA¹Y € € € € € € € € €kì € € € € € € € € € p­ Ü, @@@@@@@@@€À5ö@@@@@@@@@‚ ¸VPn† € € € € € € € € @àû € € € € € € € € €@A\+(7 C@@@@@@@@ p}@@@@@@@@@  ®”›…!€ € € € € € € € €¸Æ>€ € € € € € € € € PP× ÊÍÂ@@@@@@@@@\c@@@@@@@@@(¨kåfa € € € € € € € € €®± € € € € € € € € €T€Àµ‚r³0@@@@@@@@@ר@@@@@@@@@ *@àZA¹Y € € € € € € € € €kì € € € € € € € € € p­ Ü, @@@@@@@@@€À5ö@@@@@@@@@‚ ¸VPn† € € € € € € € € @àû € € € € € € € € €@A\+(7 C@@@@@@@@ p}@@@@@@@@@  ®”›…!€ € € € € € € € €¸Æ>€ € € € € € € € € PP× ÊÍÂ@@@@@@@@@\c@@@@@@@@@(¨kåfa € € € € € € € € €®± € € € € € € € € €T€Àµ‚r³0@@@@@@@@@ר@@@@@@@@@ *@àZA¹Y € € € € € € € € €kì € € € € € € € € € p­ Ü, @@@@@@@@@€À5ö@@@@@@@@@‚ ¸VPn† € € € € € € € € @àû € € € € € € € € €@A\+(7 C@@@@@@@@ p}@@@@@@@@@  ®”›…!€ € € € € € € € €¸Æ>€ € € € € € € € € PP× ÊÍÂ@@@@@@@@@\c@@@@@@@@@(¨kåfa € € € € € € € € €®± € € € € € € € € €T€Àµ‚r³0@@@@@@@@@ר@@@@@@@@@ *@àZA¹Y € € € € € € € € €kì € € € € € € € € € p­ Ü, @@@@@@@@@€À5ö@@@@@@@@@‚ ¸VPn† € € € € € € € € P@@@@@š!°bÅ ›0a‚{4hÐÀ:tè`]ºt±µÖZ«fd±–óçÏ·_|ÑZ·nm½{÷΢fA@@@?Z¿ý^üF0 @@@@@ 2~úé'4h 2Ä–/_ž´RC‡µ~ýú% «Éo~ýõW›2eŠ V>|¸½ûa={ö´1cÆT$µ_ÀX‘›—•B@@JV€Œk%»ih € € € €5K`îܹ6yòd«W¯^à£nÝÿ]Ò\µj•­\¹ÒôœøH–øºV­Z¶æškZãÆÝC:zhx¥—×_ݦ}úé§•¾ª¯ß¬Y³ì³Ï>«zLœ8ÑÆg‹-ʸ®r˜á“O>±·ÞzËfÏžmZw=ôZÇž‚óêׯoë®»®µlÙÒ=·mÛÖzôèá2Í5kÖ¬V‘6"€ € €”‘ke´±h* € € € €•,ðÚk¯ÙÑG]°UlÔ¨‘ë&³k×®¦ÇvÛmg;v,Øò ± ë¯¿ÞÎ=÷\”Tˆå•Û2:uêd?ÿüs¹5;ãö*`í²Ë.³‡~ØzU ‹Ï?ÿÜ=¼i”¥¯víÚ¶õÖ[Ûî»ïnûí·Ÿm¿ýöÞhž@@@ÈZ€Àµ¬é˜@@@@ÊY`É’%6vìX÷ðÖc§v²ÓO?Ý:è ó²»yãÊéù—_~±SO=Õîºë®rj6mY@Yöþõ¯Ùƒ>°µXec›0a‚{Üpà öã?Z:u¢fc< € € €¡®…ò0@@@@ %°ãŽ;ÚM7ÝdóçÏ·I“&™2°)¸¬EÝ(êѦM»ûî»]†©B.?Že}ÿý÷vðÁÛ˜1c⨮¢ë8óÌ3M^êtÔ¨QöÃ?äe}øõì³ÏÚÛo¿mçœsŽëŠ3/ J¨TÇÏž{îi+V¬HúÇKeQS ¦² n´ÑFÎAÇóæÍûc”WêÊWófSŠáM;™@@@ 0®Æ™¥ € € € € €@„€‚gú÷ï_5ÕG}d[l±…ýöÛoUÃR_hž{î¹ÇÖ^{mkܸ±½xñbÓCÁHêöðý÷ßw™¢TßÊ•+S«ð}ÿå—_ZŸ>}ì /´Aƒe¨ã[yj}{÷îmê2±ôìÙÓî¸ã 4cÆ gvûí·ÛO?ýdõë×Oœ´F½¾òÊ+«ÖWÁZÛn»mR7™U#³|±páB»ÿþû]@æ¬Y³\-Íš5³fYcz³M›6Íe ZÛe—]ìºë®s]åúÕ¨¶*óàsÏ=Wm´ŽÉZµjU6 Xamb € € €@ñ\+þ6  € € € € à#°ùæ›ÛÆo\-+qÒõ×_ß”•NY¶l™ 6̰¥.B£Š²C]|ñÅ6eÊ{â‰'2Ö‰ª?îñ 6R@Òœ9s’ªnР=ÿüóÖ°aC7\®×^{­þù6wî\Ûj«­’¦¯©oÖ]w]ëÚµkÎk ´T¶³¡C‡ÚÓO?mË—/O"UàZ>‹2î½÷ÞÙã `‰{~mi×®1ÂxàSV:žyeË-·ô^†>Û!´qŒD@@( ìòy—DÓi € € € €Tº@Û¶mc[Epyä‘öæ›oº`4u“˜Nyê©§ì’K.IgÒ¢M£ »Ã;¬ZКô§?ý©*h-±Í›7'h-ä÷×k­µVÊÌßžtÒIÖ«W/{ä‘Gª­©6Èå³((ÑËî–ºœ=zØàÁƒS¾?æ˜cìÃ?LÚ:tè8}âˆb;$¶…× € € €¥)@àZinZ… € € € €¿ (Ø,¥sçÎ6fÌ»à‚ ÒêôÒK/µ_|1M‰¥Neö?~¼o]mÚ´ñÎÀêqt›ºjÕªê' ÉwàÚã?ž°´ä—7ß|³Õ©S'y`Ä»V­ZY‹-ª¦ZsÍ5«^‡½(¶CXÛ‡ € € P®•Æv  € € € € à#°úê«û gPݺuí²Ë.3óDu{¨®5Kµ„u}šÚUe©®C)´«V­Z97#*k[bXÎ K©@Á‹³gÏNú¿·k¬±†©›ØlJÓ¦M«fS=é”b:¤Ó>¦A@@(¾kÅß´@@@@\Vâ4:å”Sìˆ#Ž[Œ7iÒ${á…"§+Æ3fÌ\ìúë¯8Žñ De$ËgÆ5ukTÚµk—q¶5¯®fÍšy/“º ­èó¢˜>Ía € € €@ „_õ)ÁÓ$@âP·0wÜqGœUR € ›À©§žjÝ»w­>*B¸í¶Ûì¹çž³Å‹Oôû˜n¸ÁöÚk¯ÐiŠ1òÇ,ÆbY¦@XF²† ZØxŸê24kÖ¬ÀéçÎ8.jDÛ¶m­qãÆn²¨Lj^]aë™o¯ <#€@å Ì™3ÇX¹+Èš!€ €@Y ì³Ï>vä‘G–U›K¥±®•Ê–  €Eøì³ÏìÑG-ʲY( € %°÷Þ{¸…Äx@ &ãwÜqvã7†Öøæ›oÚŠ+lµÕV ®Ð#—,YRèE²¼°îmó™mMÍùþûïZeöÃ?ØðáÃm¿ýö œ&hÄСCMLJ12i'Ó"€@y ,\¸ëºå¹éh5 €)ЦMײܲ®e Çl € € € € PY§vZdàÚòåËí½÷Þ³vÚ)㕟7ož=ÿüó¦¬XÊóÛo¿Ù&›lb›nº©uìØÑ:tè` 4ȸ^ÍðË/¿d5_&3Íž=Û>øàÓM`Ÿ~ú©-X°ÀZ·nmíÛ··7ÞØ¶ÜrK[o½õ2©2pZ=óÌ3ÎzРAÖ©S§ÀiýF,]ºÔFŽéºvíÖ­›ýíoó›,/ÃêÔ©XoT÷™3¦9¢víÚ¡Sžxâ‰Ö¥K+D÷±ùvP žŽ§Q£F™‚ýÿüç?'­ûĉíÅ_4=««Ó=zØa‡–4MЛ|«©Ë,äqõí·ßºàÅÑ£GÛ…^è>{Û£ž ÆŽkŸþ¹)(HYö6ÜpCëܹ³3®W¯^âäI¯õÙøÄOØôéÓí‹/¾°ŸþÙ5jd[mµ•õìÙÓ¶Øb ‹Ú?“*ä  € €Ô×jÄff%@Ò¸æškª]äLg>¦A@âx衇ìÿøGœUR €@š ¾ÒcæÌ™¡s(°#“ÀµÉ“'Û•W^iO=õ”ËÖT¹‚Öô?àïÿ»Õ¯_?h²‚W0Ëu×]çºRU°]PQºSN9ÅhÖ´iÓ É|‡+cÜøñãíõ×_wk2óJŸ>}"×Ô®©S§º 3½üò˦@Ú2pÍkw1ž©u* ÈR‘¶§2 –S ѪU«Lû…²ª[_í—^À¦ÆykÚîÚÞ÷ßÃ[o½¸VÈcµÇ•Ž‹)S¦¸ Î#FظqãLV*òRЬÊG}dýúõ³wß}×½÷ûÓ¢E »ä’K|¥ûî»Ï `óçÏ÷›Õ SPëþóÛe—]§aÙ üå/±K/½4ÛÙ™@ÈJà¿ÿý¯pÀYÍËL¸ö‡¯@j¸€.ª·mÛ¶†+°ú € Pl&Mš» ,¨ÑÛm·]dàš²¥¥S”JY† R,6ß²eË\З‚@nºé&Ûk¯½Â&¯÷ì³Ïº IUR^(`E”䣠žŸ~úÉ´,=×­[×e5K™Å½}çwìôÓOŸ:ºP½á†LíW€JXûŸ|òI„¤ìLz(èÌ ¨I­×bq”iëã?vóë½¼K¡(ƒVPQWž²ùñÇmñâÅUϺ&¡€¿\É”¹/ªh¹ýë_í¢‹.²¾}ûºÇ6Ûl“ó²S—›«ƒ‚¨ȨýCÛYÛXnaEû·~8z饗ªM–ͰPǪ•Ïãê±ÇsŸ_ ÖTÖB¤…“©=<òˆp ¦,…aEõœ|òÉ.›ÚàÁƒÝ¤òîß¿ZÝÈ~ýõ×¶Ûn»ÙÍ7ßìê [ãÈT@Ùý¸®›©Ó#€ €@®sçÎ͵ æÿ]€À5v@@@@'°råJ×õ_¥r¨ÛºæÍ›Wêê±^1 l»í¶.°(¬:E§xàöÍ7ßDMZm¼ºáTׇÆ suT›àÿ˜1c†wÞy¦Àµ°¢à=üŠºL-ß}÷ 8Ðî¹ç×iêø¨÷ :æ˜c\`Z«V­|'Wf6u[˜mQð×UW]•íìy™O?Z(Ó—²u¡xÔQGùŽ>묳l­µÖò—î@uךnQ{•yMÎíºë®Ö«W/÷P·µÙ–¸n¹åS&Út‹3:è ß 5Õ±ÆkøVUˆcU .Äquì±Çº.:}W4e M˜ª@²LÊå—_n{챇ënöàƒ6e™H·(8õì³ÏvŸo…è®6Ýv1 € €O€ÀµâÙ³d@@@@ ¤.\è2C•T£blŒtôƒ90öíÛ‡vã¢×”Áèøã¯êªÒ«puÖ±Ë.»Ì”ÝJ™Š^}õU÷^A£~夓N²wÜÑÖ]wݤÑʘuÎ9ç¸ÌfAó&Íò¦K—.ÕÆÞ~ûív÷ÝwWžÉ€ ˜ºnSV´J/êŽñ®»îr]¼jÛdSÖ^{휃ִÜ=zØ;ìຄ̤Ú§•O)ˆí°ÃsY²jÕªY]1~ýõW·¿½øâ‹íô \Ë÷±šØ˜R;®¼®UÛ˜îë}öÙÇekÔ÷†L‹>û{ñÅg:+Ó#€ € €@ ¸V•UB@@@@ìü2¥ÖÖ%åСC]·{©ó´lÙÒ^{í5KìÊq§v²-¶ØÂ=ôPßn2•‘L]ø ><©º™3g¦Õ5_ÒLoü×R'Uv%uñ׺uk×Uã´iÓL]~õÕW©“&½ùå—Mݪn°ÁIÃõFNóæÍs]˜ª[ÔL¾Ž>úhëÔ©“)pFY¹&L˜Pm… LZ 2Ì¥l¸á†¹Ìž4ïµ×^ë“føF]ËÞ{ï½î¡¶õë×ÏceÐSõq;(˜²À)¨îÎ;ï4e * @‹*©k…8VÃÚ”ãJ™4ª®B•IMݧSÔU½ŽSu/ºhÑ¢ÈYR»lÍt~e%$p-’™ @@¨®ÕˆÍÌJ"€ € € € €@:Ê|U”Yʯ(`D]wúu™´æM£îD6räHoPÒóˆ#ìÃ?´Î;W WÀ–¥ÔÕŸÚ« ‘ /¼°j|ê eÏRן~¥{÷î~ƒ«†m·Ýv¾m4hkÃOø í¿ÿþIã½7q;tëÖÍôP‘YÛ¶msê^61p­PǪg“úœ¯ãJ]wze—]v±=÷ÜÓ{ëû¬@Ý+®¸Â˵k×v]?ÿüó¦Àа]¯2msÍâ‰'Z&óësAYò4@@j¶k5{û³ö € € € € РAƒ„wþ/›7oî;⢋.rÀRG*8ãˆ#ŽH\õþðÃ÷ ó&˜4iRRàšÚ¨nËã?¸¥eŸ|òɉ“§ýZw~EÁrÊîôþûï»,M~ÓhØäÉ“MÝ †/-lš°quêÔqAÅ \«W¯ž=úè£UMTðâÕW_]õ>ñÅV[må2a%ËÇë[n¹Åu1Иér•eK–ÚÏUoj÷¡ùt¨_¿¾m¼ñÆ‘k ð¼üòËmÛm·5e¼òÊ+í¥—^r«š¸V¨c5ȸÇUb°«_;V_}uSת‰A£Ú¦:^ÇŒc[o½µ dó›Wüù·ß~ûªI¼ù•]²k×®ó+èVÙÛµkW5//@@@ f p;KÍÜî¬5 € € € €ø,Y²Äghò ¿î¿üòËjÁdÞ\ ÌjÓ¦÷¶ÚsïÞ½« K ìD…,½zõ²cŽ9Æe‚KÌà”Ú*Eu÷§.'£Jb@QÔ´Aãã¨#¨îr®,iêFuuÖ‰m”mPÛ|È!±Õ™nEQÛø ƒ²‰'Ú¾ûîë‚övÞyg{æ™gL™Çt z”Å:VKí¸R–´Ä µÄí KeB +Êú˜´–8­2 îµ×^‰ƒª½ž?~µa @@@š'@Ƶš·ÍYc@@@@ # 7ÜК6mšÑ<ÅžxÖ¬Y¾™¯ŠÝ.–_ú .Œl¤_àÚË/¿l«V­ò·uë־ý-Z´pÙ«‚º UÖ²B³´¤¶£OŸ>©ƒ’Þ+¸-ª(cš²Ò©ëÀlËj«­–í¬=ß‘Gi‡rˆ=üðÃvã7š²÷ÅQ”±Lu««ÈB•°m¬,ˆÊ¨})±(+ØèÑ£Y±ŽÕBWa^Ùi§’\Rߨ»Ðçž{.upÕûtæW·£AeéÒ¥A£Ž € €5H€Àµ´±YU@@@@ u x“M½ùœgÞ¼y®å¸‚ëþá‡"×ÎïxH ŽI¬ÄoúÄñuëÖuÁ¡ ,H\õú«¯¾ªz]j/”Í«qãÆ¶hÑ"ߦ)p(’Úíd:ó$N“ëü‰uUÚku³yÜqǹÇ;ï¼cÏ>û¬ HÊ¥kUí«ƒì5†aÛ¸G®ëÊt–[ÇjÇU˜—œ´_„• 6Ø ltÎó/[¶,´~F"€ € €@Í p­flgÖ@@@@Òøì³Ï"§êÞ½{µi¦OŸ^m˜7à‘G±±cÇZƒ ¬aÆîYÙÙ¸á=‚‚Ö¼:JéYAj&L°÷ßß=/^ؼ¨à˜À‘uí¨ÇW\a³g϶#F¸Ç믿n+V¬Èh™#GŽ,hàZXãtl¥[JõX-µã**à6Ê;jþ\2,F-›ñ € € P>®•϶¢¥ € € € € gqãÆ….AYˆ:wî\mš°À³Ÿ~úÉ>þøãjó”Ë­›²T½úê«6fÌ›1c†ukšºNQݦNÏû ¨èþýû»Ç?þh£FrÝm><°ÛÛÄÖÍœ9Óuïªn^Ë©”ʱZêÇU®ÝÀæ:9íS´@@² p-{;æD@@@@ ˆ \Û{ï½}×ø»ï¾óžË@e+S\Ÿ>}r©&«yW®\é2qÝ~ûíöÊ+¯¸¥l*"p-µÂϳæškÚÁìŸþ¹qÆöÌ3Ï„6dùòå6gÎk×®]èt¥6²˜Çj9WQ]–Úv¥= € € Pž®•çv£Õ € € € € ³€2‰Í;7´V÷ø•_~ùÅopÕ°¾}ûV½N}¡5e'jÒ¤‰{nÑ¢…m±ÅÖ©S'«[·ð—pß}÷];þøãmêÔ©©M­z¯võìÙÓvß}w»í¶Û\·“U#^£ý ‹çe믿¾ 6ÌN?ýt»å–[BkÐñRnkÅ:V9®Bw%F"€ € €@ (üU Íj#€ € € € €@i DéôèÑÃvÝuWß•hܸ±)•_QWŠ<ð€•Cö±‘#Gš²Êýúë¯~«b›o¾¹ËÆuÐAY³fÍÜ4/¼ðB`àZÝH¹ú6°†|ï½÷làÀ¦îi>úh;å”S²Q¶­ÿûß.ëž²ª•F*ÙáÅ8VKñ¸*Ù DÃ@@@ F Ô®QkËÊ"€ € € € €>?üðƒÝ{ï½>cþtÕUWýñ&å•2¦Íš5+htÉ Wö,;­þùöÁØ 'œP´ÕøU«VEM9þûᅵ† þ'0mÚ4=z´½óÎ;6iÒ¤œX4h`‡zhh^ðbèD%6²ÐÇj©W%¶Yh € €ÔP×jè†gµ@@@@@àË/¿Ü–,YòÇ€”WÊ0¶ýöÛ§ ýãí:ë¬óÇŸWcÇŽõZZƒn¾ùfûî»ï|¥lsƒ¶zõêùޏbÅŠ QiO'pMÆ(æ2­yß~û­÷2ëç–-[Ϋq­[·_ª# }¬–êqUªÛ‡v!€ € €@Í p­fmoÖ@@@@RFŒa×\sMÊÐ?Þ¶oßÞî¼óÎ?ø¼êÚµ«ÏÐ?ÝsÏ=¼)ÑWcÆŒ lÙhê>2ÓòóÏ?g:Kµé¿úê«jÃRLœ81uP|¿téÒªõ B¬š Ó§OœêÏþ³ÅÑlàò4¢ÐÇj©Wyâ¥Z@@@ #×2âbb@@@@¨$É“'Û1Çc¿ýö›ïj5nÜØž{î¹È®1wÜqGßù½o¿ý¶?Þ{[rÏ 0›0aB`»² PJ7pm5Ö\öÈ‘#-¬ËQmCu‘Y%ß™áë#pí½÷Þ d=üðÃÇEHlgÔ´q/ä±Zìã*n;êC@@â p-nQêC@@@@²:t¨ëþó‡~ðmoýúõí‰'ž°Ž;úŽO¸Ë.»Øj«­–8¨Úke¨Z°`Aµá¥0àÇ´•+W6å•W^ñ÷è£Ú믿î;Nýõ×Àq‰#6ÞxãÄ·I¯€5jÔ¨¤aÞ@)€*ÝåxóeóܘI]sçÎÍdòŒ§MìVõ“O>±—^z)ã:¼†n ô+íÚµ í:×ožÄaùvH\VêëB«Å>®R×=“÷qìï™,i@@@ f ¸V3·;k € € € €@Y(ð#îòÆoØ~ûíg'œp‚-[¶Ì·úÖ­[›¦Ûm·Ý|ǧlÑ¢…õíÛ7upÒû9sæØ®»îj3fÌHî÷FXÏ>û¬ÝrË-~£« [¾|yµaÞ€°€4ošF…vúꫯÚ_|áMnK–,±Aƒ¹ ±ª>/‚|S'íСCê ¤÷Gq„;6i˜æºtébS§NMžú&Ì&“iÓÍ׬Y³Ôj«Þ+Ðîé§Ÿ®z÷‹Ï>û¬ªJm÷ƒ>ØÞ}÷ݪaé¾P=Ç{¬o&Beß»öÚk#«ŠÛ!l;f’Á­Çj1«0/m¼¨ý9j|TýQ;H®óGÕÏx@@(ºåÑLZ‰ € € € €5Q 1ÇoýÓÉ ¤à©‰'Ú¸qãì¾ûî³)S¦øUU5l§v²'Ÿ|ÒZ¶lY5,çœsŽ=üðÑÝZþßÿýŸýýïwsë­·^RÕʘ¥6*`mÖ¬Ynœ¦ßa‡’¦K}”5NÓ)È,ª¬¾úê¦`½/¿üÒwRuíÚÕöÞ{o›={¶©ëÓ_~ùÅwÚÄóçÏO|øz“M6 §Z¿?ýéO¶þú뻇lÒÍÚf“ºÐ°iÃÆ%Ö£lda卣޲SO=Õ¶Új+›9s¦©+ÔO?ýÔ´¯+Ð)—¢zËÒ¥KÛ¹çžë 4h8Ú÷µ¶m¿~ýœ¹ß×\sxà~£’†Åíæ¿pá¤eG½)Ô±ZÌã*ÌK>‹/eŠ Žª?´òßG.Z´(jÆ#€ € €@  p­ldV@@@@røàƒ\`OXÛ¶ï¾ûš²;)HDÁ묳Ž{Öke9ûðÃC»ÁôêWWŸýû÷·Ë/¿<²ÛOožÄg"]|ñÅ.@(qxêk’]tÑEvÙe—¹à%ßé1oÞ<ûꫯ’&_{íµ­sçÎIÃRßü÷¿ÿ5u§TÆŒc ìiÒ¤IÐ$n¸ºë \ÓZÆý÷ßZGêHAiZµj•:*齂¹®ºêªÈ`¸Ï?ÿÜôH, ¸SÀ×Çœ8¸êµ2Ü)[\Û¶m«†ù½Ðv ËN¦®7µlÏ…•¨€-^}õÕÕªP›2Èe[´ùz*ÀðŠ+®°‡zÈŽ>úhS—µÚW‹2üÉOûä#<’8*鵎³Î:+iXЛ8tlè8*ãÇwÇϺë®4IÒðB«Å:®”%1¬(÷¯ýkà$:vÊê?ñÄÃ& §î‡Ã–:3#@@@ b\«˜MÉŠ € € € € P9/¼ð‚|òÉ‘gÊúóÜsÏå´âõêÕs¦þñD7E-Hu(,î Õ•ã„ B«üÛßþfk®¹fÒ4ï¿ÿ¾ 6Ì”IJê23¬LŸ>Ý<£ŒizV}Êp–4¢®Nä–nQ°ØÝwßí™Rî¼:”móÍ7w]Vn¿ýöÕ–éM·é¦›ºzþýï{ƒÒzVÙk¯½fÇw\`àš‚²´î½{÷¶6mÚX¯^½lÏ=÷tõ+øæ¥—^2e†S= *ªGÙïT‚åºuëæÛ=ì–[nil° š ª+uøÙgŸSКêûú믻¾Õxï)(Sw*OŸšOAya]CÖªUËN?ýt»îºëTUZ%WCo½õ–kŸ¶ÓŠ+—«`@íÓ;)`n­µÖrÝë}P)ıªeò¸ºôÒKMY¬©À°°rÏ=÷¸L:>·Øb Ógͽ÷Þk“&MrYG6»=þøã.+ä6Ûlc °ÕçuTphb…=ö˜ûüÒüM›6µóÎ;Ïm·Äix € €•/@àZåocÖ@@@@²P6*1ä»(ˆK]_*ƒ”â(µk×vÝŒ8Ð7«V&ËP —‚…R‹²‚)ƒV&EÁ, dñ‚Y¶ÝvÛjAd'œp‚ÝvÛm6yòäȪ;uêä‚ç:vìè²x…Í àB¸)8*5X.q¾Aƒ¹€?M—NÙh£lôèÑnÛEu;¨lq>ú¨«VP^àš<ü²Ÿ-?±u—© ¤ÔR¿~}»òÊ+íðÃOåû~¯½öʨ ¾•ü>°aÆ.S_ÚÞaåÛo¿5=Ò)ÚÆC‡5u¡›IÉÕAÁ™Ê—nQ×—êvÕ+ Ê \+ıª¶ò¸RPbX¢gã=+èUd*pMÝ¿ñÆÞèÈç÷Þ{ÏôPÑ~œIàšæIœ_ÙÛpHA@@š%P»f­.k‹ € € € €¥.„”iû•Qm½õÖ3e€:äCìŽ;îp™~ uà 7Ä´æµK1ʦ®ö¼)o\Ô³2©;F©+?µ;µ´lÙ2uPÆïýê¨S§ŽË¢´ÝvÛ…Ö§`-uͨ€&uC©Ldé¿e&Χ nêúUÙÇÔ–°¢m©€/àp5Ö›“kiܸ± 0SÖ8püñ.“U6õª=ûì³1ÂuÑ™iК·Ìb8xËNgÛæûXU[Š}\yaÏž•÷6­ß8}Æ*P‚ € €d*Pë·ßK¦31= €•" núõëçVG¯Õµ@(¦Àí·ßîºZR|ðA;ꨣŠÙ–]Ô}ç–[n©¶Ö[o½µë^¯Úˆ ®ÕEabQP‡b(¥/ .4?ýôSS0„«­¶ZÕko˜ÖBÓ)pJ´ôì=4\ºuëº.µíÕÕa±ÊÌ™3]f0uÃ9kÖ,[°`ˈ¥ö)k˜2¿é¡ &e€kРAdSU§‚nd“øetÙ×óð{VW—ÊÐåWˆ¦ ij¯º={¶)0L]0*+ÓG‘4›º+Õ<«¯¾zÕCë aêÂqÙ²eîYA1é˜éV°Ó¦Msm˜3gŽ Œiß¾½3R·„òóŠ‚• MËÕCm‘…ö µaùòåUû‰ö/@GNêVÑÛÏ-õZƉû™êSF+Õ§uQ×£aExêQ~S§Nu NÔ|;ï¼³ 0 ZW°aõe:NÛ^´¤ùå—_&=Ô=ªÖQíiÒ¤‰;VÔ¥ª²ñ)x1Î@¤lô¾dÉ’¤ýÛÛ>Ú^:¦S÷mï³`ÕªUÖ¡C‡L¹,Ǫ׈BW:^uü{N‰Ï:^ÔÙÈIû°·/·jÕÊ}èX‘»²åé¡ùõ,oo~™k~ïXPŸšmMËúøãÝü‰Ÿ ª3± Þg„ŽmÕCA ]}®è;ªÊi§f7ß|sº³2 € ‹À;ï¼c;ì°ƒ«ëÜsÏ%“v, +³Jþ8³/³†Ó\@@@@¨LHlºé¦‘+§ U”zñÓÔ^\EuFù(`$Ó¢`-uo¨G:EÁw~EËW˜™Pé‘n ZŽö¥°`9?Eík^ðOP _X»tébz«(¨W¯^îQ¬6h¹Ù8(¸2ªd»Õ›cÕ[V!Ž+eA +òÒ>¡Ï¿n9u¬„üiþt͵¾êRدè¸Ôg÷šk®é7ša € € PƒrÏ?^ƒ°XU@@@@@@@@@Ü\ËÝ@@@@@@@@@2 p-,&E@@@@@@@@È]€ÀµÜ ©@@@@@@@@@ ×2ÀbR@@@@@@@@@Ü\ËÝ@@@@@@@@@2 p-,&E@@@@@@@@È]€ÀµÜ ©@@@@@@@@@ ×2ÀbR@@@@@@@@@Ü\ËÝ@@@@@@@@@2 p-,&E@@@@@@@@È]€ÀµÜ ©@@@@@@@@@ ×2ÀbR@@@@@@@@@Ü\ËÝ@@@@@@@@@2 p-,&E@@@@@@@@È]€ÀµÜ ©@@@@@@@@@ ×2ÀbR@@@@@@@@@Ü\ËÝ@@@@@@@@@2 p-,&E@ ,X`Ë—//…¦Ð@ –-[fK–,Ép.&G@@@@@*O nå­k„ €@e (ØáÔSOµûï¿ßúôécÏ=÷\e®(k…T°À;ì`³fͲØÀ­vmî%ªàÍͪ!€ € € €@óçÏ·_|ÑZ·nm½{÷΢†š5ËŠ+l„ îÑ AëСƒuéÒÅÖZk­¬!òQgÖ©ðûí7?~¼½ûî»vÔQGYÓ¦M+|+cõ´ÍÞ|óÍj+sì±ÇZóæÍ« g@~ø‘×B×JàZ¡ÅY €Y,\¸Ð«éÄE¥}ûöYÔ’ßY8¹Ê¯o¾jçä*_²™Õ÷ÉÛ53ÿ\§ž4i’½ñÆvÀXÛ¶mC««W¯ž-^¼ØþùÏÚäÉ“í°ÕV[-tžš02î‹¡q×W¶ëˆ € € €@±~ýõW›2eŠ V>|¸ àѰž={–eàšzM¹ï¾ûìÓO?u7-n´ÑFvÜqÇY“&Mb%þé§ŸlРA6dÈj½´ :Ôúõë—ñòòQgƨ3,Z´È]O1b„éñÍ7߸µÞ`ƒ lÿý÷/˜@¡öÕ‚­P4xð`ÓçUb©S§Ž)p’?Jû‘?©òª™ÀµòÚ^´@  |ûí·¶ûî»ÛĉÝÚï±Çvíµ×–„'W%±rj'W9ñe=s¾O®Ø®Yoš´f\ºt©½ýöÛ.NÌ™3Çͧ §œrJhÆ ³nݺټyóì±Ç3&kXÆ Cç«Ô‘q_ »¾Jug½@@@(–€2ÑöÙgU]÷7nœéZs%”±cÇÚÁlºQ5±\wÝuîZÒÖ[o88ëׯ¿þº LSp\\%uÆÕ¶r®gæÌ™6}útûøãݳn:þðÃM׈‹Y µ¯sóµlõôÊ+¯T«~ûí·'ÛZ5•ìTúÿ‹ìe*oN×*o›²F €$ àwÛm7S6•m¶ÙÆž|òI«[·ðÿÂ9¹ª ëÿ¯ 'W…Ù¦…>¹b»Æ·]þùg›1c†»ªí¨ÏAe¾ÔÕ•+Wfµ ed{æ™glçw6Õ?jÔ(;äC\÷Ï |«I%î‹¡q×W“¶ëŠ € € €@¡:uê䮉jy…\޲ìqÄÕ‚ÖÔ†¹sçºn ?øàƒœ³ï_ýõvî¹çÆê÷~þ@IDATø”: i_ÊËêܹsÉíó…ÚWKy»äÒ6­é÷»Ô²ß~û¥â}•üÿ"–Šœµð¿zW$#+… €@~N:餪 µ7ÞØžþykÔ¨Q~Q+'W@e8š“«Âl´BŸ\±]ãÛ®/½ôR^RóëÎ;u×pôÑG»ÆŽ9ÒN;í4»í¶Ûâk|‰×÷Åиë+q>š‡ € € €%(0fÌûâ‹/[6uêTS¶­îÝ»N6â—_~±SO=Õîºë®°É2—:3jE ßûjQVª€ U÷®~…À5?†!-@àZ´S € P[o½Õzè!·ì5ÖXíµhÑ¢(m)Õ…rr•Û–áä*7¿R›íZª[&¹]Gu”}ôÑGvå•Wº·ß~»µoßÞÝ-›÷ÕRX¿|¶á·ß~sÝþ¦.c“M6±Ž;¦æ}•üÿ"–Šœ•ÀµŠÜ¬¬ €@¹ Œ7ÎÎ:묪ÕP`ƒ¾ô³prULýø—ÍÉUü¦A5òäŠí´²Þ¡C;ýôÓM©ó¿þúk×­§Œã*ƒ6]$ÒÅ<•óÎ;ÏvÜqG÷ˆk¥TOÜCã®Ï³â«'Á3 € € €ñ x7ñ©æyóæÙ¶ÛnkŸþyü *BM›6 ]êÊ•+Ý5¦Ð‰|FʧwïÞöÉ'Ÿ$íÙ³§ÝqǶÑFÙŒ3ìž{î1Ý©. ëׯŸ4m꛸ë\¸p¡ÝÿývÓM7Ù¬Y³Üâš5kfL]t{ÕUWU­ó!C\Ö¼ªEz‘¯}µH«SО÷Þ{öÍ7ßT[æ>ûìSmr¨äÿ¹ÉTÞܵ+o•X#ü&Mšd7ß|shŠ^ÿ9ŠV`þüùv衇š²È¨(ˆA]È»èäJ'¾º£KÙàJ¡pr•ýVàä*{»LçÔÉÕwÞi=ö˜MŸ>ÝÖ_ýL«H{z¶kÚTiM¨n^o¼ñF»÷Þ{MÝy>ýôÓiÍ—îDµk×¶xÀ¼Ï2Åxâ‰UŸÿéÖSÓéb¨ºHM½+XXu\¬X±Â>üðC;ûì³MYFUÂ.°Æ]Ÿ–§ ¬7Üpƒ©kîƒ:È®¹æ»ï¾û4Š‚ € € €yXwÝu­k×®y¨¹8Un·Ýv¡ Öµ nݺ…N“:RA`=zô¨´Ö A×K˦›njõêÕ³Í7ßÜ®½öZ›3gŽMœ8Ñ”í?¨ÄU§®e=ÚŽ8âkÕª•»ß ZÓ²¸FIØk¯½’é]>öÕ"­JÁ;|øpßeÒM¨/Kl+íÿEl0Rk²!YêK—.µ—_~ÙeÉØpà ­K—.îuP÷YÕk` PSN9ÅæÎë^«V-4¡çR*œ\•ÒÖÈ®-œ\eç–ë\ù>¹b»æº…ÂçïÓ§OøYŒ]k­µ’2lªûЫ¯¾:‹šJw–¸.†zkg}\`õTyF@@@ 8º6R)eË-·´~ýú®ŽnØÓo–ée…?ì°Ã\0Zê<úÓŸ¬aÆ©ƒ­yóæ¶ÕV[Uî ˆ³Î“N:Ézõêe<òˆ-_¾Ü[DÕ³®…R’Ö\sÍäEz÷¾Z¤Õ(Êbýb tcr÷î݋Ҟš´ÐJúQ“¶[:ëJW¡é(e8‚ ¦L™bŠt_}õÕݳ²¬¶Új¦F–-[–ôÐ?í°/é.þ«¯¾²©S§ºej¹Þ2e¯Ô³?ÿü³{(›÷Zݵoß>ÝE”ätZ¥¿ýì³Ï\êÙ™3gÚ»ï¾ëî&ÐzS@rÐÿaÆU5ù裶m¶Ù¦ê}©¼(µ“«»ï¾Û—&Ó ¾•Tè@N®Š·aóyrÅvÍïvÕ÷y}ÇÖ÷Ï8‹º#½îºë\Æ/Õ{Ùe—™>ÿÛ¶mçbŠRW¶Cu‘Õ¯Ä]Ÿ.°Þu×]~‹røÀHÃ@@@b˸Ë \‰º‚\gu\ö3¯WýnûÏþ3ãn3•ýüøñ¾kЦMßáQã¬sÕªU¡‹ãºJužRÚßãÜW«¯ie™={¶Mž<¹ÚÊ)ÙC:uª g@¼¥tüÄ»fÔFàZö×^{ÍýДnÕê G_:”Â5—¢ˆû7ß|3£*ÔuÕ€2š§Ô&~饗lÿý÷/µf•d{ £~íuÆlP’m¤QÔtýë_.ÈYJ~ñÅ—$I)}9ää*ó]„“«ÌÍâœ#_ÇÛ5έ\—.4Ƹ¦`Æ3Ï<Ó.ºè"·`Ý¡ª.+Õ½C¹—8/†Ê"îú¸ÀZî{íG@@(wRëm$WOÝøxÅWØ%—\bÓ¦MsÁ,;v´ºu3ÿY~ìØ±ÍñËp8qˆ8댺A·E‹ Kæ¥ô»O©”8÷ÕRY§|·ÃïÆq-sß}÷Í÷¢©ÿwJûÁFýC t>ÿhS{¥@¢C9Ä–,Y’Óºç:N gæ’о¡Àƃ>ØU–j LI#Ò8 0}út{òÉ'«–¤/ºíÚµ«z_J/JñäJŸu'NtYOüñG»à‚ ²ºPJÎùj 'Wù’M¯Þ|\±]ÓóÏuª|m¿3Î8Ã7n\Õ¼¡C‡ÚâÅ‹«Þ—ë‹8/†Ê îú¸ÀZ®{íF@@@ ´¤·:wîœõujõ8TÖ_ý Q¡Ãã¬3ªg2®…nŠ’ǾZ2+“ç† >¼Úä·çž{VÎH_ óÐîôë®±Sî¸ãŽvÓM7ÙüùómÒ¤I¦ lQAe VP75ÿùϲv»ð ]—™Ÿþ¹½õÖ[îÇûÔÊ”ácçw¶®]»š¾,¨ßñr/êîTÝ+釽¯¿þÚFU•­¨Ü×-×ö+(òž{î±k®¹ÆæÌ™SU]%üZµ2¼@ ‚lêÍ+ýû÷÷^òœ†€wr•Ƥ5~N®*s`»–÷vUК‚×.½ôR·"ú¾¦.,Ï9眲^±8/† "îú¸ÀZÖ»G@@@ #%vÐMÏ^Q×~çwž5oÞÜTRωm«aqÖ©^Å‚JÆ -l|Ð|QÃËmF­ãËG`Ñ¢Eöúë¯WkpÏž=-êæØj31’\KâˆçÍFmd‰Á}ô‘m±Å‘ÁT?ü°uïÞÝN>ùä¬rÀ$Íwâ‰'º»>÷ÜsÖ»wïÄAeÿºS§Nvã7V­Ç³Ï>k©U#+üŲeËL‹ÊD¡‡2¯,X° ÂךÕC 2>ýôS{ä‘GªVfÓM7­ˆàâªâEÉprU2›"Ö†°]cå,Ze:ð×ÔÝ sÖYgå¥ B]ä‹ób¨Lâ®/ìj¾.°j=( € € € PxÛo¿ÝæÍ›—´`%ú(Õ££$­Hšoâ¬sõÕW\j¾²­•Û6 bDÙ ¼ôÒKöË/¿Tk÷~ûíWm@ 3×2óÊjêÍ7ßÜ6Þxcûä“O"ç?óÌ3m›m¶±nݺEN5Áþû֨Q£ѧOŸ(šŠ¯tÀß~û­eÛŸ}ŰB”¡€2n®Zµªªåûì³OÕk^ §'Wqj–N]l×ÒÙ¹´¤eË–¶Ùf›ÙÔ©S]5º!áwÞ1etŽ»ê"_œCew}ŸÀ÷¶¤>@@@â è7eÃ=z´©‡$ݘœXÆŒãè\áÂ…¦Ìën¸¡ëFrï½÷¶zõê%NžôZ¿ý<ñĦޛ¾øâ ûùçŸM¿ù©JeúQòŒÚµk'͓͛Y³fÙ3Ï?tm¬]»vÖ±cG÷Ðñvcf&mÉe_M\Î?üàœÔn}¾ýùÏNíz{ñÅÝs³fͬGvØa‡%Mã÷FÉZt½úê«®Î}÷Ý·Úd'N´>øÀõŠ÷Í7߸íÑ¥K÷yÙ¾}ûœ>+ýz΋¡jSÜõÕ´ ¬qmWêA@@jºÀo¿ýfS¦Lq×H1nܸªßi¤á®©W¦~ýúÙ»ï¾HÖ¢E »ä’KÌ/Ðë¾ûî³Øüùóç×õÝ­k ™ÀŒ?Þu}§€µÉ“'WÍ®ë Qk2ÐÍwêHÁ?Ž;î8L4ƒn„½ùæ›]|AÐ4aÃãØW•”AŸo¾ù¦©÷7êz×ð4Î \ÓïRú\¸ÿþû“šôÖ[où®i^¢½ñÆî©ê÷@(021`lÒ¤IvÆgøvåé-L]ßrË-Ö·o_oPÚÏ+W®tÇrê ºF½Á¤NzÏÿ s=òÿ"i·àMŠk) ùzÛ Aƒ´«V´ô¡‡ê¾àÕ­›ý&JÍú>í•á„Zך¸¦L}ú!UiwõЗE+(@' ®•áNL“k”ÀO?ýäî–ñVZŸaºÃ$¥'Xœ\qr•º/s1.U$·÷¹œ4—ÒÉ¿§  ×]w»È¡“ú ¢ ²SN9ÅÝÜ´iÓ ÉÒ®ÌÈW^y¥=ôÐC¦Iüо—ë®IÝ1˜Ï¢‹Ì·ÞzkÕ"¸¦¶Q@@@@ x=ö˜»ÑMYºÙM×<ÂÉÔÒGyİ(³LXQ='Ÿ|²Ë¦¦àxôïß߆6«§ñvÛm7¼¢z‚Ê“O>é‚K”¹MxA Aó$W žnNV¶)ͯ÷ù¾N’¸üR}­}B7BΘ1ÃÙÈU¿õæZž}öÙÐz0©å(@Hû‹~gX¶l™{Öu,eÎK-qשOƒÊ÷ßï*´·xñb¼§g]ÇSF¿8²-;®á:>ÔkÚ«L‰é}>(ëb®ké~~(«˜â 8àX–é.®}U¸¯¿þºûÐç> ¼àÌ #í£j£zI-^…€U·>_u).ª÷Åœ{î¹vÛm·E~ž)Cž2»=õÔS6dÈŒ’)¸Îoðë&”ÿæö þ_¤îé¼È>**¬VÆUÈ4hL?Øzê©vÇwT«+Ý©ËL}Ÿn=å8]MÈ,—¸]®¹æšÄ·¼F ¬ôåvàÀvÑE™‚[jBÑ]"‰ Z‹û3»X'Xœ\qråw s1ÎO%ûa™œ4—òÉ¿ÒÝŸ~úé¾Õütô¹yà 7˜î<Ö]Å{íµ—ßd¡ÃôwõÕW»»š£nzд©Ù1C+ÏräÎ;ïì²${A{Ó¦MsÆóÑÍ@–ML{¶¸/†Æ]ŸV¤Ò/°¦½±˜°äÎ9çw—uçÎK¾­4@@ æ è&û“N:)2SW9 {ì±i'LÐ5 ]ãP¤LÊå—_n{챇ërðàƒ¶ÿþ÷¿iÏ®ë¿gŸ}¶ëé@]úݨ›³-ºþrÕUWe;{ÅΧ.O;í´ØÖOpçwžé:HXQp~E]Ñ&–¸ëT7™Êœ¥ìZAEÝÚuÔQ¾£Ï:ë,×ŦïȨàLµ_½¤eZ¢2p…Õ—í燲&j¿QWP‰k_UÖ2ÝœnQpåAä´¦:¼®NuónPÏ"~ËÒ~¨z•92“¢®—uM]ÙÜÖYg´f j—_àÿ/Ì]¯çÿEZ»ý× ´+d“9MY²:tèࢄ³ifê2SßgS'ó €ùøõ×_ÝJ¹¬»$ô#]£Fò½Ø¢Ö?zôè¤åÿßÿý_Òû\ßã‹“«áio6N®¸—öÎ0a&'Í¥xò¯;ݰ|Ï=÷¸”á«8XwösÌ1î¼V­ZN—:Béã?þøŒ.§Ö‘÷Íš5sÝ ª}^yíµ×\÷¨ÞûRŽûbhÜõɯ&\`-õý„öe& ;’¬«‹Ÿ—^z©ëÆ8³˜@@ü (”t¾}ñÅ[&çéùo]á–àu‹—Í÷ÙgÓïy~™}¢êSF#]_‘=¥ü”L¿‡è&MÝ@™KQ¯L*qש-ïºë.ûûßÿîêΦk¯½vÉ­)±ŒA•Åί([œ~Ë *¹Üt–Ë燶º.¥¤rúË_þb š *^àZÐx¿áê¢x›m¶1eœË¦|õÕW.ÀXÉÒ)~×àõ?Nmȥ䲽ù‘‹<ó–š@íRkPMiOýúõÓZUEF6,­i™¨$¥ÿÕ ö†nh—]vYE§OÍàÓ±cÇØ6¥N°tWPT:îlO°ôe;Ó; ½•ÖÉUTš}oÚB=çûä*Ó;‚¼õöN®¼÷QÏù<¹Êv{ëäªk×®Yyã¢Ö»ÒÇçk»z'ÿùÞ?•nÿî»ïÎ*hÍÛ¶ ,p@¼÷QÏ×_½»¨àwç²îˆÔ]ͺSvï½÷v]¯GÕ÷xušX”n¿Š.†žp ¶ùæ›GÞµ>ºÀw}Z¦.°ê¡Í6Û,ô®à°ö•ÃÖ°ö3®|ô}äÞ{ïµöíÛ»,ê⃂ € €@© (ó—®ïé{«zš3gN©51«ö(Û˜®µ0 *;P:5iÒÄtŽ›š+h^õH’´–éüa°Ôí¨Î‰/¸à‚¬‡Ž>úhT¥ë*qß|äQÃûôéãºh¼ñÆ­W¯^Y7yæÌ™®kØ\ƒÖÔ/p-î:uª²*êšM¶E¿ù”jQf°C9Äe“K ZkݺµûÍ^RšNÝXêxÒöO-[l±Eê ¬Þ¯µÖZ¶õÖ[ÛFm”VתúüU7˜A%®}U%$«Á6Ùd“ Å¹á {øá‡C§ñ×ôùªëÄÊÔÕ®]»Ðyt½4Û 5¯âG}ÔpU¼1R§Óµc¿^àøaÆÿ‹Ô½…÷Qd\‹ÊÓx£é {X×4Z´~XÑݶm[ëÖ­[žZCµ €@é (á /t_€•Ê\:A¨”¢“Ÿ>ø iuâ\Ó‰Óá‡î{’"?í¸ãŽ. ²Ò‰«‹¾§Ÿ~ºÚ]/qœ`éäJª-Zäþï…݉$ïäêä“ONrñÞx'Wº€£€ÔÀ?oº¨g\íºë®öý÷ß»Leô *éÜu“xrµÿþû›Nâ3kÖ¬ j-Ž`\qĶï¾û.G#²9¹Òñ§ŵ¿¤ž¨-LÓø£õÖ6*º—X2_ã‚î"ÕŸyóæÙçŸn7ÝtSÆTô¬S§Nîb¡RŸ€&¶¿Ð¯3Ý®:ù/Åý3ÑM]bì¶Ûnî³^û‡Öñ±Ç3j†•—_~Ù] Jů»ýöÝX¢;Z;ì°¤Å(VÙn½õÖ¤áù|£ R‰åÃ?L|[²¯½‹¡q4PXã®Oíò.°æÒÆR¾ÀšËz1où(³­.Žëb®¾ï)KñV[mU>+@K@@j„€n82dˆûîªîö”I*Û›dKL7¹yE7œí¹çžÞ[ßgª]qÅ.ÈG7ë÷¾çŸÞýæ§ìñQE7MiþO<Ѭ¤;¿®7êú«ß ˉ]Ú)àb‡vˆjFÒxݨ¦‡Jÿþý]pT¹\³HZ‘˜ß(¨H]sÖ¾¡¬Ù™]‡TP˜Îù´ýuT¿u «^ üJ÷îÝÝà¸ëT»úöí뮯èuX”à×…ªn&,Õ¢2ýÖI¿“(cXâoS¦‡æùç?ÿiêê×+¹®£lu=R¿‘x=ªé:©2Ý)€6¬¼÷Þ{n>¿iâÚW³àÅ-h+Ž!—nˆ½ßVô›•*ªWÝxFý.±é¦›Ú_ÿúW7Ÿ®©*XZrRw QEÆú<ôûÌôæ nKüLõ¦Õ3ÿ/Ìý¯àÿEâ^Áë(×¢„ò4^´ú±]¾Ê*Vô¬>DýVOãL Ìj‹¢˜õc¿êÔ©cêÖ¨iÓ¦.²ºgÏžIÿ¼ãX¶WÇ×_m#GŽt?déGñÕV[Íu“¢/ fй|ý® -_ÿu ˆrýsTÀ‰ºxmРA¾›AýÔ(ÝÁ£.ît‡¾ôéÐû[Îú2ŸÈGàZ)œ`qrÅÉãòÿé”éIs©žü{RÛm·ûžç½÷ž ä.Ü=ñÄÞ ßg]œ û¾|þùç»ÿ%©3ë»ì+¯¼bÞE½ÄñJ­¯nNtQZ" ÎwiÞ¼yÒ"ârMª0Oo⾺ñÆÇ~Á¶Ò/°æiÓRm‰ èü\ÕzôèÑÃ}?>à€Üùy‰6™f!€ €Ô@eÒbzè&R]×Õ ˜aA¥Î€·úê«» —ÄÀ0eåQÏcÆŒq”ô»RPñæß~ûí«&ñæíµ×\ïAó+èI¿[Ee+ò ªá ]KÑulתÃÉ6›À5ý®¨¡Ëã?h¬›™ƒnþöêÈGº‘Ú+ê9ìꫯöÞ&=ë«l{éHª¨@ot£ëý÷ßï»´k¯½6ôwou+š¸¦ Âl‹¶™‚äÕµæškºýC1ÿú׿«Ÿ:ujà¸ÔÙõèf`]Ë \Ó禌¶Ýv[w£²~ïóŽ/p-±^ Óç˜/Õõ /¸o}.é¡›“ü¦ÿÂÊܹsÝÍûZ‡ â×ã‰Ú—N†Eþ_˜»NÅÿ‹ ½‹ážkžDž…­T‘x`µ …Ôæ(HL_h¬Ì5….'Nti9õtjf” ¶èèŒ3ΰãŽ;ÎÒí5¨. ÿä“Oܺ(¯/Þ~EQç šKçn¿ù£†)í«þ‘*Ú>¨ ªC_*þñ¸È÷8Ö=ª]ŒG & èB‡2ïè¡»'t'…NÐôCt9e~I,-[¶Ìùs¾N°8¹úßVå䊋q‰Çw>^çrÒ\Jû§gsÖYgy/“žuc‚¾7¿ÿþû¡Y õ]Mß™ýŠæÕ÷8¿¢,t~Ak‰Ó*hZ]ô)_ÐÅáÄésy­B‹þWèÆ‰u×]7qpɽÎÇÅи/ØÖ«WÏ*õkÉí4¨ o¼ñ†é¡»œu®Ù º X € €@£G6=tÓY¿~ýì/ù‹û›Æ¬%5‰®©„eIK ZKœVÁ<ÊîÖ¥§²n¥­xu(Cù^{íå’+xÃRŸ•=>*p-jRëô{G~õ–û0\Êo ê˜Q†C¿¢Ä%½{÷öU5L×òXæý†®sól‹>?‚ŽÕ©ë§7ÜpC`bœLºÏŒk_ªç ƒ2a*àUE×8µŽú,“}Ðõܰz¨Þx5jäêLý£k€Êº¦ë¸ê¾:¬è¦á À5ä7®ÚìÚ't-4ª„­ƒæåÿE” ãkŠ@íš²¢¥ºžÊ¤ô0µÍºkáÐCµ8ú6O­;轺ÓÛ Ñ<Þ?Ü é‡OŸ>ÝEú«{¸7ß|3qTF¯µ¾2R ŸúË Ó´ÙvÖ(Âé. ®]»šº‹ kƒêY¶l™)3ˆ"ÕéMAü¨‹Mu»¼Þzë™ÒÍ«›8ež(§’¸¦»er)q`ymÈö+“«  -»TO®È­;BubµóÎ;Û3Ï<ã²0éó>—“«Ô Ïß;¹R½¨–‘©N®ÂڟΟ°ùµßG•¨Ĩù5>Ž:ÒYNºÓäº]£ÖÉ;ùÏ÷þ©;ÓŽ9æ×=hbõT Ì +ê6¨\uÕUA£Üÿ’À‘ #Ž<òHSWù.~æéìçùnõ#€@é |ñÅ®;uÿ¡n—~øa[ºtié7œ"€ €Ô(eSˆºÙBÝ*ê&å¨îàJ H=…•vÚ)l´»).l‚\çOç@$¹f½‹r[ǸÆéwA]KN÷‘Úû‰Ú¡ß>Ó?ëÖ¥à—o!ê)…m¨À¦ ß~t¥Œ‡QÅ˲¦ãªU«VQ“ŽêiH½Ÿ…uUœÎñï-<®}5¬õ,¡’½ 5oÙÊ,©@fÅ?(€Í¯„Õ«à°ß˜¼ú.¸àÓuå°öÛŠ’úø}nušºœ°uд¹~Þç::ûK¥ü¿HÝ6¼/-2®•ÀöPU¥¹|à"[3jÔ(—Ö25ó@äŒYL €eƒÓ „_Ñ?iuÏ©qÕ}hP°ˆRlêÉ!C†¸ÌH~u ›4i’üñ®þ iò=\Ý¢Ê!/£©mùôÓOÝ3Æ su¤Žç=Ä#°|ùr÷ÅW_~Õ¥›‚|Õ›¾à§sBO+²«%5pMwåäRâ:ÁÒÿ¥\N°Ò=¹zòÉ'}W7/ËÞŒQ_ü½é¢žÃê‰:¹ «;¬ÞLN®îû½;…°. ur¥ÿW~¥N®Âî"MçäJëTÒÙ_¼“+¿“Ì zS‡‡mËÔi ñ>×íª6†­S¡öOÝtrª£º…+A!fΜiú>æWˆ¦®JJ©ø®©Ëì°¢‹|©ÿS¦÷;¼ µaó%ŽS–P ”¦€î(VÈzèœ]÷ú~¬‹Ûd/ÍmF«@@š( sSu§GÆ MÑõ½u÷Ýw½fQl«¨kÎQß¹•q.¬ä:¿’+¤S¢Ö#ªŽ\çª?ñº~Ê)§¤3ià4—\r‰é‘nÑ5ZõE‰G ¶¡®½•Ö­[J®ýP½‡éF²\ö¨ã_ K8εò¤†Çð&ì³ G¦ µlJX½éÖ§€Bõ ¡ä8AeÚ´iA£lĈÕÆé·³ D©G­CÔöæÿEª(ï+U v¥®X¹­—úWŽú‘ß['¥µ¼æšk¼·yyV¶ ­)ŠXÙØ=~üx×ouXæ8}‰S6]4O·\ýõ¶Í6Ûø­éCZ9Ô7µ~T ú‘2ÝeM§ìj»ì²Kµ 5eDR𠺜;v¬ûBö%Dë®î( e¤¹õÖ[]—oº«E™¿Ü¡à¶R,ʘ”XÒ¹C$qúÔ×q`yæ™.ÝtØg[ê²ßG}ÙÖ´œ\%Š…¿öN®Â¦âä*Lçã¢N£jÈuþ¨ú3ŸëI³–Ç:åºf²Þú–ñ,è"ˆ¾ÓùjiٹܙIÛ3™VÝ_§Þ…¸¦‹|Ê@šî#õÿÚ§‹´éίé,GAÒPæ e°P6uãrÈ!öàƒÚ÷ß_ú§… € €5F@Áʬ›.ô½µoß¾îfå… VœAº0A+5Ð5 úÊyx%îå¼=²i{±·¡³Ìš5+°éé^;Tàš~Ûîß¿`]qhÓ¦M`U¥vü§ÓfàÊÄ4"¬—-"è¹~WTR¡Ô²í¶Ûº^yR‡çã}Ôç}Ô2£æ/µý%j}_¹„ƒ—ȶÕüÊ¡º°.޼æ0ÀÔgPfoºlžþùgSÚÓ Œ {챇=ñÄIw»4iÒÄhÊ®vË-·ø.V|ºSF?¬ë¤#¬èG3¿® ä¤ ŒÃ;,ivª\zé¥.X%iDo†j~]³)³Äk¯½f;v¬ª]™bÔ•©²<ùežÓƒªkøðáUóðÂ_@WÒ9üçfh%(;D\E™õC¾úrܽ{wS¿ózlµÕVÕ‚âZn&õ¤~Öæ¸ç V&ëí´œ\e&§“«°»‚8¹Ê̳ܧ.…“æDÃl÷ÏÄ:‚^/Z´È&L˜ànÐMa\AA³q\| j_>†ëxÖ÷ëÄÿaë­6û"_>¨ó 0Òù¥f ø›f#²dÉ{ê©§ÜCwëf1ïûñöÛoï¾3gSo9Í#ƒ¨ ·å´>´@(%ýVGQFðÇÜ=tcW·nÝ’¾·ÿDZìBÔ¡Ör)¹ÎŸË²™Jøúë¯}ÛõÖ3èFYo|1žù ÈL]ÝR‡• ^% k©EÖ…*¹në\ç/Ôz²\+¡}`Ýu×uM ‚ŠJã© °£Ž:ÊÆŒ㾰ǹ÷Þ{¯ûq0¨Î›nº))h-q:E’+›~E?¾Ý|óÍ¡)wÏ?ÿ|»òÊ+«Í®“elSàIjQ×€ ˜Sv4Ç…u¥–:¯ß{e“S®~嬳ÎJ Zó¦Qán»íf#GŽô%=++Š2ÔuîÜ9i8oþ{w%IQ'8tE`œA9üÃ%¸«( r "° Š‚‹ìpx°(°x³,È9ã ¸‹€ »"ç¢Èé3ÜŠ¨ (‚€r=„Çß_®ÙÖTWfewgwgeñ^¿ªÊŒŒŒü"»ª2ê—ó ÄÈt<ðÀü ½"Pƒ@ >wîÜì/Š‹‘cÔÆÉîäèé"¦rm´ ,_˜GÖÒ.®FæÕöÜM¸hî4íùÙYFþ<¾‡]vÙeéÒK/;ëÞ~ûí©jPsÑÔ§wß}w^ü°ÇÅ[lز&,ˆzu®[êªã+׆¾¯ñT-=úòë÷9sæ¤iÓ¦e7\uß(Ö6Ÿ8nÿSmkUÇC€´Y nÖ½öÚk³¿Ã?/¦tó×vð×j£¬¿ ˜kùÜsÏ͆¸-{CÍ÷üÕ¯~5­¼òÊéSŸúT¾hÄý¢Æ«®ÅÈceéÑG¶ºlªŽ‰ \ûÕ¯~5¬nù‚®ûÁ~Zh¡2ñ?>óÌ3Cc :É÷ã‘jë®»núЇ>”vÜqÇ.>î$ìLc}qÕ©Ù®çeß\\½­é⪉Í£9?=öØÔë;a´ftÌž=;Év$­ûÜsÏõÌï1U}QŠ›?þýßÿ½hõÐòU÷OúÓÐëñzòì³Ï¦çŸ~¾âû®Í—Ù ¦¬@ŒÀûÁ~0»¡£ßWSÉ @€Lº@ü–ß[wÙe—´ôÒKOz}T`¸@•n¼ñÆáZB`â¦×¢Àµè<üðÃÓÙgŸ=€G¦Ê!p饗ö„ˆßçb¶î}Ý1ëIwŠ©«bß­’’Ï‹á&–Œ\@àÚÈÍ&t‹×¼æ5éüóÏOoûÛS•;>ûÙϦøpá”G“úýh£‰UIýòõÚOÙáýÊ«R§ªyÊ‚F¢ n»í¶ªEÉG€À8,¸à‚Y Ú>ûì“Ö^{íqØÃÄïÙ)ac:»^Mr5µÁØÆÅÕø¶Ó \\5õ¢y¤çg´fÙõñ]6¦_iŠ€¯^©×pïùN>ùätðÁ÷}ï½õÖ[SL‹:Þ©{»¸=îè“ P$S€Æ÷ã˜~yF.:Ë  @€Ú'×ù[l±Eö½5FYÍuûT&÷ˆ^ùÊW¦‰¾Wúþ÷¿Ÿ Ü£9÷Jäý$"*¿km;ÑË»§éÞÿ9眓﫯¾z÷*¯.¿¹}ík_ëYËýöÛ¯gŸëE]Ô3ÿ?üÃ?ô\>ú¼˜ ­<¹ÇøÒÉݽ½WxÓ›Þ”N=õÔJ_âcª¥wÞ9õšŠ³ß¾b:¢ºFèhöä“O«Î=÷Ü3lY¾à‰'žÈŸŽûcÑÈcÙq o½ÖZk%ó^EѶS]`ÆŒéÀL÷Þ{oúÆ7¾ÑŠ µhÓÕV[mXÓ–ÐËܵ êV×f^6\ÀÅU= WE)ïŒ+Zߔθ&^4æüŒ³ë¯¿¾ˆ{ÔA£ \‹ïÿùŸÿYXŸ|EŒp<©{`£­M„úð} RëðÚ[2â.ßÝwß=Ý|óÍiîܹÙõ¦ µ©ÐòŽ‘ 0Xq#òG>ò‘,è#ú_bJЩ´#85!ÅÈwE)úJ.¹ä’ž«ãz9f€ˆß$Ç;5Åj¼³åßÿýsXñ¾T–â\ÿØÇ>VèY¶­uã#7xWyˆß{ ¨³ùæ›§Ï|æ3=+×kÆ“|j람ãÂ*Ç8Ž»*ÚçÅ…'ã$ pmœ`ë.ö=ïyO:ôÐC+_#â7‚;F’b„Ÿ~©×{¯m"À¤,õúá¸l¤“îQ'ÊÊëº~Á{1%aÑ߬Y³²»…:è ìÇÏÓO?=Z6‚¯»îºtØa‡µz¶'0åbêᘾ-‚[ã}p©¥–j•A_÷{æC=4êct5jºIÙÐÅÕIJ·áâj"/šÇóüŒ»y»§Âì<æÍ›×ùrèùYg•®¼òÊ¡×ÝOŠ:LßúÖ·ö †ÛÿýKƒ×Ž9昑º×–_~ù‰Ø­}t RkWÕ½l¹@ŒÌ»×^{¥;î¸#ň‘k¬±FËØá @€ ¢@ü´ï¾û¦»îº+Å`½nàÄãŠ:÷û=­èƺüxû­ïW~^NÑcÕíWYe•¢"²å;í´SúÁ~0_žè—YsÍ5Ó-·Ü2ßòîUëÛ•åígÕ½ß&¼.;ž²u½ê^–¿¬o­WYù²:Ë|õ«_;ì1~¯>÷Üs‡-oâ‚M6Ù$m³Í6¥U»üòËSô1Æ{ÚXR™”Ûo}¿}?÷Üs•ºúík$õ(Ë;^7†Æ Þq3ß /¼PHòãÿ8EŸowŠÏ£3Î8£gqô[G[w§ 6Ø çèlÝùº_—ÙDÞ~ïqýÖ÷+¿»>ݯ«nïó¢[ÎëºLZ·è8–wÀd_Ï<ó̾{ùÝï~—MÒ7cG†iÓ¦u¼êý´jàÚB -Ô»€¿,í5"[Ì#]ôæ8‘keõˆ»×#2;î’_ø_Œé£s£ß{ÊøÖdüKÝë^—~ô£ íèÚk¯Mo~ó›‡^äI~U4*S”•_`]xá…©ßôyEû.z¿Îó÷[Ÿç+zÌ/®ªÜ}Y¶¯²uÝû.Ë;ÞW'tR*vßÅUwK ]Öv¹ãâê†nè\4ßó茋ÿ‹˜¢=OÑ÷á8ýú׿Îõ|¬Z‡Ø¸,oÙ…hÝÍ=¤ca~ñ_÷ù»Xd‘E²»«‹î‹©G#p}mV£¸ à _øB:üðÃ;j8üiŒþÖ+EgÄûßÿþ¬C¢×ú|Ù'>ñ‰ìð¸ $:#¢=~ö³Ÿeûýá˜g+|,kÛÂz¬è\Ûxã{äŒEe&£é`­»¼*¬1u­D  qwï?øÁ7J-½ôÒM¨’: @€&#ÇkŸûÜçRÜ´ÛÆÔo– ^³u:MÏ™çéW~ž¯èñ÷¿ÿ}Ѫù–¯ºêªó½î~õˆþ‘ÿ÷ÿþ_ö÷›ßü&U½Ék$ÇP–·l]w}›òºÌ¿l]¯ú—ô—&ÕYæŠ+®XZ…˜%,F*{ãߘÝ|£.ÞyçYðWô6)yä‘)êWÖ_›ë¬³Nvókô×å}—ùqÄ ßþö·Óí·ßžŽ:ꨴð 竆Ëü#S¿õC<‰þÖ8ϦOŸ^㯋ËÎDzu-áÿž•ÕùñÇïÎ^Ûë¯ýë)Ê?ñÄÓK,1_¹_|qÚ~ûíÓÿøÇù–Gðaô{õ ƈö½úèG;Mh™MTÌçÅÈÎù2ϲuó^4R६•J ü×ýWöX˜¡cEÕ ³|“Æê5Z¾>«–ÙïM¶WàZÙôvñ%þî»ïî¬Ê¸=/û Q<⋹D€Àø D€h«ÅÅË¿þë¿¶>h-$;ƒdâu¯»9byÕXñÃfYÊ/°b*êéNqÓæí¹çžÃ¾ØGÞ~_û­ïÞ_÷ëüâª{y¯×ePeëºË*«óx_\ÅÔ#<Ò]¥W›m¶Ù°Ï߸¸Š š¢é]\ £ÌT팋ѭ¢Sn¹å–KÑÑ/h- /;ºkS–·l]ÝíÚ]¯^¯ãâ¿îó3ö£-»ì²½v™-‹`ÑàÝu×]³6ˆïŽý‚ÖbÇ~¸°ÌC9¤ï{cl1½{|'ŒQ>ßõ®weÿo…w¬(k¿Žl}Ÿv®ÅûÀ ¦2“Ñt°Ö]^•Ö˜6 FRŽsèmo{[öÃËhê>¨m¨Þ͈;¿oºé¦ô•¯|EÐZ3šD- @€zÄìAÑïxÜqǵ6h-;n¸+K7)÷ÊwÍ5×ôZ<´¬_ùC ždß=‚Š"а_ŠžbäµÎ µè×)E/wzõ;wï+®¯ãÆÝ¢ýr#aª¨¬‰X³:]uÕU…»Š š¸q½JŠ›)cÊÖ¢tÅWdA;Eë{-¯»Ì~ý*ÑÇ¿WĹvðÁgÇýà1‚xÓR 070÷K=öXЏ" 3n*‹¾Ãø‹íWXa…7ÆÆµ{ÑyÛïÿ»ßú~õ‹õUʨë\„~ùË_Vë'?ùIË,C…ÿeÅù矟õá¾þõ¯OúЇ²s-ÎË-·ÜrØo[qãzØ­E‘½f<‰åÑ_<šÔ¯-|^¤,Ðs*~^Œæ|jó6å¿j·ùÈôØâG¾óÎ;/½å-o™ï b]‡ÑȬP”âøJêàÖkª®õ§ìËz|ÈÇtýR;ô›î³¬Œ0¸í¶Û ³Ä—ó²/ã…ZA€@%E]´tª¶J… X¦-¶Ø"»xË«œñ^Ve´±|›ÎÇüë„Nè\<ìy~+^óš×dC¼ÇóÐí|ÜgŸ}Òꫯ«†R¿/Û±~=öÊ?š'QFÌ”¥ªWèU–ª^\×TµqqDáA1¢P¦õ ÚŽ‹«¬) 6ŸŒ‹«¸(,JeŸï±M´u܉;ÚqeûÏË’µ«ß÷„èTèîXˆÎ¸¸ °ó#/7óθî»ì:óÄóªqÑñÑên×îò‹^×}~æû‰ïƒe£êFÇÜ)§œ’g¯ôçZL?¿Ì2Ë Ëû‹@¸An´)«>úèÂÍ#ð8ntˆ ìѦøÿ¿þúë‡6ùË_>,ÀyheßTí íž2»è°ê./öSµƒµ»NÑÁS£H&J ÊµðDÕÅ~ @€ D€J[Ó¡‡šÝ\}@ýÃbЏ1;)þîïþ.ýó?ÿsúïÿþïôóŸÿ<ëï»ä’KJ™Î>ûì¬víµ×În¬‹›‹ûõ9uø?ÿó?ÙˆV±}Ü 7dõšu)~ëúøÇ?žŽ8âˆÎÍû>~«èÙm·Ý ûÊ¢$nJ|Ç;Þ‘Ý7åE I¤è ŒvãÄ(§,¨%ÊYk­µ²r¢.~ÝqÇûÖq"3\wÝué[ßúVdvõÕWgý„eûßâ/£/?ïrb´®Ì#¦£Œ™ ÊRÜøý]áñÛJܸÛÙO:evÖé oxCŠ›€Ë~[îÌÏ#°«©}*qÓbœŸñ^%Å h1J[wŠÁ :SÉûÇ7¿ùÍl›üÿ?Þ?zõwï³óuôƒG¿j õè<Çê8Wc ØøýâÁ̼b¢Á‹q^®·ÞzY?\¼Åèeñº®¿§EÐtüõJ1«Ó—¾ô¥¾‰1íèw¾óaE¬±Æ™å° FÒÞ>/RÖŸ>U>/ N‹ÿ, pmOƒˆÞŽ7ÜpÃT÷Ôeñ!Xöå"¾àÄÔ$ýR¿·¨{wê7]ÝÉ'ŸœEãGð^YºõÖ[‡EP—åï^oŒœV”⤊AÑö– @ [ .bâ‹s>Å]kÄ*Ñ©1Ú4X#ù²íâjä-çâê7)¿×7¼3®®‹æ‘Ÿ™ÿ·E]çgçþ£“1u«¦ŒïƒÑ¡Ái½RÜùÂô«u‘?îìüîw¿[¸}¯2cY¼GÇwÀø[¸ß!cêƒèx‰;÷Fz.Ǿ¢ƒ3ÿ<ˆ×Ì:(Sf×ÝZwyáÙÚÖÁÚ}|^ @€ @€@=sæÌé9}\Qétt'1ª}ÙH\Ýåüô§?Mñi«­¶QàZlÓ¹}­ô \‹|x`Š ”*3Dþø-ï²Ë.Ë‚•úÍvýÜguVl–"°%\‹À¿‘9v–óîw¿»qk1b\Ü,[5E`Ú9眓eþÐ<¨(Êùüç?_µ˜,_ $žy0eLcÙÙ6evV0n¸Œ­þñÿ±sqáó8—GÒö…ÓŠÜ$nŒÝn»íJ+î·ûOúÓóeéûGçÿoŒæ6ÒÀµ¸1ö /ÌêÁžçXçjXžvÚiócÙ‹˜©­3À/úMë \+ÛwôýqÆYŸqY¾X£ŸÅûMwé4¡#moŸ)sŸ ŸÝç–׸öW‹q}Ö=²GÙüØU*_t#Rz‡vÈF婲M•<믿þЗ¥^ùcÎç~#øÄve£gÄúÑ®ÅEL[S–¥¯~õ«e«û®‹ÊcŽ9¦0_|a‰aMãËŸD€:" w£6šï‹{sŒ%pm<.°FúeÛÅUgÇÿ•á⪿eçùÖÆÎ¸º.šûKŽ<ÇHÎÏÎÒcèýø^ù‹_ü¢sqÏçqg`tˆÄ]¨ÿôOÿÔ3O¾0¦’Œ·èpí쨋õqgn¼¿n²É&•G/Ž;šcÄ×ß@IDATã¸¹áæ›oÎwSøÇùðø£éXî¾ëz¦ ­»3´îòz5ZÛ:X{£e @€ 0y1ÓE¤üq¤5‰©<˦µiyÝùãFÁo¼1 `‹‰âæÉ¢}@1ÑrË-—e)›¢¨ŒX>Z‹²2«®‹5ÊFx«ZNg¾ºŽ§ŽrºËè~ÝYïªÏû•7§Þpà é‹_übéù£Vyæ™cš© ê<mØiÿoÑ¿ÃÇÝ3stæí|¾à‚ fo1øÉæ›oÞ¹ªöç#™&ÄÉS¿¶Ìó÷ãDÔ#F Œ›˜cF¡—½¬ZHLì×}ü# \ëÞ¾êëÜ%¬º]žÏçE.áqªý—â‘5¬ÎÙÜ™:GQè\>’çï}ï{³þâ·®?®}ò“Ÿ, †‹€´øòñ¦7½©t—1’EQzç;ß™ýhؽ>Fňi•bØß¢´ÿþû§ðˆ‘6z¥8+ :ëµM÷²ø!3¾\” kƒ1…Óx^,t×ËkÚ-0kÖ¬ù×bˆô½÷Þ{LÝô ,Wý›×ÅU£‘æÔθɾhîå<šó³³œ¿ù›¿I1õÅ.»ì’"@©(EçWLïm7ƒ”}Wì,£è•UVÉ:dã;tÜí£É¥ü¦Žüýªûf”¢íbytˆŒæ»bL•Ñ™bJñHãÑÉWd>’úw–Ñù|$etæ­RÆDw°vÖÏs @€ Œøý¨û/ú¢¿"‚¾â·¥øMðÙgŸÍ—Yf™ìcªÐÃ?<Å TñeÄcäÛG¿Cl±}üÅúîiB^xálJ¼Ø>~3Ëÿ¢ÌÎ:ĬMñ{dgàJ/í@‹ £|à)°ˆ™bʘ¡)ú6bºÁ­·Þ:Y¾3ä{ßû^6JNŒQ¨oÔ?öñ<ŽëU¯zÕЮ#$F_‹¼Ý–ñ:~+Œ@º=¢¼ÑËå;éZëN1òÜ@õî>ž8Æ8o¢]:'?G:=«”ýXQNÑ_ܴٙƣÌÎòãùK^ò’ì÷êu-¦©Á?bÊÆ8÷¦OŸž:n¼ñÆé³Ÿýl6i÷ö#}=mØ]‡h³Ýwß=»6¦Äœ;wnÖùÈ#dç|þµÂ +¤ø‹ â·ãÅ_¼»¨ìuÙûGœ#íÿ÷ñ“ÿÿôm-~Ï鈣žÿÑù{OlŸ×3¯T•ó¡Ê¹£ìpÀÃÎ÷8ÿãx¢Ýçhþ^åGÿìXSŒD÷þ÷¿?É2n6¾ï¾û²c)cdÆ Œóo$)Þw:ß«bÛx\wÝuGRLÖ÷ìóbÏùñóbD.ó˜^òç7Ââ_jÆ\¼rM7Ý4›§=_üâK^)†®2ÊXüðV6f^—¨WYàYÍž=;Ï>ì1>þöoÿvØò|AcÄ”^)¾ÇpýRŒÐÑÍl°Aö!ô³Ÿý,ûÂ_ú¥øòó§—¥ 9õÔS˲¤¸³$¦Â‹y¹ËR\¬ÄÍñ¹×^{•e—uq÷Ë6Ûl3_Ùñ£–H¤lê·|úߘn·Ýv›–¸˜ˆ;Õòaˆ[l±ôàƒf_üë¨P\$Œõ+:+оlW¹¸êÕ©p×]wU¾¸Š™<ÝqÇ£îXqųbbJÁ?üáã~qŸUEŸ¿ãqqè1ÚTgŠ‹«¼ó¤syÙó²öÎ;Ó:;Âò â茋¶ŠÎ©0®£3.>Ko»í¶¬¬¼#.ãB¸³yg\t¦Å9Ù/Å÷‡‘tÆÅôñ?ÚÙû)êŒËfâëöí·ß>êθxo¨«]»M&úüìÞ¼ŽösöŠ?ßÉçÝÝwßuBÆÿj|ÏÝi§æÛ,Þ7b›Îs!Ú$–åç@<†¯÷ÎÂâÿ"¦f¸ôÒK³óõá‡Î.¨£“/‚™öÝwßaïÃÑyç^Þ!Ñ1ùÇ?þ1û^ï·qNĹßݱܹï^Ïã²Â°• ]Ÿõñ}¤3E't¿>Îüž @€4S ®áòAâ·—c=¶™U+•@Yßõž{?þøQ•k#u \{íµémo{[Vä§>õ©FO‡\çq×]–×ê-(/~`ëL¿ùÍo:_Žéù—¿üål˜ÒˆT-KU§'©8£¬¢˜Æ˜{›¯€¿¼È§×º:–ÅÝX,u¥ÐŽ¿±¦~í6aí=mÚ´a» û²;§bûª¾Ñž1Ud¯çJüÿT=ç:ˈ©ã¯jŠº»ï¸Šm£eçZÜawz•¥ünÌ¢ó¾®v-«C÷ºñ8?»÷¯£}ã»TüUIEïq>µQQ¹qŽî·ß~Ù_QžîåEçbœ‡cMÝ"xàX‹œðíû½?G…ªþïG޺ˋ2‹Òšk®™âO"@€ @€ @€&Fॳ›©½—Ë.»,N·S!‚³bT†:Rüðåõûá5FB©’Þþö·gõå‘bšÒ9¤;|ðÁ…£¿ÅŒßøÆ7º7™ïuü0µë®»Î·l¤/bdŒ²#¾Åˆeéo|cŠcé—b¤žƒ:(Å0­1"F¦E;D0DŒnpyÐZ GZ6]¿}v}owŠ!zzè¡îÅ^ 0É1²P¯äéóŸÿ|6œrþÚ#´WàÉ'ŸL§œrÊÐÆ÷Ñ¿ÿû¿zí  @€ @€ @ mF\‡‘¸âG§˜j(æÙŽ©3»ÓÕW_7­³Î:)º–_~ù#6-²È"Ùôž1šW(Õ]VÌe]5vØa馛nJ1Íd¯#­±ÆÙèa1zOáÅ4hESÅTO1]æŒ3z7ß²‹©Jxàù–÷{Ó4Å4n¸a:úè£ ³G=ã‡ÀõÖ[/ .‹¡D{MãS¢Æ´"çž{naYùŠ!îúë¯Ï_ö|Œ€”ьӳ°®…1 éyç—äE€b´Ç=÷Ü“µIÌqÞbª³˜Fm³Í6˦:) c”9'R˜Äè1+Ÿ1òÏ¡‡šíøî?O—¿ùT¦[{#@€‰ˆéRŸzê©¡]pÀ)Fë“ @€ @€ @€m™2-kÿñÿÑ·äš;wn–oÉ%—SàZ²ÔRK¥ïÿûY@Öc=6lÿ# \‹ ¥óÏ??}âŸHÇs̰²bAL£A`ñW–6ß|ótæ™gfAbeùòu1ZÙW\‘MEzÿý÷ç‹K#ð,·b´³^‚Ýÿò—¿Lñi«­¶ê¸ÓV}ë[ßÊ‚Hò©ûºË©úú­o}kÚgŸ}ªfq¾²;í´ÓF´]·]pÁ=·‰ k=i,$0îñÿ|ÖYg j9gΜ´Ë.»øŸwy; @€Àä ÄTöñ~Ÿ§¸Adûí·Ï_z$@€ @€ @€´RÀT¡ãЬ@6YiÕUWÍF6‹ÑǺÓ3Ï<Ó½¨ôuLAú¥/})]uÕU)FUiZýõ³‘Ó.¹ä’ÊAkù>VYe•tã7¦ÝvÛ­ïH±Ÿé-Ÿ*5¦2­š"8+¦ô,J¼vÄG¤k®¹&m¹å–EÙz.©A÷Þ{ïŒ?úÑÒÒK/Ý3_ cÄ·1-FN‹ýÄ~1rS8¾îu¯ËþV_}õ”ÿŲÕV[-ÅÔ¬1ÞrË-—–Xb‰#÷EžÎ© 먟2¨.ÿË'œpÂÐwÝuW:ýôÓ‡^{B€íˆÑŽ}ôÑ¡›={vßïÀC™=!@€ @€ @€ ¨€ׯ¡áb„±4ˆ©}"á¯xEöÁ@#•E QŒ€ÁdñËêJ1]hLóyÝu×¥_ýêWéÖ[oÍ{³UÙçl.¿üò¬œyóæ¥+¯¼2Ý{ï½)Fu{üñdz §˜Š°VX!m´ÑFY [E%-¾øâÙÔŸ1…ê9眓.½ôÒ#°Å¬p#³í¸ãŽiß}÷/ÐjÍ5×L·ÜrK¶,Ž9üã1ìc”±˜J3‚Ûž{]zMÚ]ï0ýÞ÷¾—î¸ãŽtÙe—e#ÂÅ”¬aÓ¦F}"P,Ž9þb”Œ­·Þ:ÛowYãñ:F´“hÀ¦›nšfÍš•MGuøá‡§vØaÂÞSÚ#éH Ð|øÞzì±ÇU4FéÝn»í†^{B€ @€ @€h«€ÀµqjÙŪ(EÕ , ̘1£(Û˜–¯´ÒJ)þêLùh]1ŠØD¦5l¿ýöËþªî7êÚ+E áXR˜ö‘|d,ÅØ–}¾øÅ/¦ï|ç;Y€lÍîºë®Ù¢- @€@;âf–˜:18F>ꨣÚqpŽ‚ @€ @€£ˆÁhŠR P# ÐS…¶§- ´H Fì ^8ûì³Ó¡‡Ú¢#œ:‡ââjê´õ ©ósr[íàƒÎFIŽZ,µÔR)Þë㉠@€ @€SYàÉ'Ÿ,<ü²u…YA€@c®5¶iTŒ¦º@Œ²ö¹Ï}nˆáCÉ‚†x2ePeëâàTràÊÎÁ²uà 8€“O>9Íž=;«IL;AkË,³Lj¦  @€ @€˜}zúä'?™} `¶™3g¦µ×^{jA èÑV½¸2ì€6ð€WÛù9y xá…¦=öØc¨G}tÚpà ‡^{B€ @€ @€©$0wîÜtË-·¤ûï¿?{î¹é…^(<üjÛh£Ò&›l’f̘‘Yd‘4kÖ¬´à‚ nc͸ÖܶQ3 |üãÏ‚×>üá§gžy&m»í¶é'?ùIZvÙe 5PÀÅUE•†œŸC“öäºë®K;î¸cÖñA«Çw\úèG?:iõ±c @€ @€L¶@ô“^pÁ•«qõÕW§øËÓ{Þóž´Øb‹å/= 0@ר±T•¦®Àn»í–¦M›–vÚi§ôÀ¤]vÙ%Í›7oê‚4øÈ]\5¸qT- ’rñ?¹'¾ûî›!/°ÀéÄOÌÞÏ'·FöN€ @€ @€ÉXtÑEG]W¼âF[µž L¾€ÀµÉo5 @€•¶ß~ûÓËyä‘é-oyK¥mdšxWonÕœŸÕ­Æ+gL Ak³gÏNë­·ÞxíF¹ @€ @€`×]wÍÐbÊÏοèOôüóϧçž{.ýéOÊóçK.¹d6]èÀ¬Š 0Ÿ€Àµù8¼ @€ÍØb‹-RüIÍpqÕܶQ³”¸æârÏ„9sæLnì @€ @€ ˜>}zzÇ;ÞѰZ©! pm"”탘2.®¦LSä:?²ÙTš @€ @€ ÐJ—¶ò¨ @€ @€ @€ @€4V@àZc›FÅ @€ @€ @€ @€ ÐNkílWGE€ @€ @€ @€ @€Æ \klÓ¨ @€ @€ @€ @€Ú) p­íê¨ @€ @€ @€ @€ ÐXkm#@€ @€ @€ @€ @€@;®µ³] @€ @€ @€ @€+ p­±M£b @€ @€ @€ @€h§€Àµv¶«£"@€ @€ @€ @€ @€@c®5¶iTŒ @€ @€ @€ @€í¸ÖÎvuT @€ @€ @€ @€h¬€ÀµÆ6Š @€ @€ @€ @€ @ ×ÚÙ®ŽŠ @€ @€ @€ @€¸ÖئQ1 @€ @€ @€ @€´S@àZ;ÛÕQ @€ @€ @€ @€ @ ±×Û4*F€ @€ @€ @€ @€v \kg»:* @€ @€ @€ @€4V@àZc›FÅ @€ @€ @€ @€ ÐNkílWGE€ @€ @€ @€ @€Æ \klÓ¨ @€ @€ @€ @€Ú) p­íê¨ @€ @€ @€ @€ ÐXkm#@€ @€ @€ @€ @€@;®µ³] @€ @€ @€ @€+ p­±M£b @€ @€ @€ @€h§€Àµv¶«£"@€ @€ @€ @€ @€@c®5¶iTŒ @€ @€ @€ @€í¸ÖÎvuT @€ @€ @€ @€h¬€ÀµÆ6Š @€ @€ @€ @€ @ ×ÚÙ®ŽŠ @€ @€ @€ @€¸ÖئQ1 @€ @€ @€ @€´S@àZ;ÛÕQ @€ @€ @€ @€ @ ±×Û4*F€ @€ @€ @€ @€v \kg»:* @€ @€ @€ @€4V@àZc›FÅ @€ @€ @€ @€ ÐNkílWGE€ @€ @€ @€ @€Æ ¼¬±5S1 @€ @€F<öØcéÅ_lD]ªVâé§Ÿ®šU> @€ @€&A@àÚ$ Û% @€ @`î½÷Þ @€ @€ê0Uh]’Ê!@€ @€ @€ @€ @€J×*1ÉD€ @€ @€ @€ @€u \«KR9 @€ @€ @€ @€ PI@àZ%&™ @€ @€ @€ @€ @ .—ÕUr @€ @€[`ÆŒiï½÷ž°ƒ¸çž{ÒòË/?aû{éKÝÃ9aØvD€ @€ @ €Àµ>@V @€ @€¦ŠÀË^ö²´øâ‹OØáž}öÙ)‚åfΜ9aû´# @€ @€š! p­í  Ї~8Ýyç ¨‰* @€SYà‘G™Ê‡ïا˜À5×\“¦M›&pmе»Ã%@€Ô)ðûßÿ^¿n Ê"@€* Üÿý•òÉT. p­ÜÇZ˜Bûí·_Š?‰ @€‰¸êª«ÒB -”vÞyç‰Ù¡½ @€´NàÔSOMñ' @€ žÀK¯ÊjL€ @€ 0è·ß~{ºï¾ûÒ¼yóýPÔŸ @€ @€Qqmh6!@€ö¬¼òÊiÖ¬Yí9 GB€´J`æÌ™­:C S`îܹÙË»ï¾;›Úi¥•Vê\í9 @€BÅ[L¿n¡Ž @€ÀD ¬µÖZ½ËÖìï%/þ9µæh @€ @€À@l»í¶é‚ .Èêzüñǧ=÷Üs ê­’ @€ @€Ô#`ªÐz•B€ @€ PQà™gž™oŠÐ<€­âæ² @€ @€ Ðk-hD‡@€ @€$y󿥧Ÿ~z¨Ê—_~yzê©§†^{B€ @€ @€@û®µ¿! @€ @ QçŸþ|õyöÙgÓÅ_<ß2/ @€ @€h·€Àµv·¯£#@€ @€4JàùçŸOçwÞ°:}ó›ß¶Ì @€ @€Ú+ p­½mëÈ @€ @€˜;wnzì±Ç†Õ뢋.JøÃ†-·€ @€ @€v \kg»:* @€ @€@#Î<óÌžõzúé§Ó\Ðs… @€ @€´O@àZûÚÔ @€ @€)ðÌ3Ïôœ&4¯ìgœ‘?õH€ @€ @€@Ë®µ¼ @€ @ )çœsNzê©§ «sñŧ|°p½ @€ @€´G@àZ{ÚÒ‘ @€ @€-pòÉ'—ÖïùçŸO§œrJi+  @€ @€h‡ÀK^üsjÇ¡8  @€ @€¦ Üyçi•UVIýº¢"Ïí·ßÞÔÃP/ @€ @€j0âZMŠ!@€ @€(8餓ú­ÅÖ¿þõ¯ÓW\Q\5 @€ @€´B@àZ+šÑA @€ @€š+ð¿ÿû¿éÄO¬\Ác=¶r^  @€ @€LkƒÙnjM€ @€3Î8#=öØc•ë{þùç§{ï½·r~  @€ @€<kƒ×fjL€ @€(cŽ9fDõ}á…Ò—¿üåm#3 @€ @€ƒ%ð’ÿœ«ÊjK€ @€ 0(sçÎM[l±Åˆ«;}úôtÏ=÷¤iÓ¦x[ @€ @€ Ð|#®5¿Ô @€ 0°sæÌUÝŸxâ‰tüñÇj[ @€ @€ Ð|#®5¿Ô @€ 0×\sMZýõG]÷%—\2Ý}÷Ýi¡…u6$@€ @€ @ ™F\kf»¨ @€ @`à;ì°1ÃÃ?œ¾öµ¯©  @€ @€ ÐL#®5³]ÔŠ @€ 0ÐW\qEÚd“MÆ| K,±DºóÎ;Ó¢‹.:æ²@€ @€ @€@sŒ¸Öœ¶P @€ @€@+^|ñÅô™Ï|¦–cyä‘GÒGQKY !@€ @€ @ 9F\kN[¨  @€ @ W_}uúèG?Zz,1 hüEš9sfzå+_Y˜?ÖÍ›7Ϩk…BV @€ @€<kƒ×fjL€ @€xû·KsæÌÉŽãÊ+¯Ln¸áÀ“ @€ @€ @ º€©B«[ÉI€ @€ @€ @€ @€5\«Q @€ @€ @€ @€ P]@àZu+9  @€ @€ @€ @€ @ k5 *‚ @€ @€ @€ @€ª \«n%' @€ @€ @€ @€Ô p­DE @€ @€ @€ @€ @€@ukÕ­ä$@€ @€ @€ @€ @€®Õ€¨ @€ @€ @€ @€¨. p­º•œ @€ @€ @€ @€ Pƒ€ÀµA€ @€ @€ @€ @€Õ®U·’“ @€ @€ @€ @€j¸V¢" @€ @€ @€ @€ @ º€ÀµêVr @€ @€ @€ @€ @€@ ×j@T @€ @€ @€ @€T¸VÝJN @€ @€ @€ @€¨A@àZ ˆŠ @€ @€ @€ @€ @€êת[ÉI€ @€ @€ @€ @€5\«Q @€ @€ @€ @€ P]@àZu+9  @€ @€ @€ @€ @ k5 *‚ @€ @€ @€ @€ª \«n%' @€ @€ @€ @€Ô p­DE @€ @€ @€ @€ @€@ukÕ­ä$@€ @€ @€ @€ @€®Õ€¨ @€ @€ @€ @€¨. p­º•œ @€ @€ @€ @€ Pƒ€ÀµA€ @€ @€ @€ @€Õ®U·’“ @€ @€ @€ @€j¸V¢" @€ @€ @€ @€ @ º€ÀµêVr @€ @€ @€ @€ @€@ ×j@T @€ @€ @€ @€T¸VÝJN @€ @€ @€ @€¨A@àZ ˆŠ @€ @€ @€ @€ @€êת[ÉI€ @€ @€ @€ @€5\«Q @€ @€ @€ @€ P]@àZu+9  @€ @€ @€ @€ @ k5 *‚ @€ @€ @€ @€ª \«n%' @€ @€ @€ @€Ô p­DE @€ @€ @€ @€ @€@ukÕ­ä$@€ @€ @€ @€ @€®Õ€¨ @€ @€ @€ @€¨. p­º•œ @€ @€ @€ @€ Pƒ€ÀµA€ @€ @€ @€ @€Õ®U·’“ @€ @€ @€ @€j¸V¢" @€ @€ @€ @€ @ º€ÀµêVr @€ @€ @€ @€ @€@ ×j@T @€ @€ @€ @€T¸VÝJN @€ @€ @€ @€¨A@àZ ˆŠ @€ @€ @€ @€ @€êת[ÉI€ @€ @€ @€ @€5\«Q @€ @€ @€ @€ P]@àZu+9  @€ @€ @€ @€ @ k5 *‚ @€ @€ @€ @€ª \«n%' @€ @€ @€ @€Ô p­DE @€ @€ @€ @€ @€@ukÕ­ä$@€ @€ @€ @€ @€®Õ€¨ @€ @€ @€ @€¨. p­º•œ @€ @€ @€ @€ Pƒ€ÀµA€ @€ @€ @€ @€Õ®U·’“ @€ @€ @€ @€j¸V¢" @€ @€ @€ @€ @ º€ÀµêVr @€ @€ @€ @€ @€@ ×j@T @€ @€ @€ @€T¸VÝJN @€ @€ @€ @€¨A@àZ ˆŠ @€ @€ @€ @€ @€êת[ÉI€ @€ @€ @€ @€5\«Q @€ @€ @€ @€ P]@àZu+9  @€ @€ @€ @€ @ k5 *‚ @€ @€ @€ @€ª \«n%' @€ @€ @€ @€Ô p­DE @€ @€ @€ @€ @€@ukÕ­ä$@€ @€ @€ @€ @€®Õ€¨ @€ @€ @€ @€¨. p­º•œ @€ @€ @€ @€ Pƒ€ÀµA€ @€ @€ @€ @€Õ®U·’“ @€ @€ @€ @€j¸V¢" @€ @€ @€ @€ @ º€ÀµêVr @€ @€ @€ @€ @€@ ×j@T @€ @€ @€ @€T¸VÝJN @€ @€ @€ @€¨A@àZ ˆŠ @€ @€ @€ @€ @€êת[ÉI€ @€ @€ @€ @€5\«Q @€ @€ @€ @€ P]@àZu+9  @€ @€ @€ @€ @ k5 *‚ @€ @€ @€ @€ª \«n%' @€ @€ @€ @€Ô p­DE @€ @€ @€ @€ @€@ukÕ­ä$@€ @€ @€ @€ @€®Õ€¨ @€ @€ @€ @€¨. p­º•œ @€ @€ @€ @€ Pƒ€ÀµA€ @€ @€ @€ @€Õ®U·’“ @€ @€ @€ @€j¸V¢" @€ @€ @€ @€ @ º€ÀµêVr @€ @€ @€ @€ @€@ ×j@T @€ @€ @€ @€T¸VÝJN @€ @€ @€ @€¨A@àZ ˆŠ @€ @€ @€ @€ @€êת[ÉI€ @€ @€ @€ @€5\«Q @€ @€ @€ @€ P]@àZu+9  @€ @€ @€ @€ @ k5 *‚ @€ @€ @€ @€ª \«n%' @€ @€ @€ @€Ô p­DE @€ @€ @€ @€ @€@ukÕ­ä$@€ @€ @€ @€ @€®Õ€¨ @€ @€ @€ @€¨. p­º•œ @€ @€ @€ @€ Pƒ€ÀµA€ @€ @€ @€ @€Õ®U·’“ @€ @€ @€ @€j¸V¢" @€ @€ @€ @€ @ º€ÀµêVr @€ @€ @€ @€ @€@ ×j@T @€ @€ @€ @€T¸VÝJN @€ @€ @€ @€¨A@àZ ˆŠ @€ @€ @€ @€ @€êת[ÉI€ @€ @€ @€ @€5\«Q @€ @€ @€ @€ P]@àZu+9  @€ @€ @€ @€ @ k5 *‚ @€ @€ @€ @€ª \«n%' @€ @€ @€ @€Ô p­DE @€ @€ @€ @€ @€@ukÕ­ä$@€ @€ @€ @€ @€®Õ€¨ @€ @€ @€ @€¨. p­º•œ @€ @€ @€ @€ Pƒ€ÀµA€ @€ @€ @€ @€Õ®U·’“ @€ @€ @€ @€j¸V¢" @€ @€ @€ @€ @ º€ÀµêVr @€ @€ @€ @€ @€@ ×j@T @€ @€ @€ @€T¸VÝJN @€ @€ @€ @€¨A@àZ ˆŠ @€ @€ @€ @€ @€êת[ÉI€ @€ @€ @€ @€5\«Q @€ @€ @€ @€ P]@àZu+9  @€ @€ @€ @€ @ k5 *‚ @€ @€ @€ @€ª \«n%' @€ @€ @€ @€Ô p­DE @€ @€ @€ @€ @€@u—UÏ*' Ðk¯½6½ï}ïká8˜bçŸ~zó›ß<ÅŽÚá @€ @€ @€v \kg»:* P(ðì³Ï¦ßþö·…ë­ @€MˆÏ0‰ @€ @€h‡€Àµv´££ @€£˜6mZš>}ú¨¶µ˜Ç<=õÔS±+û @€ @€ @€&P@àÚbÛhšÀ^{í•fϞݴj©øô§?Ž:ꨡמ @€ @€ @€íxi;ÃQ @€ @€ @€ @€ @€À \”–RO @€ @€ @€ @€´D@àZKÒa @€ @€ @€ @€ @`P® JK©' @€ @€ @€ @€Z" p­% é0 @€ @€ @€ @€ 0(×¥¥Ô“ @€ @€ @€ @€-¸Ö’†t @€ @€ @€ @€kƒÒRêI€ @€ @€ @€ @€–\kIC:  @€ @€ @€ @€ Š€ÀµAi)õ$@€ @€ @€ @€ @€@K®µ¤! @€ @€ @€ @€E@àÚ ´”z @€ @€ @€ @€ @ %×ZÒƒ @€ @€ @€ @€ƒ" pmPZJ=  @€ @€ @€ @€ Ðk-iH‡A€ @€ @€ @€ @€A¸6(-¥ž @€ @€ @€ @€h‰€Àµ–4¤Ã @€ @€ @€ÉïD[@IDAT @€ @€À \”–RO @€ @€ @€ @€´D@àZKÒa @€ @€ @€ @€ @`P® JK©' @€ @€ @€ @€Z" p­% é0 @€ @€ @€ @€ 0(×¥¥Ô“ @€ @€ @€ @€-¸Ö’†t @€ @€ @€ @€kƒÒRêI€ @€ @€ @€ @€–\kIC:  @€ @€ @€ @€ Š€ÀµAi)õ$@€ @€ @€ @€ @€@K®µ¤! @€ @€ @€ @€E@àÚ ´”z @€ @€ @€ @€ @ %×ZÒƒ @€ @€ @€ @€ƒ" pmPZJ=  @€ @€ @€ @€ Ðk-iH‡A€ @€ @€ @€ @€A¸6(-¥ž @€ @€ @€ @€h‰€Àµ–4¤Ã @€ @€ @€ @€ @€À \”–RO @€ @€ @€ @€´D@àZKÒa @€ @€ @€ @€ @`P® JK©' @€ @€ @€ @€Z" p­% é0 @€ @€ @€ @€ 0(×¥¥Ô“ @€ @€ @€ @€-¸Ö’†t @€ @€ @€ @€kƒÒRêI€ @€ @€ @€ @€–\kIC:  @€ @€ @€ @€ Š€ÀµAi)õ$@€ @€ @€ @€ @€@K®µ¤! @€ @€ @€ @€E@àÚ ´”z @€ @€þ{wæXyþûÅŠww÷â®Å¶¸ÓB -EÊ"Å¥HqŠ•]lqwwY¼h¡¸ÛŸçü¿'¿L&'ÉÌd2™Ùû½®!É‘÷œs'“ W>û< @€ @€ 0@×Èé2 @€ô7¯¾ú*}þùçýí´/ @€ @€ @€@ך€h  @€º.°Øb‹¥)¦˜"vØaéÇìúö @€ @€ @€è·#÷Û3wâ @€À€xýõ×ÓoûÛtÒI'¥Ygµm®ó§Ÿ~J>ø`zàÒ¦›nš&˜`‚¶97'R] ž¯»îº«ÓÊ-¶Ø"M4ÑD–[Ðs'žx"Ýyçiíµ×NSM5UÍ Ge”ôé§Ÿ¦ýöÛ/=ùä“éüóÏO£Ž:jÍ}†‡•ß~ûmzä‘G²Ÿ1Æ#Í4ÓLiÞyçMãŽ;n·.¿Ùóuë$ìD€ @€ @€¨\«ñè[—^z)-¿üòéÍ7ßL›l²IÜèË3úä“O²Î!CRü¼ûî»ÙéL3Í4i­µÖjÙ©}øá‡éÜsÏM/¿ürqÄÓôÓOŸ¶Új«4þøã·ìú㜮¾úê§>ÒH#¥®Íøâ‹/Ò½÷Þ›ý~„uOc„óöÛo_ó —_~yZh¡…Ò{ï½—þùϦ>ú(Ų±Æ«æ~uå—_~™8à€tÊ)§¤¯¿þºÃežyæ™i›m¶é°¬ÞƒfÏWïxÖ @€ @€ @€º" ¸Ö-Û @€½*ðÌ3ϤVX¡ku(kذaéùçŸO/¼ðBvÕºž~úé>oax÷Ýw§õÖ[/½ÿþûü=öØ,,4ß|óuXîÁÿ/ðÕW_¥›o¾¹Ç¢‹.ªÚZ'•ú ¾ùæ›ôâ‹/¦W^y%½úê«)~_¢úàã?ž¾ÿþûúTÙ"*²]yå•i™e–I1ÿ7Þ˜ ”®¹æš,øVe—»èŽ;îÈ‚iNmÆhö|Í8's @€ @€ @€Ê×Ê5Ü'@€úL Â/+®¸búàƒ²sˆŠf—\rIKÏgÎ9çÌÂ3-=hƒEÅ7Þ¸Sh-v{ë­·²–¥=ö˜öŠU#´§*ǯýëÊE7 0tèÐ^©2A¨&¶Ùf›egqà 7¤wÜ1zê© œÕÀØä¸ãŽKúÓŸš’mö|CÙU @€ @€ @€í&0b»ó!@€†?×^{-­²Ê*¥ÐÚcŒ‘®¸â U±~~)Ü~ûíYÛÔ¢WųÏ>›¢2œÑY Z»V‚kÕTúvÙ¦›nšöÞ{ïÒIœvÚiéè£.=¨w¾ûî»´í¶Û¦ÝvÛ­)¡µfÏ7PÝ] @€ @€ Ђkíñ<8  0Ü |üñÇiõÕWOï½÷^ÉàôÓOO}ÑþrçwN[mµUZzé¥KçÒ×wž{§0òÈ èVúé§Ÿ²6ª•ëfžyæ4묳V.ö¸™fš)í´ÓNiË-·L+¯¼ra„Ø«ñMÜ¡¢Ûž{î™î½÷ÞÆ'èg[þïÿK+­´R:ãŒ3šræÍž/?©ü1 ï±Ç¥6Îù:· @€ @€ @€ž®õDϾÚ\à‰'žH'tRÍJ=m~ N.Õ ”¢jX>Ö]wÝRËÀ|Y«n<òÈtöÙggUÎþö·¿µê°53ÁÔ\ÿý÷ß§wÞy§æ6Ããʇz¨jÈf5Ö9šrͳÍ6[:á„Ò9眓¢gTElæqÄÓù矟ò×|„£Y¼O ´ñÆo¤h‘ËG„fŸþùôí·ß¦§Ÿ~:«Ä6æ˜cf›Œ6Úhå›v¸ßìùbò>ú(üñiÆgLñ¾ðÎ=÷ÜÇõ€ @€ @€ ÐÁµžèÙ—@› |ñÅ馛nʪ¡L;í´iÞyçÍîµJk³Ów: ‡‡~xºå–[JW>ÑD¥SO=µô¸/זּÚj}yøÒ±Yd‘Òýjw"ì³ÐB U[5\/»úê««^¿6¡UYºµpÕUWíÖ~µvwÜqÓ®»îZÚä™gžIGuTéñ@¸óꫯ¦¥–Z*½ôÒK.'Z$_{íµi–YfI£Œ2JšcŽ9Ò1Ç“^ýõôøã§h§Zm4s¾ Þzë­iã7N“O>yö\Äüù˜p ó»n  @€ @€ @€=Ð[ªÇ„&ÈÞzë­ôÔSO¥øÒmôÑGÏn£2Ĩ£ŽšâK°¯¾úªÃÏ$“L’æ™gž|÷nß¾ýöÛY¥ž8füäÇŒ/ü¢ Í7ß|“ýDåŠü~´ºša†º}̾Þ1®ãÅ_L¯¼òJŠ/‡ –xàìK͸fƒýAà…^H‡vX‡S=âˆ#ÒÄOÜaY_=gœqúêÐŽ;÷Üs§m¶Ù&uÖY–çvÛm·ae££@µÐvTòZb‰%:nèQ·â3^|îŠÏ%ÍÑŽôØcÍ*~żùË_²*ŒSM5U3Ó'sEÛÍ 7Ü0 £UžÀ’K.™Æk¬ÊÅ)½ñSm4{¾í¶Û®fëÒøün @€ @€ @€š% ¸Ö,Ió¤Ûn»­K­Ý¢íу>˜U“è _|ùw×]wuiŠFìµ×^]Ú§6:thZk­µÚé”Úæ\¾üòË,Èm£ ÑÌ3ÏœU.o¼ñÚæ) 4ÿþ÷¿ïx™nºéÒ[lÑ6<µÚòµú$O9å”ôË_þ2«¾”·MŒ°ö~ûí—öÞ{ïVŸNÛïµ×^KO>ùd§óŒ*z#4R§åt_ ^‡Í®EÕµ]vÙ%xàÙ‰}ýõ×YËʨ>ÖßG´WÏ¿ÕÆ”SNYmqÍeÍžï‡~¨y<ÁµšÿüóMÚÓý{tp;·…ÀóÏ?Ÿ6Ø`ƒ•ô¢BR´Ì‹ðËf›m–¢ÅÞ/~ñ‹4Ùd“¥e—]6ûâ;}´ÅÉ; é@´q¼ýöÛ;\}„°F¹}2õ~m—•­¢­jüÍ‹–Qáô³Ï>Kûï¿[™µ‹Wµjkqnk®¹f»œâ€9F¡W®eçwNå¡ó3Ï<3}úé§½r¬VNz÷Ýw.z]Íž/BƒµF»TĬuŽÖ @€ @€ @€ýG }¾î?fδ@`ñÅO'žxbzÿý÷ÓO<‘U`«*‹ÀQ´$ºð f­¿øÏþsÖ6ó7ÞH÷ÜsOö…~å^Q5g™e–IóÏ?ŠJË/¿|å&ýêq´¢V|ûÎ;ï¤o¼1«^Ô¯.¢ '´#<2zè¡u«½¼ûî»)~"(•‹Ž:ê(Uëšð˜‚@wŽ>úè»FÀtÓM7í°ÌƒÎ`kF›íÎ3¬%Œ¬a·Ê*«T.ö¸M"´áµC9$;ÃøÌsÆg¤Ýwß½Mϸ±ÓŠVïEcê©§.ZU¸¼ÙóÕk‘¬âZáSa @€ @€ Ð Áµn Ù¥ºÀôÓOŸþøÇ?–V>óÌ3i®¹æª¨ºè¢‹ÒK,‘þð‡?”öíʵ×^»ÃæÛn»möÅfùÂk®¹&­°Â å‹úõýÙf›-p ¥k¸êª«R¥Ciå½ó /¤õ×_¿Ô .*#­±ÆYðe–YfIÑŠ6*=üðÃéÊ+¯ÌÚ‡æ/½ôRæØÎ:ë¬í Z'ðÀ¤Ê*AQ5±Zs¶NÑš-ðÉ'Ÿ¤;ӴK/½tªWMªÓNô©@|6̃kq"ñ$vÝu׬x³Oì ƒʪæóFKÙ=÷Ü3M4ÑDù¢¦ÜF¥ÄfŽfÏŸŸŠÆXc•}¾*Zo9 @€ @€ @ «‚k]³}ÃsÌ1GšqÆS„„ê]vÙ%-¸à‚Y‹ÇzÛÖ[¿ÖZku®=öØý¾ÂZ½k^uÕUëm2 ÖõÕWi½õÖKŽŒ´h!áÉò¯¿uÖY'EûÁm¶Ù&]rÉ%嫳ª€Ë-·\ºóÎ;ÓTSMÕaôžÀ1ÇÓiòÍ7ß¼Ó2 tG`èС©ZKè_ÿú×Ý™Î>}(0餓¦ÙgŸ½>êº÷ߊ*¿Í§vZzï½÷:LÕÅš]á­^5â'ÐÀƒfÏ7úè£UµµB+ @€ @€ @€n ®uÎn D¨‘àÚ·ß~› ”yä‘W¶¨ M9å”i„Fhì„ûéVÑþ,*}óÍ7ýô ºvÚQm%­mµÕVéôÓOO£Œ2Já$Q=äâ‹/N¦<à€:l÷Úk¯¥ÕV[-=úè£5çè°“t[àË/¿L•m'žxâ¦Q¾þúëtíµ×¦¨ÎøÖ[o¥>ø «¶5ùä“§hÇÕ·"ÜÚì­›#X›”DEÇYg5û‰*¤µªuå\âýþÖ[oMC† IqMûï¿Wv/mûñÇgNqÞ«¯¾zVɲ´òç;?þxºþúë³Û 'œ0-µÔRià 7,ߤêý?ü0;·[n¹%›sÍ5×ì´]ÌýØceíÅ£•s<óÎ;oÖ u†fèvU­Ê×W~àjç¯+ºíËë¨<§ø{^¯¼òJzùå—SœÛSL‘Â*ìæž{î4Ùd“UîÖ£ÇÑŽü†nHÆ K¯¾újŠÏñ¹*Âdšüñ{4#;G8ýÙgŸ-mÏoo×Jèå;ÕB•=9d³ç‹JsE£^Ñ¢ý,'@€ @€ @€ ®ÉXÞ1Æ£áyâKþßüæ7馛nJ#Üý—fe¥ˆÊÇ ŸP?Û0®sx®]qÅYP-žž¨–vöÙg7üLE°#¾ð~衇:ìóôÓO§ã?>í±Ç–{@€@ón¿ýöNïU -´PÓôßÿþ7 <8þùé£>ª9o„Ê~÷»ß¥}öÙ§æv¬Œ0mi+ß_Ê÷êQ'tRÔ._Þèý×_=kyÝu×¥øÉ[FµÉFƒk?üðCÖbù®»îJÑF;ž<øë¢sŒþýþ÷¿Oçw^‡Ó»çž{ª×bߢEËxŸùcYŒ»”‡Æžx≴óÎ;Wmç™,Ú3ž|òÉ)ZÈve|ÿý÷™Må>êšfši*wzÜ.×Q~bñ{ì±ÙóõÓO?•¯êp?BeÛo¿}О`‚ :¬ëêƒøGGqDºà‚ Rüã‚j#>«E4½9"¸ö·¿ý­tˆx}Ź @€ @€ @€ý_ ûé þí® ] Å—³;ì°C)˜ÔS¬ø`£»dÛýë_ÿJF‹jn¿üå/ëî[tNEÁµv¼Žíí½÷ÞYEÑ<¬U÷ÂË6ˆ h›o¾y&Œ6²Ž¨„·õÖ[g­³ݧÛE‹Ú¨˜ç—Ûn»­WZýæó7û6¨ñ;A³Z#‚ y+ôÊíÆo¼Ò¢fÏÇïhTYŒJŒEãÍ7ßÌÞk«­ðÿ¸ãŽ[m•e @€ @€ @€Bÿ,¾ÆŠÞm´Ñš6¾Ü»üòËÚÖFÃ@TY)¯¤óã?¦¨t´ï¾ûv aÑE­º}´¸3ô•@TØÚxãST‹–Éq¼üòËY¥¤ÊkëIŵÓO?=-°À…¡µz€æœsÎÊÓiøq´Õìjh-ŸüŒ3ÎHï¿ÿ~þ°-nã=uË-·LÕªâå'˜×òÇÜÞ}÷ÝiÁìrh-Ÿûí·ß΂ùãZ·Õ‚kÞŠã÷t´ê:¢r`TíNh-¿Æ?ü0{.óÇõn;î¸ÌèÑGí´i„;×[o½,H¹úê«§QF¥Ó6½½ Ú…–¨ØFT#‹jŒsÌ1GÝÐZ½ë™wÞy³*gÍœ/ޝ³øG#³Ï>{ÍÐZ­óûÅ/~!´V È: @€ @€ @ P@p­ÆŠÞˆ0Ú´ÓN[wêøm³Í6ëÔ©îŽ6ÐåÕVÊ/ôºë®KѲ¬Ñ1Ë,³TÝ4¾ȫ¬=òÈ#i¥•VJ‹,²HVѪ'ᕾº–¢ãÞ|óÍVÅß„‰&š¨Óòz ¢2Ø Aƒ² AÑF¯|L1ÅYø9ZsÆvÑÆ2‚«®ºjùfÙý¹æš«Ó²î,ˆJCóÍ7_Vý±^X.æÖ¦Ñ³hĹžþùé„N¨ÚÞ¸h¿ÊåQîì³ÏNG}tšyæ™+WwxAà‹.º¨Ã²Êypm¯½öÊÂUGydšnºé*7ëð8BFñ\ôd\rÉ%)Ú(Öy ÉÊm"l•ÿ~U®kÇë¨<Ç•W^9{þâù‰×qT¶j¤’Z`_ýõÊé:=޶ »í¶[§¿£ñ ☯ýÜ:òßÿþwÒŒj\ÿùϲ¶î&êÅñ»U>ž~úéò‡m{ذaéÌ3ÏìdÛŽàZ³ç‹óˆ Ûm·]ŠëÎyÅ>|¶ïîÜö#@€ @€ @€¶€V¡ûùm»«‹6_Q eñÅOŸþyÍó‹ B´öºÿþû{ÔÊ­æA\ƒ¨Bç_¾G[ɨü5ÒH#¥ha5Ád€¥—^:E`¢ÙãwÞIÑ^/¾°|õÕWÓ¨£Žš¦œrʬ:F„Æüf²Ó|ï½÷^ºöÚk³ãÇᦉD„ÀfuÖ¬½ëcŒÑi¿f.˜d’IªN¯¥xŽm [²øïÿ[u~ ´B 2 -×Zk­Áªªl´ÑFiôÑGoÅ©ôÚ1ÞxãNsý>vÚ°bATªü ¯¨Vþ^Ëâ'öÙo¿ýÒa‡Vš-ª õdD¥¡C9$ýá(½}öÙgi=öHQ ®Öx衇²ýªmíóÈ>[e•U ÛwVÛ?_ÕìòŠvÑztª©¦J=y¯Ëƒkñw<~bļñ÷½2@˜ŸC~/~ûÛßfûE)þ–„A8E;Ðz#Œ#„V , ¶µ ãµãu”;D€µZ{Ý8 E+ÕZ#BƒµÚáî³Ï>)Ú¥VŽø|AÓ%–X¢rUšxâ‰S´ *hñ¾áÐÞ•áÖþRqm¶ÙfËž§h+ïñyíÏþs!×ÔSOµ‰­¶A<3Î8cSç‹ãÄyEÛõ°Åýjï«åçS­…qOßGËçwŸ @€ @€¾׆¯ç»-®6Â^xaZguR´%«5"$¶Ækd_hG5›VÇÁ—zÁµhŸF /¼pŠ*fñ7 ?v\+?p,‹â3ÏâoÍQG•?ìp;Ï<ót»=r‡‰< @€ @€ @€üŸ€V¡^ }"ÕO?üð†Ž_îýæ7¿iJ›¥†øóF~øaZ{íµ³¶oñe^£¡µ˜ÿùçŸÏ¾tœa†Ò]wÝÕè!;lm/Ã'B~Ñâ­V`,¶½å–[:ìߌ‹*?óÏ?Ö*¬Ö9Äñ¾úê«`"\á„Þñåj|a›'¢õÛ2Ë,“Ü¥ÃEÕºj£^½jûXF YE­ óù#¨a¢hɶæšk¦+¯¼²%•Žòã7ãöý÷ßï4MWƒk1GÑßø^a…:£|A`"X–¨@ÖÝAÂÊÐZù\:{ì±Ëu¸ß•ö™ùû^‡ ºñ Þ<ë®»nŠÐv¼Æ"ï±ñZ‹êZñþ^‚ª5o„ãïTeø(?ýxoªkQ¯Þ(ª´a¼ûÓîñzèJ5о¾Ž¸€Úm¾ùæiÅWL+á!ÎZ£Z•Ã|ûævØ¡hU‡å›l²IjEè»Úk§ÚûI‡“ó€ @€ @€ @ í×Úþ)¸'â‹ÙFÆ7Þ˜UcidÛžnóØc¥X ]uÕUU§ŠpI|y­×¢•VшÊ0ñåó™gžY´IÕåO<ñDŠÖ`Q½ì›o¾©ºMo/Œ–¨ÑÞꤓNÊZpvåx/¿ürÖÊíŠ+®èÊn oí¬¢½ÜÝwßUÞ¹í¶ÛR„»2¢cµ!¸VMŲV 4Z±(Úâ^sÍ5YÕʨ¼¶ûî»gU—Zuž=9N3*®qÆ…aÞ]Õ ÆùçUÖ¢åää“OÞíKÊ[eMm¤£ÅgÑøâ‹/ŠVuZ-¢›1jÍí£ŠWåß¶hQ{ë­·fU¢V[mµª§QkÞhïY+À—O¸ÿþû§cÕEÁµ¨ŒZ­Šk­6¡ÕŽÓ××ç´Øb‹eã³O½ØÑ.¼Ö(òŒÖã—_~yÕ]#ˆ¶ÜrËU]×W «×>ýôÓš§Áþ‡6úSíõÿp¡Ñý»D­yâV @€ @€ @€†#‘‡£ku©m(mÁâËÓ{ï½·îÙE5–hö§?ý©î¶ÝÝ *“¬´ÒJY ªjsÄàçž{nŠ]1>ú裬JÌ>ûìSmó¬ÒvÛm—UHªW(&8î¸ã²v€ñekå˜fšiÒ‚ .˜&›l²¬-X|¡ýÝwßUnÖãÇ_|qÖ–ð믿î0W´¼ûË_þ’C¬‹ê9ñ¸Ú¹ÆŽqÝêè6^®ˆŸîŒO>ù$k{Z¹oXÂ× ÐŸ¢âбÇ›ýÄûc´\4hPŠvní8š\‹÷à¢1ÅS­ê°|ûí·OÑŠyú駯 ê°cŃFZתèÖ•àZÅ¡»ý°V°o©¥–JRëΨ5o£óE p³Í6Ë*í­K«!C†tZïëEâ:mü úú:ŠÎ«hyümŽ Yüm«6ŠžÏø[_-¨sô$ÌYíš±,*3F 2‚»ù¨\‹kü®÷d|ðÁ)~ñ¹¬^ذѹlG€ @€ @€׆‡g¹¯1¾ôŠ /¼pªÕÎ*¿„½öÚ+«®µÎ:ë䋚vÕÍ¢ZO´ã«6V^yåô¯ý+•Wc‰ÛÞ{ÚÉ'Ÿ\m·ì‹á6Ú(Å—íµÂVñÅhµ–_aA¤”¬rÈ!éoû[ùâÝêpÕZµM:é¤)*›Í:묥ùõ«_e­L£kùÉùѶ-æºúê«óEmqÕ„¢­iåØb‹-²KåòV<Ž0Oµsjű£}ŠB$ža„€=ôÐì'‚®´Ÿ%–X¢Ûa¤FÝèvñ¾P9ºÒf0ÞkŠZýƼnzf©¼†Z£*^Ñèés^4ow—w¥¥fwQo¿h-²‹Fµ`Y„©#Ì]9â³Eo„§+Síqw®£Ú<Õ–EHí‘GÉ*->üðéV€«(\ٌߣjçÖ[ËâyÏ\åŸÑj]wœGüãc` |øá‡éµ×^˜çª ‡üñpxÕ.™ @€ @€\@p-—pÛgñ¥r„›"U¯úL|ɿ馛¦Ûo¿=kÕÙÌ“>çœs²/‚‹æ<ñÄ;„ÖÊ·‹JiÑ~²¨e|Ñm7‹ªvDŶ#Ž8¢|Êì~T¹ù曳àIåʉ'ž8 Ë-»ì²)‚q=­¾_FûÖjc×]wíZË·‰áŠ+®˜n¸á†|Q‡Û¨€óôÓO§9眳Ãò¾z_ryä‘i"ØW#* ÝtÓM}uxÇ€ÑN7Þsâ'^ß^‹ªQQ+Z!÷Õh´jÑù½óÎ;Uƒ²ùöEÕ¥òõ}q•¢ŒÆ¦vÚšGºrD°úóÏ?¯\œÖ\sÍNËZµ ;×Qtnñ÷9ZµF¥Óøüóâ‹/¦F—ÊÃöåó× ýD‹Ûvq^åÁµ¢kkÇswN͈ÏÍñùÒ @€ @€ @€ú¿Àˆýÿ\Á@ˆ¶v\pAªVI¥òú¾üòˬŠP#Ú*÷-zU|Ž:ꨢÕYðjæ™g.\m¡6ß|óÂõ±"*£U«ªÕRª…ÖbŸµÖZ«jh-Öå#ªºDè®»|Ÿj·x`Õê$Ñjmã7®¶K¶,BsµÆO{Í/šïºë®ÁµaÆ¥Ë/¿¼êÜðXn¹åª®köÂhSUçª)¦˜¢ÚâÒ²¨…ŠÚ–=ùä“¥mûêÎ'Ÿ|’~÷»ßu:||ñyÞyçõ¸Z]§‰»¸ ¨Ek§±y?¸þúë{\‹V™T‹JSÑ&±Ý¾ü«4îJpmºé¦«ù, ®Õäé+£ŠVQh-. ÚŠ—Ç{,U Zõe›Ð8¿®^G~M²Ž |´F¯6"L¾óÎ;§u×]7M8á„Ù&×]w]ap­²JY>g|Æ(r®æ™ï×—·•Áµ‰&š¨æéDµØï6:~ó›ß¤Ê÷£í¶Û. 4¨Ñ)R´x7z_ ‚Æ6 @€ @€ @€þ/ ¸ÖÿŸÃwQëå—_N÷ßÝk:thVi¬¨ZXÝ þoƒ;ï¼³æ¦ù—Ã57úyeT?©U!塇ê0ÅÅ_\øåôä“OÞaÛÞ|pë­·N_/¸¡Á &˜ EE¹j£VU—jÛ÷Ʋ]wݵS°!Bkˆ A ºÛR2~£¢Ú†n˜ÖYg¦T¡ì-ñÇ¿ÓÔ}ôQ§eE "ˆ•¬*,ùö6ú·@­ÐÔ,³Ì’UR+¿Â¢6¡}\ëêuÄ5E«ÛÍ6Û¬ðsA„û>øàRåÄr‡¢ûE¡ôf˜¡SõÃ|ŽÏ>û, ÂE ¿Fåï}½àZœW®a´ÑFët¹3Í4SöþÚi… @€ @€ @€M±)³˜„@"HtÅW¤©¦šª¡YO?ýôtôÑG7´mÑFõÂ×¢úX­ñÁtXýꫯvx\þ •ÁµçŸ¾üÐîG¸nÖYgMóÍ7_Zb‰%²jNQ .*]D›¦…Ö:LÔGŽ?þøtÎ9çt8úRK-•n¼ñÆg í"PTµ°èüæœsÎtì±Ç¦‡Fµ¶-¶Ø¢­Ckqñ^R9*+U®¯|- ‹F„ƒÛ¡ÊcÑùY^_àÍ7ß,Üh£6ê´®Zp-ZHÆû|_Ž®^GœëI'”*?'ä××íÔóv¿ùòz·ß~ûmÕMjýÅþƒ€xßúî»ïª£™ £ò÷ßßaÊzÁµ{@€ @€ @€ Ж*®µåÓâ¤&tÒtÕUWeA©/¿ü².È^{í•¢zHTêÎøßÿþWs·¨(ÖȨ·]åqjµµ«7W#çÓè6µ‚gáÿ /4:U[mwÁ¤ÝvÛ­Ã9­µÖZ颋.JcŽ9f‡åècŒ1FÚd“M²Ö·]i×.×V­íò{ï½×¥Ó‹÷ú¢pZ„h=ôÐt饗viN·À-·ÜRõd"h-2ËG„âUhåXe•Uº𪜣§»rù±jµ-Ï7ݩʯj£^pí¬³ÎJtPݪ¤Ï=÷\ÖµÚ1𹬲‚]TšŒ€¢A€ @€ @€ пT\ëßÏ߀>û¨ðõü£¡/jüñǴ馛¦ÊVœEë©fU ©6ûôÓO;œÒ믿Þáqùƒ®V!*ß·«÷‹*¼tužòí£åÖ ,úª]Ûµ×^›¶Új«T^Åjï½÷Ϊù ­•?Sî·‹@­PÊ4ÓL“Ž<òȬåígœ‘úch-œ«×^|ñÅÂÖŸÕž›®Õ—]vYŠ0Ñÿ¾úê«ô÷¿ÿ½ê‰ÇûwePéšk®©ºí¯ýëªË[µ°«×ç³Gy¤ðG±{Ù»\‹ÏÇw\áùä+¢êm+FeeXÕÖZ¡Þùüc’Î{YB€ @€ @€(Pq­ØÆš6XwÝuÓ!‡’8à€ºg_¦Å—ÕÑf´+ã믿®»yTJk¤uçøã_s®ÊÀT´ø+•ÕEжkÆòzÁ½ 6Ø ð0P‹0A\{ÜF»Ô¹æš+Í6Ûl)*¢ôňJ7ƒ *µ‹s<óÌ3³pc_œchD «XXïüº»>»‹-¶Xºçž{:Lñàƒ6\[vÙeÓk¬‘ŠªmÅÄ·Ýv[Ì2dHª×±Ã‰T<ÈßG*—Ö[_Ú°àηß~ÛP +v¯u¬Zë*]kÛÞª¦ÏÕÖ[oˆ‹˜<ð@”ª<ßYf™%kï\Yq,‚^ñ+L2É$YE»FBk±o­×[¬otT×âõÒ_G-“$vu4{¾é¦›®æ)lºé¦YÄ /¼0Åk(ÂÈQm·;ç^ó@V @€ @€ @€Ã…€àÚpñ4÷ÿ‹Œ/t¯¼òÊš_êöä*ùË_ÖÜýÃ?¬¹>_Y/à6ãŒ3æ›f·õ*vÚi¶/zá‡zí>‹öåõ®?ª˜µûxâ‰'ÒÊ+¯œòð`´+½÷Þ{ST•©7Ž8∬%í1ÇSoSë ôšÀ 'œžzê©T-ØÕk탉«]_T\ëʈ€ßï~÷»º»Ä{÷æ›ož¦žzê4Ùd“e›h#ûO;í´)ªIÅûìo¼Qu®¨rUkÔ[_kß|]#sÄûûwÞ™ïÒé6ºv¬7Þ{ï½ôôÓOnÏClÓ[㪫®Ê‚OsÌ1Gúío›µoŽÌ*«¬Ò)<¸ñÆgUÆŠª?Ukç«ÞͼŽòs­üŒP¾.îðÁé¼óÎËZƒ6ú7?þµ%ãE®'c—]v©¹{TÅ+jWZsDz•QQì‘G)-‰ößK,±DéqºÕêây,·ß~{êJ€´ÙóÅyÕ ®Eˆ2ÂÃ`;è ƒ²÷ž¤6¬è²,'@€ @€ @€ Œ\¸Æ m&¡ƒø²x©¥–JÍng¶à‚ ¦¨¾S4~øá´Í6Û­.-¯p‹s/õ‚kguVö¥`÷jhã•W«µ]Ѻ¨âR+œï¹þ¢ù{{yTVZqÅK_ö†s¼VjU’ËÏ)¾üæ™g²‡³Í6[¾Ø-– DPgx–‰àRþ{×Õ%£Õó¤“NÚ0ATú‰Ð׋/¾ØÐ>1ÿ 7ÜÐiÛÀ”ÿîrÈ!Y寘÷æ›oî´}ù‚ýë_YU·øï7¿ÿýï³\ù6õîGø#ÂHQ3ÎcÛm·Ív‰¿;ÿþ÷¿³Ë]wÝU÷:WZi¥?Ò‹`^>O´õ‹êXï¼óNæíI‹Fümyæ™Óâ‹/ž…W¢•vT0‹ÇÍ´Ž÷ìø©6¢åv„8køá‡tíµ×vÚ}öÙgÏ;­è…͸ŽÊÓÚ`ƒ R—Ñ^4>'ìºë®…á´·Þz+û}‹jwQÁ.ƒå#‚G×]w]áþåÛ–ßç)>ÄßÛã?¾|U‡ûñÙbžyæÉ^CBŒ ‡y»ÓÖx¯ÿò–§Qá«^köÓµtUü_~ùå)ªè¾òÊ+Yè°Ö De¹xŒÏeq;Î8ãd¿“ùóÖìùªËÜsÏ¢bm­ÏÅ•ûExÞyç­\ì1 @€ @€ @ ®€àZ]"ôD ²"È÷ßß“éÒ ,}Á¿þúë§øÒ¸Y#ZI^vÙe…Ó]sÍ5éÔSO-\Ÿ¯øÏþ“ß­zÛÕàZTåˆVvûì³OÕùò…§Ÿ~z~·[·J8ñÄ ÷Š-Q‰'ÚîµÛˆpI´ ËÛÏ­³Î:颋.JõÂ~ùuD…«<ÈQ^É×»%@ ùªÚyç;L«µ4:¢Rd¼7E·ZÁÛzóí±Ç69ì°Ãª¶ ì°QÙƒhsš·:jnëʈjRC† ÉvYh¡…J³x yä‘ OÁ˜üïXéòàZ„f.¸à‚†ç‰ª•å¿53¸VëD"0ïßl¬5î»ï¾ªU«z»Mh­s*_×èu”ï÷#¬Ÿ5i[¯â¹âf›mV9U‡ÇÑJ2n/½ôR§àZ´'°Ü²Ë.›"äÖȈàYTÁpUyµhߨò—Wú[mµÕº\»ñÆ;LÝŸÚ„Æïñá‡Þáüë=ˆê½šÍƒ³ñÙ+®5{¾jçí¢íFmTmu§eñœF6ƒ @€ @€ ЭB»£fŸ†â ùòQ^1£|yWî4¨K_æ72w|é6Â#n´Ç{¬p}¾"ª–hc_—¨~2∵ ÷Ýwß,¼V¾_ùýœÕ •o[t?¾°uÔQ‹VgË#,X¯¢\Í zaeT/‰/°£’RŒí¶Û.«PÔhh-öùç?ÿ7Ù\Ë%Üè]¨à8þøãw8Hùïb‡5D¨*B7ÊéJ`,Þïâ=mèС½ÞZr’I&©qWEeÑ|t¥ú\¾Ooܶâ<¢ªT´jŽvõBkqyЯòzû:¸ÖÕë¨<ÿ‘F)]zé¥i‘E©\ÕáqTf‹0y„Öâ4ÚŠ³è¹œi¦™Òã?ž¶Új«šŸ…â$"èAͭŨü Ù‚ÿŒ<òÈ©¨õkÁ.Ùâø=-+¬°BùæÝïÊïj£-2otÿØ®|Žòû]™£|ÛFæˆ×Øž{î™â5YkD•Ë/¾¸îgÙZsXG€ @€ @€ ß*® ßϯ_}eP­òqwO *ä¼üòË©‘JcTf›e–YRTÊ©<‹–móÍ7_á)GÕ‘aÆ®ß{ï½;­‹ãn¸á†Y…™N+ËD ¦h_Ê/¹ä’Y5 G}4zè¡Y ¸²M«Þ­ Vn4ñħø’òÿøGåªÒãhµÜrË¥h­äjø=‚o¾ùfÚqÇkmÚíuo¼ñFZË«ÜE»½x]¼úê«…sF{¹ø O>ù$kÛuÎ9çdÛG…‘hÕg Ðûc5VÚ~ûíÓàÁƒK‹v€Ñβ<¼UZYãN+¶Þzë¬êT´Ä¼é¦›RT%Š*ŒQµ2þîÄœÓN;möÁ¨­M4ÑDUgOÌá¶ÊŸQF%«öaøùæ›o²Šñž•«…ç"¯bÎho?®°tìûÆO~žùIEkǨáæjçóÅûYœG;~bž˜3BùˆÊIûï¿§9bθž8üzòÛ˜+îÇüjê鈿¯ñ·îÎ;ïÌ*tÅ߆8ÏøU2#ÈØHkçü<¢zëxã—?Ìn#U/ðÕa‡nSä›^uÕUYˆ?ìvøˆœpôÑGgÜû;? IDATŸ-‡IWJ€ @€ @€ÚK vÂö:WgÓÏn½õÖ¡µ8ýh8š1¢ÚLÌ—·«*š3ª}42–Xb‰¬rDѶQ#Ú”¾øâ‹69è ƒ CkÓO?}:ÿüó;í“/˜qÆÓ–[n™?ìÖmT@©5n»í¶º­Äæ™gž×Qo|þùçéÀÌ* Eå“»ÅsA¢âÍî»ï^ ­EUŽ„5sDõ—hZ«º]wŽí® Z'mù*ßs"Ü\dkÝÙ8í&ðé§Ÿ¦óÎ;¯tZñ9%þÁ‚A€ @€ @€ 0pþ_/©sM®¤ž|òÉì ÆEõ®hY9¢\œ^xá®i¦™&kKU¹]££úW´÷ŒŠ^E-"»‚øË_þ’UݺöÚk«žBT‹€S/Z$E/*Ì<ôÐCU·Ÿnºé²Jkã?~ÕõùÂoÄuD%±®ŒhÇ­½¢RÜñÇ_¸kœc|á»øâ‹gᲨ~S­]WT^ФW\qEá\ùЍ÷È#ä«ÞFºqƧêºî,ŒVg+¯¼rÖ:­;û×Ú'ªÞ´V ‚®Ñ†î±ÇËí=£}o¼G ßÑ.õ³Ï>+!DËÛhai @€ @€ @€ Áµó\öù•DpíØc­{Ÿ|òIºé¦›²í&žxâ×b’¨Úsà 7d¡¬?ü°Óñ»\yä‘S´¡Úm·ÝÒ‰'žØi®X­4#VÔ-ßiÅWL_|qË—ÝF˜ïöÛoÏÚ¾õÖ[E›uXÁ³+¯¼2«vV-$ØaãŸ<ýôÓÙO,_mµÕª×FqÄôïÿ;í½÷Þé¨£Žªœ¢K]tÑ´ÓN;uiŸzÇëæñǯ·Y·Ö«¸Ö-6;è‘@¼çF8%ÂÌy»äƒ>8m¸á†©^à·G¶3m-ðñǧÃ;¬tŽñ7z½õÖ+=v‡ @€ @€ @``h:0žÇ¶¸ŠõÕ˜y晳ÊfQ¬r|õÕW•‹j>ޤ'œpBºóÎ;³ªj57®²òW¿úUV=íÆol(´–O1ÓL3e¡¬­¶ÚªnE‘8FTyËÛ¤FÓFGE¢¥gшðÚ_ÿú×tï½÷¦UVY¥h³ªË£5èÿøÇ!Æûî»/M6ÙdU·ëîÂ1dz»»ÖÝOŵºD6 Ð+óÍ7_Öb8Ÿü½÷Þ«Ù¶9ßÎ-W *à~ðÁ¥ ûì“.»ì²tË-·¤¨ÀíW#p•Ù6Ø`ƒ´Ë.»dÇÏ'™wÞyÓ³Ï>›-‹ë û¸ ÷hgúÍ7ߤ·}ûí·ÙsR­Mh>W~ž×_}6lXºõÖ[³ŠpÑŽ5®?Z¦ÆùD»×¸Þø‰j(«¯¾zvÜ|Žf߯kì§Ÿ~jö´æ#@ ú¹]ò!C²÷±8•3Ï<3mºé¦Yä>>5‡'@ Åñy椓N*5ª·®½öÚ¥Çî @€ @€ @€ Áµó\¶Å•L7Ýt…ç!ªQF% 6õV ¸f˜!ÅO3GT⊟¨$ÖªÕ×¢]gü4:Š*†Ex°'#¦m»í¶=™Æ¾(ˆ°me_|ñ,¬Õ®=öØc5+DNhýR þÃ[l‘…íã¢ÂìÑGÝ/¯ÅI @€ @€ @€ÔÐ*´¾‘- @€^˜zꩳðZ´Žñæ›o¦Í6ÛL•Å^voÖôQÙ³hDµÏþ2ÊuôïÊóŒê‹Q97F´ ¿ôÒK³ôP¹Ç @€ @€ @€C@pm`<®‚ô{¹æš+ :4åᵨÂvøá‡÷ûë.àÓO?-¼ÌZë wꣵεֺ>:ÝuسÎ:+ <8»¦hG¡µÉ'Ÿ|@]£‹!@€ @€ @€è( ¸ÖÑÃ# @ ^xátË-·¤ &˜ ;‹‹.º¨ÏÆ¡øàƒÒ°aà 7–¯ÑþµÝÇ@¹Žvw®v~C† IÛm·]iÕñÇŸ–Zj©Òcw @€ @€ @€¦ÀÈó²\ Ð_æŸþtë­·¦õ×_?uÔQýõ2ôyßtÓMéÙgŸMo½õVºâŠ+Ò?üPx½j[zé¥Ó²Ë.›Æü4öØc§Í7ß<:ꨅû´jÅ@¹ŽVyõÆq¢5èl½†Fa„tòÉ'§í·ß¾7eN @€ @€ @€6\k³'Äé @€)Í3Ï<é…^@Ѧ.ºúê«>»»îº+ÅO>Ö]wÝRU½|Y_Ü”ëè »fs—]vI_}õUe”QÒgœ‘¶Øb‹fMm @€ @€ @€6\kó'Èé @€ÚM`œqÆéö)>úèmQm-.` \G·ŸŒ6Ø1Z‚FhmðàÁiñÅoƒ3r  @€ @€ @€­\k•´ã @€ˆ@T*ÛrË-³Z´ü,ÿ‰RŒï¿ÿ>}ûí·é»ï¾ËnóûO Kanika Yadav (external) Microsoft Office User 2020-09-25T06:54:04Z 2021-12-22T19:07:50Z 16.00 true 2021-02-23T09:13:03Z Standard 90c2fedb-0da6-4717-8531-d16a1b9930f4 45597f60-6e37-4be7-acfb-4c9e23b261ea 0 true 2022-01-14T16:33:39Z Privileged AMD Official Use Only-AIP 2.0 3dd8961f-e488-4e60-8e11-a82d994e183d 3ab6c0f7-c658-4f6f-bd9d-6ef921551ff7 1 14235 32767 32767 32767 False False Filename Title Categories Version Doc Type MAP rocm;hip-sdk;hip;gpu;amd;rocfft;fft 4-5 apply-ALL default rocFFT API Guide reference