* further optimize search
   - use achievement 4496 as shortcut for everything based around total achievement points
   - get talent distribution separately
   - get total profiler-items found separately
   - opt to not sort found results
 * fixed Profiles with zero Achievement Points
 * added cache key genrator i forgot :<
 * fixed typos
This commit is contained in:
Sarjuuk
2018-03-28 20:34:18 +02:00
parent 431e984f03
commit bf42973c00
49 changed files with 117 additions and 86 deletions

View File

@@ -257,12 +257,24 @@ abstract class BaseType
// execute query (finally)
$mtch = 0;
$rows = [];
// this is purely because of multiple realms per server
foreach ($this->dbNames as $dbIdx => $n)
{
$query = str_replace('DB_IDX', $dbIdx, $this->queryBase);
if ($rows = DB::{$n}($dbIdx)->SelectPage($mtch, $query))
if (key($this->dbNames) === 0)
{
if ($rows = DB::{$n}($dbIdx)->select($query))
{
$mtchQry = preg_replace('/SELECT .*? FROM/', 'SELECT count(1) FROM', $this->queryBase);
$mtch = DB::{$n}($dbIdx)->selectCell($mtchQry);
}
}
else
$rows = DB::{$n}($dbIdx)->SelectPage($mtch, $query);
if ($rows)
{
$this->matches += $mtch;
foreach ($rows as $id => $row)

View File

@@ -35,7 +35,7 @@ class ProfileList extends BaseType
'talenttree3' => $this->getField('talenttree3'),
'talentspec' => $this->getField('activespec') + 1, // 0 => 1; 1 => 2
'achievementpoints' => $this->getField('achievementpoints'),
'guild' => '$"'.str_replace ('"', '', $this->curTpl['name']).'"', // force this to be a string
'guild' => '$"'.str_replace ('"', '', $this->curTpl['guildname']).'"',// force this to be a string
'guildrank' => $this->getField('guildrank'),
'realm' => Profiler::urlize($this->getField('realmName')),
'realmname' => $this->getField('realmName'),
@@ -181,7 +181,7 @@ class ProfileListFilter extends Filter
protected $genericFilter = array( // misc (bool): _NUMERIC => useFloat; _STRING => localized; _FLAG => match Value; _BOOLEAN => stringSet
2 => [FILTER_CR_NUMERIC, 'gearscore', NUM_CAST_INT ], // gearscore [num]
3 => [FILTER_CR_NUMERIC, 'achievementpoints', NUM_CAST_INT ], // achievementpoints [num]
3 => [FILTER_CR_CALLBACK, 'cbAchievs', null, null], // achievementpoints [num]
5 => [FILTER_CR_NUMERIC, 'talenttree1', NUM_CAST_INT ], // talenttree1 [num]
6 => [FILTER_CR_NUMERIC, 'talenttree2', NUM_CAST_INT ], // talenttree2 [num]
7 => [FILTER_CR_NUMERIC, 'talenttree3', NUM_CAST_INT ], // talenttree3 [num]
@@ -245,7 +245,7 @@ class ProfileListFilter extends Filter
parent::__construct($fromPOST, $opts);
if (!empty($this->fiData['c']['cr']))
if (array_intersect($this->fiData['c']['cr'], [2, 3, 5, 6, 7, 21]))
if (array_intersect($this->fiData['c']['cr'], [2, 5, 6, 7, 21]))
$this->useLocalList = true;
}
@@ -435,6 +435,17 @@ class ProfileListFilter extends Filter
return ['AND', ['at.type', $this->enums[-1][$cr[0]]], ['at.rating', $cr[2], $cr[1]]];
}
protected function cbAchievs($cr)
{
if (!Util::checkNumeric($cr[2], NUM_CAST_INT) || !$this->int2Op($cr[1]))
return false;
if ($this->useLocalList)
return ['p.achievementpoints', $cr[2], $cr[1]];
else
return ['cap.counter', $cr[2], $cr[1]];
}
}
@@ -442,9 +453,8 @@ class RemoteProfileList extends ProfileList
{
protected $queryBase = 'SELECT `c`.*, `c`.`guid` AS ARRAY_KEY FROM characters c';
protected $queryOpts = array(
'c' => [['gm', 'g', 'ca', 'ct'], 'g' => 'ARRAY_KEY', 'o' => 'level DESC, name ASC'],
'ca' => ['j' => ['character_achievement ca ON ca.guid = c.guid', true], 's' => ', GROUP_CONCAT(DISTINCT ca.achievement SEPARATOR " ") AS _acvs'],
'ct' => ['j' => ['character_talent ct ON ct.guid = c.guid AND ct.spec = c.activespec', true], 's' => ', GROUP_CONCAT(DISTINCT ct.spell SEPARATOR " ") AS _talents'],
'c' => [['gm', 'g', 'cap']], // 12698: use criteria of Achievement 4496 as shortcut to get total achievement points
'cap' => ['j' => ['character_achievement_progress cap ON cap.guid = c.guid AND cap.criteria = 12698', true], 's' => ', IFNULL(cap.counter, 0) AS achievementpoints'],
'gm' => ['j' => ['guild_member gm ON gm.guid = c.guid', true], 's' => ', gm.rank AS guildrank'],
'g' => ['j' => ['guild g ON g.guildid = gm.guildid', true], 's' => ', g.guildid AS guild, g.name AS guildname'],
'atm' => ['j' => ['arena_team_member atm ON atm.guid = c.guid', true], 's' => ', atm.personalRating AS rating'],
@@ -466,14 +476,12 @@ class RemoteProfileList extends ProfileList
return;
reset($this->dbNames); // only use when querying single realm
$realmId = key($this->dbNames);
$realms = Profiler::getRealms();
$acvCache = [];
$talentCache = [];
$atCache = [];
$distrib = null;
$talentData = [];
$limit = CFG_SQL_LIMIT_DEFAULT;
$realmId = key($this->dbNames);
$realms = Profiler::getRealms();
$talentSpells = [];
$talentLookup = [];
$distrib = null;
$limit = CFG_SQL_LIMIT_DEFAULT;
foreach ($conditions as $c)
if (is_int($c))
@@ -486,7 +494,7 @@ class RemoteProfileList extends ProfileList
$curTpl['battlegroup'] = CFG_BATTLEGROUP;
// realm
$r = explode(':', $guid)[0];
list($r, $g) = explode(':', $guid);
if (!empty($realms[$r]))
{
$curTpl['realm'] = $r;
@@ -503,17 +511,9 @@ class RemoteProfileList extends ProfileList
// temp id
$curTpl['id'] = 0;
// achievement points pre
if ($acvs = explode(' ', $curTpl['_acvs']))
foreach ($acvs as $a)
if ($a && !isset($acvCache[$a]))
$acvCache[$a] = $a;
// talent points pre
if ($talents = explode(' ', $curTpl['_talents']))
foreach ($talents as $t)
if ($t && !isset($talentCache[$t]))
$talentCache[$t] = $t;
$talentLookup[$r][$g] = [];
$talentSpells[] = $curTpl['class'];
// equalize distribution
if ($limit != CFG_SQL_LIMIT_NONE)
@@ -527,8 +527,10 @@ class RemoteProfileList extends ProfileList
$curTpl['cuFlags'] = 0;
}
if ($talentCache)
$talentData = DB::Aowow()->select('SELECT spell AS ARRAY_KEY, tab, rank FROM ?_talents WHERE spell IN (?a)', $talentCache);
foreach ($talentLookup as $realm => $chars)
$talentLookup[$realm] = DB::Characters($realm)->selectCol('SELECT guid AS ARRAY_KEY, spell AS ARRAY_KEY2, spec FROM character_talent ct WHERE guid IN (?a)', array_keys($chars));
$talentSpells = DB::Aowow()->select('SELECT spell AS ARRAY_KEY, tab, rank FROM ?_talents WHERE class IN (?a)', array_unique($talentSpells));
if ($distrib !== null)
{
@@ -537,9 +539,6 @@ class RemoteProfileList extends ProfileList
$d = ceil($limit * $d / $total);
}
if ($acvCache)
$acvCache = DB::Aowow()->selectCol('SELECT id AS ARRAY_KEY, points FROM ?_achievement WHERE id IN (?a)', $acvCache);
foreach ($this->iterate() as $guid => &$curTpl)
{
if ($distrib !== null)
@@ -554,22 +553,18 @@ class RemoteProfileList extends ProfileList
$limit--;
}
$a = explode(' ', $curTpl['_acvs']);
$t = explode(' ', $curTpl['_talents']);
unset($curTpl['_acvs']);
unset($curTpl['_talents']);
// achievement points post
$curTpl['achievementpoints'] = array_sum(array_intersect_key($acvCache, array_combine($a, $a)));
list($r, $g) = explode(':', $guid);
// talent points post
$curTpl['talenttree1'] = 0;
$curTpl['talenttree2'] = 0;
$curTpl['talenttree3'] = 0;
foreach ($talentData as $spell => $data)
if (in_array($spell, $t))
if (!empty($talentLookup[$r][$g]))
{
$talents = array_filter($talentLookup[$r][$g], function($v) use ($curTpl) { return $curTpl['activespec'] == $v; } );
foreach (array_intersect_key($talentSpells, $talents) as $spell => $data)
$curTpl['talenttree'.($data['tab'] + 1)] += $data['rank'];
}
}
}

View File

@@ -23,7 +23,7 @@ if (!defined('AOWOW_REVISION'))
// tabId 0: Database g_initHeader()
class AchievementPage extends GenericPage
{
use DetailPage;
use TrDetailPage;
protected $type = TYPE_ACHIEVEMENT;
protected $typeId = 0;

View File

@@ -8,7 +8,7 @@ if (!defined('AOWOW_REVISION'))
// tabId 0: Database g_initHeader()
class AchievementsPage extends GenericPage
{
use ListPage;
use TrListPage;
protected $type = TYPE_ACHIEVEMENT;
protected $tpl = 'achievements';

View File

@@ -12,6 +12,8 @@ class ArenaTeamPage extends GenericPage
protected $lvTabs = [];
protected $type = TYPE_ARENA_TEAM;
protected $tabId = 1;
protected $path = [1, 5, 3];
protected $tpl = 'roster';

View File

@@ -10,6 +10,8 @@ class ArenaTeamsPage extends GenericPage
{
use TrProfiler;
protected $type = TYPE_ARENA_TEAM;
protected $tabId = 1;
protected $path = [1, 5, 3];
protected $tpl = 'arena-teams';

View File

@@ -8,7 +8,7 @@ if (!defined('AOWOW_REVISION'))
// tabId 0: Database g_initHeader()
class ClassPage extends GenericPage
{
use DetailPage;
use TrDetailPage;
protected $type = TYPE_CLASS;
protected $typeId = 0;

View File

@@ -8,7 +8,7 @@ if (!defined('AOWOW_REVISION'))
// tabId 0: Database g_initHeader()
class ClassesPage extends GenericPage
{
use ListPage;
use TrListPage;
protected $type = TYPE_CLASS;
protected $tpl = 'list-page-generic';

View File

@@ -8,7 +8,7 @@ if (!defined('AOWOW_REVISION'))
// tabId 0: Database g_initHeader()
class CurrenciesPage extends GenericPage
{
use ListPage;
use TrListPage;
protected $type = TYPE_CURRENCY;
protected $tpl = 'list-page-generic';

View File

@@ -8,7 +8,7 @@ if (!defined('AOWOW_REVISION'))
// tabId 0: Database g_initHeader()
class CurrencyPage extends GenericPage
{
use DetailPage;
use TrDetailPage;
protected $type = TYPE_CURRENCY;
protected $typeId = 0;

View File

@@ -8,7 +8,7 @@ if (!defined('AOWOW_REVISION'))
// tabid 0: Database g_initHeader()
class EmotePage extends GenericPage
{
use DetailPage;
use TrDetailPage;
protected $type = TYPE_EMOTE;
protected $typeId = 0;

View File

@@ -8,7 +8,7 @@ if (!defined('AOWOW_REVISION'))
// tabid 0: Database g_initHeader()
class EmotesPage extends GenericPage
{
use ListPage;
use TrListPage;
protected $type = TYPE_EMOTE;
protected $tpl = 'list-page-generic';

View File

@@ -8,7 +8,7 @@ if (!defined('AOWOW_REVISION'))
// tabId 0: Database g_initHeader()
class EnchantmentPage extends GenericPage
{
use DetailPage;
use TrDetailPage;
protected $type = TYPE_ENCHANTMENT;
protected $typeId = 0;

View File

@@ -8,7 +8,7 @@ if (!defined('AOWOW_REVISION'))
// tabId 0: Database g_initHeader()
class EnchantmentsPage extends GenericPage
{
use ListPage;
use TrListPage;
protected $type = TYPE_ENCHANTMENT;
protected $tpl = 'enchantments';

View File

@@ -8,7 +8,7 @@ if (!defined('AOWOW_REVISION'))
// tabId 0: Database g_initHeader()
class EventPage extends GenericPage
{
use DetailPage;
use TrDetailPage;
protected $type = TYPE_WORLDEVENT;
protected $typeId = 0;

View File

@@ -8,7 +8,7 @@ if (!defined('AOWOW_REVISION'))
// tabId 0: Database g_initHeader()
class EventsPage extends GenericPage
{
use ListPage;
use TrListPage;
protected $type = TYPE_WORLDEVENT;
protected $tpl = 'list-page-generic';

View File

@@ -8,7 +8,7 @@ if (!defined('AOWOW_REVISION'))
// tabId 0: Database g_initHeader()
class FactionPage extends GenericPage
{
use DetailPage;
use TrDetailPage;
protected $type = TYPE_FACTION;
protected $typeId = 0;

View File

@@ -8,7 +8,7 @@ if (!defined('AOWOW_REVISION'))
// tabId 0: Database g_initHeader()
class FactionsPage extends GenericPage
{
use ListPage;
use TrListPage;
protected $type = TYPE_FACTION;
protected $tpl = 'list-page-generic';

View File

@@ -4,7 +4,7 @@ if (!defined('AOWOW_REVISION'))
die('illegal access');
trait DetailPage
trait TrDetailPage
{
protected $hasComContent = true;
protected $category = null; // not used on detail pages
@@ -48,7 +48,7 @@ trait DetailPage
}
trait ListPage
trait TrListPage
{
protected $category = null;
protected $filter = [];
@@ -85,6 +85,16 @@ trait TrProfiler
protected $doResync = null;
protected function generateCacheKey($withStaff = true)
{
$staff = intVal($withStaff && User::isInGroup(U_GROUP_EMPLOYEE));
// mode, type, typeId, employee-flag, localeId, category, filter
$key = [$this->mode, $this->type, $this->subject->getField('id'), $staff, User::$localeId, '-1', '-1'];
return implode('_', $key);
}
protected function getSubjectFromUrl($str)
{
if (!$str)

View File

@@ -12,6 +12,8 @@ class GuildPage extends GenericPage
protected $lvTabs = [];
protected $type = TYPE_GUILD;
protected $tabId = 1;
protected $path = [1, 5, 2];
protected $tpl = 'roster';

View File

@@ -10,6 +10,8 @@ class GuildsPage extends GenericPage
{
use TrProfiler;
protected $type = TYPE_GUILD;
protected $tabId = 1;
protected $path = [1, 5, 2];
protected $tpl = 'guilds';

View File

@@ -8,7 +8,7 @@ if (!defined('AOWOW_REVISION'))
// tabId 0: Database g_initHeader()
class IconPage extends GenericPage
{
use DetailPage;
use TrDetailPage;
protected $type = TYPE_ICON;
protected $typeId = 0;

View File

@@ -8,7 +8,7 @@ if (!defined('AOWOW_REVISION'))
// tabId 0: Database g_initHeader()
class IconsPage extends GenericPage
{
use ListPage;
use TrListPage;
protected $type = TYPE_ICON;
protected $tpl = 'icons';

View File

@@ -8,7 +8,7 @@ if (!defined('AOWOW_REVISION'))
// tabId 0: Database g_initHeader()
class ItemPage extends genericPage
{
use DetailPage;
use TrDetailPage;
protected $type = TYPE_ITEM;
protected $typeId = 0;

View File

@@ -8,7 +8,7 @@ if (!defined('AOWOW_REVISION'))
// tabId 0: Database g_initHeader()
class ItemsPage extends GenericPage
{
use ListPage;
use TrListPage;
protected $type = TYPE_ITEM;
protected $tpl = 'items';

View File

@@ -8,7 +8,7 @@ if (!defined('AOWOW_REVISION'))
// tabId 0: Database g_initHeader()
class ItemsetPage extends GenericPage
{
use DetailPage;
use TrDetailPage;
protected $type = TYPE_ITEMSET;
protected $typeId = 0;

View File

@@ -8,7 +8,7 @@ if (!defined('AOWOW_REVISION'))
// tabId 0: Database g_initHeader()
class ItemsetsPage extends GenericPage
{
use ListPage;
use TrListPage;
protected $type = TYPE_ITEMSET;
protected $tpl = 'itemsets';

View File

@@ -8,7 +8,7 @@ if (!defined('AOWOW_REVISION'))
// tabId 0: Database g_initHeader()
class NpcPage extends GenericPage
{
use DetailPage;
use TrDetailPage;
protected $type = TYPE_NPC;
protected $typeId = 0;

View File

@@ -8,7 +8,7 @@ if (!defined('AOWOW_REVISION'))
// tabId 0: Database g_initHeader()
class NpcsPage extends GenericPage
{
use ListPage;
use TrListPage;
protected $type = TYPE_NPC;
protected $tpl = 'npcs';

View File

@@ -8,7 +8,7 @@ if (!defined('AOWOW_REVISION'))
// tabId 0: Database g_initHeader()
class ObjectPage extends GenericPage
{
use DetailPage;
use TrDetailPage;
protected $type = TYPE_OBJECT;
protected $typeId = 0;

View File

@@ -8,7 +8,7 @@ if (!defined('AOWOW_REVISION'))
// tabId 0: Database g_initHeader()
class ObjectsPage extends GenericPage
{
use ListPage;
use TrListPage;
protected $type = TYPE_OBJECT;
protected $tpl = 'objects';

View File

@@ -8,7 +8,7 @@ if (!defined('AOWOW_REVISION'))
// tabid 0: Database g_initHeader()
class PetPage extends GenericPage
{
use DetailPage;
use TrDetailPage;
protected $type = TYPE_PET;
protected $typeId = 0;

View File

@@ -8,7 +8,7 @@ if (!defined('AOWOW_REVISION'))
// tabid 0: Database g_initHeader()
class PetsPage extends GenericPage
{
use ListPage;
use TrListPage;
protected $type = TYPE_PET;
protected $tpl = 'list-page-generic';

View File

@@ -11,7 +11,9 @@ class ProfilePage extends GenericPage
use TrProfiler;
protected $gDataKey = true;
protected $mode = CACHE_TYPE_PAGE;
protected $mode = CACHE_TYPE_PAGE;
protected $type = TYPE_PROFILE;
protected $tabId = 1;
protected $path = [1, 5, 1];
@@ -125,7 +127,7 @@ class ProfilePage extends GenericPage
/* Onyxia */
/* ony */ 10184,
/* Flame Levi, Ignis, Razorscale, XT-002, Kologarn, Auriaya, Freya, Hodir, Mimiron, Thorim, Vezaxx, Yogg, Algalon */
/* uld */ 33113, 33118, 33186, 33293, 32930 33515, 32906, 32845, 33350, 32864, 33271, 33288, 32871
/* uld */ 33113, 33118, 33186, 33293, 32930, 33515, 32906, 32845, 33350, 32864, 33271, 33288, 32871,
/* Anub, Faerlina, Maexxna, Noth, Heigan, Loatheb, Razuvious, Gothik, Patchwerk, Grobbulus, Gluth, Thaddius, Sapphiron, Kel'Thuzad */
/* nax */ 15956, 15953, 15952, 15954, 15936, 16011, 16061, 16060, 16028, 15931, 15932, 15928, 15989, 15990
);

View File

@@ -12,6 +12,8 @@ class ProfilesPage extends GenericPage
protected $roster = 0; // $_GET['roster'] = 1|2|3|4 .. 2,3,4 arenateam-size (4 => 5-man), 1 guild .. it puts a resync button on the lv...
protected $type = TYPE_PROFILE;
protected $tabId = 1;
protected $path = [1, 5, 0];
protected $tpl = 'profiles';
@@ -34,7 +36,7 @@ class ProfilesPage extends GenericPage
if ($this->realm && $r['name'] != $this->realm)
continue;
$this->sumSubjects += DB::Characters($idx)->selectCell('SELECT count(*) FROM characters WHERE deleteInfos_Name IS NULL');
$this->sumSubjects += DB::Characters($idx)->selectCell('SELECT count(*) FROM characters WHERE deleteInfos_Name IS NULL AND level <= ?d AND (extra_flags & ?) = 0', MAX_LEVEL, Profiler::CHAR_GMFLAGS);
$realms[] = $idx;
}

View File

@@ -8,7 +8,7 @@ if (!defined('AOWOW_REVISION'))
// tabId 0: Database g_initHeader()
class QuestPage extends GenericPage
{
use DetailPage;
use TrDetailPage;
protected $type = TYPE_QUEST;
protected $typeId = 0;

View File

@@ -7,7 +7,7 @@ if (!defined('AOWOW_REVISION'))
// tabId 0: Database g_initHeader()
class QuestsPage extends GenericPage
{
use ListPage;
use TrListPage;
protected $type = TYPE_QUEST;
protected $tpl = 'quests';

View File

@@ -8,7 +8,7 @@ if (!defined('AOWOW_REVISION'))
// tabId 0: Database g_initHeader()
class RacePage extends GenericPage
{
use DetailPage;
use TrDetailPage;
protected $type = TYPE_RACE;
protected $typeId = 0;

View File

@@ -8,7 +8,7 @@ if (!defined('AOWOW_REVISION'))
// tabId 0: Database g_initHeader()
class RacesPage extends GenericPage
{
use ListPage;
use TrListPage;
protected $type = TYPE_RACE;
protected $tpl = 'list-page-generic';

View File

@@ -8,7 +8,7 @@ if (!defined('AOWOW_REVISION'))
// tabId 0: Database g_initHeader()
class SkillPage extends GenericPage
{
use DetailPage;
use TrDetailPage;
protected $type = TYPE_SKILL;
protected $typeId = 0;

View File

@@ -8,7 +8,7 @@ if (!defined('AOWOW_REVISION'))
// tabId 0: Database g_initHeader()
class SkillsPage extends GenericPage
{
use ListPage;
use TrListPage;
protected $type = TYPE_SKILL;
protected $tpl = 'list-page-generic';

View File

@@ -8,7 +8,7 @@ if (!defined('AOWOW_REVISION'))
// tabId 0: Database g_initHeader()
class SoundPage extends GenericPage
{
use DetailPage;
use TrDetailPage;
protected $type = TYPE_SOUND;
protected $tpl = 'sound';

View File

@@ -8,7 +8,7 @@ if (!defined('AOWOW_REVISION'))
// tabId 0: Database g_initHeader()
class SoundsPage extends GenericPage
{
use ListPage;
use TrListPage;
protected $type = TYPE_SOUND;
protected $tpl = 'sounds';

View File

@@ -8,7 +8,7 @@ if (!defined('AOWOW_REVISION'))
// tabId 0: Database g_initHeader()
class SpellPage extends GenericPage
{
use DetailPage;
use TrDetailPage;
protected $type = TYPE_SPELL;
protected $typeId = 0;

View File

@@ -8,7 +8,7 @@ if (!defined('AOWOW_REVISION'))
// tabId 0: Database g_initHeader()
class SpellsPage extends GenericPage
{
use ListPage;
use TrListPage;
protected $type = TYPE_SPELL;
protected $tpl = 'spells';

View File

@@ -8,7 +8,7 @@ if (!defined('AOWOW_REVISION'))
// tabId 0: Database g_initHeader()
class TitlePage extends GenericPage
{
use DetailPage;
use TrDetailPage;
protected $type = TYPE_TITLE;
protected $typeId = 0;

View File

@@ -8,7 +8,7 @@ if (!defined('AOWOW_REVISION'))
// tabId 0: Database g_initHeader()
class TitlesPage extends GenericPage
{
use ListPage;
use TrListPage;
protected $type = TYPE_TITLE;
protected $tpl = 'list-page-generic';

View File

@@ -8,7 +8,7 @@ if (!defined('AOWOW_REVISION'))
// tabId 0: Database g_initHeader()
class ZonesPage extends GenericPage
{
use ListPage;
use TrListPage;
protected $type = TYPE_ZONE;
protected $tpl = 'list-page-generic';

View File

@@ -9692,7 +9692,9 @@ Listview.funcBox = {
}
}
if (achievementPoints > 0) {
// aowow: changed because legitemately passing zero APs from the profiler is a thing
// (achievementPoints > 0) {
if (typeof achievementPoints == 'number') {
if (ns) {
$WH.ae(d, $WH.ct(' '));
}