*Mini PC と チューナーレスTV とで広告用看板を作る
購入するもの
- Mini PC
- TV(チューナレス)
- 壁固定金具
- 適当なHDMIケーブルと電源類(USB TypeC PB を使って必要なボルトの電源を確保できればなお吉)
Mini PC の BIOS を設定して、通電したらPCを自動起動になるように設定する
Aptio Setup - AMI
Boot -> State After G3 -> [S0 State]
他のPCの場合の参照URLhttps://hp.vector.co.jp/authors/VA022629/Manual/PageBIOS.htm
補足
- S0 PC稼働状態
- S1 省電力状態
- S2 省電力状態(CPUへの給電はオフ、チップセット給電はオン)
- S3 スリープ状態(メモリへは給電されている)
- S4 休止状態(高速スタートアップを有効にしているときのシャットダウン状態)
- S5 シャットダウン状態(完全シャットダウン)
- Firefox のリンク先の設定
>||"C:\Program Files\Mozilla Firefox\firefox.exe" http://対象となるURL --kiosk|
適当に画面が切り替わるようなプログラムを書いて、サーバにアップする
Javascript 関数を繰り返して呼び出す
callBack 関数が true を返す間、定期的に関数を呼び出す
class Repeater { #cnt = null; #timerId = null; #loop = function () { if (this.#cnt && this.cbFunc (--this.#cnt) && this.#cnt) this.#timerId = setTimeout (this.#loop.bind (this), this.interval); else this.stop (); } //_______ constructor (cbFunc, count, interval) { this.cbFunc = cbFunc; this.count = this.#cnt = count; this.interval = interval; } start (force = false) { this.stop (); if (force) this.#cnt = this.count; this.#loop (); return this; } stop (force = false) { if (this.#timerId) this.#timerId = clearTimeout (this.#timerId) || null; if (force) this.#cnt = 0; return this; } //_______ static create (cbFunc, count = 1, interval = 1000) { if ('function' !== typeof cbFunc) throw new Error ('関数ではありません'); let obj = new this (cbFunc, count, interval); return obj.start (); } }
callBack 関数が true を返す間、定期的に関数を呼び出す。その2
勉強のため promise を使ってみた。
class AsyncRepeater { #cnt = null; #timerId = null; #loop = function () { if (this.#cnt && this.cbFunc (--this.#cnt) && this.#cnt) //マイナスに対し ―∞ へ this.#timerId = setTimeout (this.#loop.bind (this), this.interval); else this.end (); }; //_______ constructor (cbFunc, count, interval, resolve, reject) { this.cbFunc = cbFunc; this.count = this.#cnt = count; this.interval = interval; this.resolve = resolve; this.reject = reject; } start (force = false) { this.stop (); if (force) this.#cnt = this.count; this.#loop (); return this; } restart () { this.#loop (); return this; } stop () {//あくまでも中断 if (this.#timerId) this.#timerId = clearTimeout (this.#timerId) || null; return this; } end (force = false) { this.stop (); if (force && this.reject)//強制中断 this.reject (); else if (this.resolve) this.resolve (); return this; } //_______ static create (cbFunc, count = 1, interval = 1000, async = true) { if ('function' !== typeof cbFunc) throw new Error (); let obj, resolve, reject; let p = async ? new Promise ((a, b)=> {resolve = a; reject = b}) : null; obj = new this (cbFunc, count, interval, resolve, reject); Object.assign (obj, {promise: p}); obj.start (); return obj; } }
応用して、画面をフェードイン・フェードアウトで切り替えられるチェンジャーを作ってみた
css のアニメーションが楽だったような感じ。
LI 要素に data-change_time を設け、以下のようにセットする
構文は、[フェードインまでの時間, 表示し続ける時間, フェードアウトまでの時間, 空白の時間] とする
停止・再開機能を付けようと思ったが中止。
フリーソフトの exeUSB なるものを見つけた。
USBメモリを装着すると、自動で指定のアプリを起動できる
そこで Chrome --kiosk のモードで起動すれば、電子看板のように使える
html
<ul id="SAMPLE"> <li data-change_time="1000,1500,1000,1000"> <li ...> ...
Script
DisplayChanger.create (SAMPLE);
class DisplayChanger { #skip = function (n = 1) { // - !? for (let i = 0, I = n % this.children.length; i < I; i++) this.parent.appendChild (this.current); }; #getStateTimes (e = this.current) { let state = /^(?:\s*([+-]?\d*)(?:,\s*([+-]?\d*)(?:,\s*([+-]?\d*)(?:,\s*([+-]?\d*)?)?)?)?)?(?:,.*)?$/.exec (e.dataset.change_time); if (null == state) console.error (e, e.dataset.change_time); return state.slice (1).map ((a, i, _, val = parseInt (a))=> isNaN (val) ? this.default[i]: val); }; #next = function () { this.#skip (); this.#loop (); }; #loop = async function () { let e = this.current; let [t0, t1, t2, t3] = this.#getStateTimes (e); let i = this.interval; await (AsyncRepeater.create (this.#fadeIn (e, t0, i), -1, i)).promise; await this.#keep (t1); await (AsyncRepeater.create (this.#fadeOut (e, t2, i), -1, i)).promise; await this.#keep (t3); this.#next (); }; #keep = function (a) { return new Promise (c=> setInterval (c, a)); } #fadeIn = function (e, time, interval) { let max = Math.round (time / interval); let step = 1 / max; let opacity = 0; return function () { opacity += step; e.style.opacity = (1 < opacity) ? 1: opacity; return opacity < 1; }; } #fadeOut = function (e, time, interval) { let max = Math.round (time / interval); let step = 1 / max; let opacity = 1; return function () { opacity -= step; e.style.opacity = (opacity < 0) ? 0: opacity; return 0 < opacity; }; } //_____ constructor (a, b, c, d) { this.parent = a; this.begin = b; this.interval = c; this.default = d;//[fadeInTime, displayTime, fadeOutTime, blankTime] this.#skip (b); this.isInOpration = false; //作動中か? } start () { this.isInOpration = true; this.#loop (); } //_____ get current () { return this.parent.firstElementChild; } get children () { return [...this.parent.children]; } //_____ static create (parent, begin = 0, interval = 20, fadeInTime = 1000, displayTime = 5000, fadeOutTime = 500, blankTime = 300) { if (1 > arguments.length) throw new Error ('引数がありません'); [...parent.children].forEach (e=> e.style.opacity = 0); let tm = parent.dataset; interval = Math.max (1, parseFloat (tm.interval || interval)); fadeInTime = Math.max (1, parseFloat (tm.fadeInTime || fadeInTime)); displayTime= Math.max (1, parseFloat (tm.displayTime || displayTime)); fadeOutTime= Math.max (1, parseFloat (tm.fadeOutTime || fadeOutTime)); blankTime = Math.max (1, parseFloat (tm.blankTime || blankTime)); let obj = new this (parent, begin, interval, [fadeInTime, displayTime, fadeOutTime, blankTime]); obj.start (); return obj; } }
配列からテーブルを作る2
配列からテーブルを作る2
配列からテーブルを作る場合、セルを結合したりclassName を指定したい時がある。そこで配列のセルに該当するテキストから colSpan , rowSpan などを取り出せるようにした。
- 文字列の先頭が "#" ならば TH 要素になる
- ”!r4c2 def" ならば rowSpan=4, colspan=2, textContent = "def" となる
- "!{"className":"red"} ghi ならば class="red", textContent = "ghi" となる。 { } で囲まれる部分は、JSON 形式に変換されるのでダブルクォーテーションを適材適所に使用すること。また {"innerHTML":"<a href='xxx'>Test</a>"}とすれば子要素以降も文字列から生成できる。その場合は適切に文字をエスケープすること。
let ary = [ ["#!r4{"className":"red"} abc", "#!c4 def, ... ], [...], ... ]
function ary2thead (ary = [ ], thead = document.createElement ('thead')) { for (let row of ary) thead.insertRow().append(...row.map(cell=>{ let [, th, rowSpan, colSpan, json, textContent = ''] = (new RegExp ('^(#)?(?:!(?=[rc\\{])(?:r([2-9]|[1-9]\\d+)|r[01]|r0\\d+)?(?:c([2-9]|[1-9]\\d+)|c[01]|c0\\d+)?({.+})?)?\\s*(.*)\\s*$','g')).exec (cell) ?? [ ]; return Object.assign (document.createElement (th ? 'th': 'td'), Object.entries ({rowSpan, colSpan, textContent}).reduce((a,[b,c])=>((c===undefined||(a[b]=c)),a), {}), json ? JSON.parse(json): {}); })); return thead; }
短く
const ary2thead= (a=[],b=document.createElement('thead'))=>{ for(let c of a)b.insertRow().append(...c.map(d=>{ let[,e,f,g,h,i='']=(new RegExp('^(#)?(?:!(?=[rc\\{])(?:r([2-9]|[1-9]\\d+)|r[01]|r0\\d+)?(?:c([2-9]|[1-9]\\d+)|c[01]|c0\\d+)?({.+})?)?\\s*(.*)\\s*$','g')).exec(d)??[]; return Object.assign(document.createElement(e?'th':'td'),Object.entries({rowSpan:f,colSpan:g,textContent:i}).reduce((j,[k,l])=>((l===undefined||(j[k]=l)),j),{}),h?JSON.parse(h):{}); }));return b};
文字列からテーブルを作る
配列からテーブルを作る
<!DOCTYPE html> <meta charset="utf-8"> <title></title> <style> .red { color: red;} </style> <body> <table border="1" id="TB"></table> <script> function ary2thead (ary = [ ], thead = document.createElement ('thead')) { const textSplitStr = ( th = '(#)?', //文字列の先頭が "#" なら th要素とするための判別 rowSpan = '(?:r([2-9]|[1-9][0-9]+)|r[01]|r0[0-9]+)?',//rowSpan の2以上を対象として取得 colSpan = '(?:c([2-9]|[1-9][0-9]+)|c[01]|c0[0-9]+)?',//colSpan の2以上を対象として取得 attr = '(?:\\{(.+)?\\})?',//attribute として { ... } 括弧の内側を文字列として取得 text = '\\s*(.*)\\s*', //前後の空白を取り除いて取得 ctrlCom = '(?:!(?=[rc\\{])'+ rowSpan + colSpan + attr + ')?' )=> '^' + th + ctrlCom + text + '$', attrSplitStr = '\\s*(.+?)\\s*\\:\\s*(.*?)\\s*(?:,|;|$)', rows = [ ]; for (let row of ary) { let tds = []; for (let cell of row) { let splitter = new RegExp (textSplitStr (), 'gi'); [, th, rowSpan, colSpan, attr, textContent = ''] = splitter.exec (cell) ?? [ ], prop = new Map; if (rowSpan) prop.set ('rowSpan', rowSpan); if (colSpan) prop.set ('colSpan', colSpan); if (attr) for (let a, splitter = new RegExp (attrSplitStr, 'gi'); a = splitter.exec (attr); ) prop.set (a[1],a[2]); prop.set ('textContent', textContent); tds.push (Object.assign (document.createElement (th ? 'th': 'td'), Object.fromEntries (prop))); } rows.push (tds); } rows.reduce ((a, b)=>(a.insertRow ().append (...b), a), thead); return thead; } const RECS = ` #!R2C1 No #!r2 Name #!r1c2 INFO #Age #ETC #1 akio 57 abc #!{id:sss,className: red}2 ayako 56 def `; const csv2ary = csv => csv.split (/r\n|\r|\n/g).reduce ((a,b)=>b.trim()?[...a,b]:a,[]).map (row=> row.split (/\t/g)); ary2thead (csv2ary (RECS), TB); </script>
ショートコーディングしてみる
<!DOCTYPE html> <meta charset="utf-8"> <title></title> <style> .red { color: red;} </style> <body> <table border="1" id="TB"></table> <script> function ary2tbody (recs = [ ], tbody = document.createElement ('tbody')) { function* splitter(a,b=new RegExp(attrSplitStr,'g'),c){while(c=b.exec(a))yield c.slice(1)} const addObj = (a,[b,c])=>(a[b]=c,a), delObj = (a,[b,c])=>((undefined===c)&&delete a[b],a), makeTD = (a)=>{ let [, th, rowSpan, colSpan, attr, textContent] = (new RegExp (textSplitStr, 'i')).exec (a) ?? [ ],//文字列を分解 prop = [...splitter (attr)].reduce (addObj, { rowSpan, colSpan, textContent });//要素の属性 return Object.assign ( document.createElement (th ? 'th': 'td'), //フラグとしての th により生成 Object.entries (prop).reduce (delObj, prop) // prop オブジェクトから値が無いものは削除する ) }, attrSplitStr = '\\s*(.+?)\\s*\\:\\s*(.*?)\\s*(?:,|;|$)',//文字列から属性値を切り出す "key:value, key2:value2, ..." textSplitStr = //#!r2c3{className:red} text '^' + '(#)?' + //th 文字列の先頭が "#" なら th要素とするための判別 '(?:!(?=[rc{])'+ //制御文字は !で始まり以下の順で記述する '(?:r([2-9]|[1-9][0-9]+)|r[01]|r0[0-9]+)?' + //rowSpan の2以上を対象として取得 '(?:c([2-9]|[1-9][0-9]+)|c[01]|c0[0-9]+)?' + //colSpan の2以上を対象として取得 '(?:\\{(.+)?\\})?' + //{attr: 123, attr2: 456} の用に属性を取得する ')?' + '\\s*(.*)\\s*' + '$';//textContent 前後の空白を取り除いて取得 recs.forEach (row=> tbody.insertRow ().append (...row.map (makeTD))); return tbody; } const RECS = ` #!R2C1 No #!r2 Name #!r1c2 INFO #Age #ETC #1 akio 57 abc #!{id:sss,className: red}2 ayako 56 def `; const csv2ary = csv => csv.split (/r\n|\r|\n/g).reduce ((a,b)=>b.trim()?[...a,b]:a,[]).map (row=> row.split (/\t/g)); ary2thead (csv2ary (RECS), TB); </script>
さらにショート!
<!DOCTYPE html> <meta charset="utf-8"> <title></title> <style> .red { color: red;} </style> <body> <table border="1" id="TB"></table> <script> const ary2tbody=(a=[],b=document.createElement('tbody'))=>{ function*c(a,b=new RegExp('\\s*(.+?)\\s*\\:\\s*(.*?)\\s*(?:,|;|$)','g'),c){while(c=b.exec(a))yield c.slice(1)} const d=(a,[b,c])=>(a[b]=c,a),e=(a,[b,c])=>((undefined===c)&&delete a[b],a),f=g=>{let[,h,i,j,k,l]=(new RegExp('^(#)?(?:!(?=[rc{])(?:r([2-9]|[1-9][0-9]+)|r[01]|r0[0-9]+)?(?:c([2-9]|[1-9][0-9]+)|c[01]|c0[0-9]+)?(?:\\{(.+)?\\})?)?\\s*(.*)\\s*$','i')).exec(g)??[],m=[...c(k)].reduce(d,{rowSpan:i,colSpan:j,textContent:l});return Object.assign(document.createElement(h?'th':'td'),Object.entries(m).reduce(e,m))}; a.forEach(c=>b.insertRow().append(...c.map(f)));return b} const RECS = ` #!R2C1 No #!r2 Name #!r1c2 INFO #Age #ETC #1 akio 57 abc #!{id:sss,className: red}2 ayako 56 def `; const csv2ary = csv => csv.split (/r\n|\r|\n/g).reduce ((a,b)=>b.trim()?[...a,b]:a,[]).map (row=> row.split (/\t/g)); ary2tbody (csv2ary (RECS), TB); </script>
再考して更に短く
const ary2tbody=(a=[],b=document.createElement('tbody'))=>{ let A='^(#)?(?:!(?=[rc\\{])(?:r([2-9]|[1-9]\\d+)|r[01]|r0\\d+)?(?:c([2-9]|[1-9]\\d+)|c[01]|c0\\d+)?(?:\\{(.+)?\\})?)?\\s*(.*)\\s*$',B=RegExp,C=Object; a.map(c=>{b.insertRow().append(...c.map(d=>{let[,z,y,x,w,v]=(new B(A,'i')).exec(d)??[],u={rowSpan:y,colSpan:x,textContent:v}; C.entries(u).map(a=>a[1]===undefined&&delete u[a[0]]);for(let a,b=new B('\\s*(.+?)\\s*\\:\\s*(.*?)\\s*(,|;|$)','g');a=b.exec(w);u[a[1]]=a[2]); return C.assign(document.createElement(z?'th':'td'),u)}))});return b};
javascriptで行列の演算をする
class Matrix { constructor (ary) { this.matrix = ary } add (arg) { this.matrix = this.constructor.add (this.matrix, arg.matrix) } sub (arg) { this.matrix = this.constructor.sub (this.matrix, arg.matrix) } mult (arg) { this.matrix = this.constructor.mult (this.matrix, arg.matrix) } get transpose () { return this.constructor.transpose (this.matrix) } get inverse () { return this.constructor.inverse (this.matrix) } //行列の加算 static add (a, b) { return a.map((a,i)=> b[i].map((b,j)=> a[j]+b)) } //行列の減算 static sub (a, b) { return a.map((a,i)=> b[i].map((b,j)=> a[j]-b)) } //行列の積 static mult (a, b) { return a.map(a=> a.map((_, i)=> b.reduce((g,_,j)=> g + a[j]*b[j][i],0))) } //転置 static transpose (a) { return a[0].map((_,i)=> a.map ((_,j)=> a[j][i]))} //単位行列 static identity (n) { return [...Array(n)].fill(0).map((a,b,c,d=[...c])=>(d[b]=1,d)) } //逆行列 static inverse (A) { return A.reduce((a,b,c,d,e=b[c],f=a[c])=>A.reduce((a,h,i,_,j=h[c],k=A[i],l=a[i])=>(c==i||A.forEach((_,m)=>(k[m]-=b[m]*j,l[m]-=f[m]*j)),a),(A.forEach((_,i)=>(b[i]/=e,f[i]/=e)),a)),this.identity(A.length))} } //_______________ let mt0 = [ [ 1, 2, 0,-1], [-1, 1, 2, 0], [ 2, 0, 1, 1], [ 1,-2,-1, 1] ]; let rst = Matrix.inverse (mt0); console.log(rst);
2*2
/* | a b | | c d | */ class Matrix22 { #matrix=new Array(4); constructor(A=1,B=0,C=0,D=1){this.#matrix=[A,B,C,D]} add(M){let[A,B,C,D]=this.#matrix,[a,b,c,d]=M.matrix;this.#matrix=[A+a,B+b,C+c,D+d];return this}//加算 sub(M){let[A,B,C,D]=this.#matrix,[a,b,c,d]=M.matrix;this.#matrix=[A-a,B-b,C-c,D-d];return this}//減算 scalarMultiple(n=1){let[A,B,C,D]=this.#matrix;this.#matrix=[A*n,B*n,C*n,D*n];return this}//スカラー倍 multiply(M){let[A,B,C,D]=this.#matrix,[a,b,c,d]=M.matrix;this.#matrix=[A*a+B*c,A*b+B*d,C*a+D*c,C*b+D*d];return this}//乗算 get matrix(){return this.#matrix}//内部の値を配列で返す get det(){let[A,B,C,D]=this.#matrix;return A*D-B*C}//行列式(determinant) get transpose(){let[A,B,C,D]=this.#matrix;return new this.constructor(A,C,B,D)}//転置 get inverse(){let[A,B,C,D]=this.#matrix,e=1/this.det;return new this.constructor(D*e,-B*e,-C*e,A*e)}//逆数 } /* | a b c | | d e f | | g h i | */ class Matrix33 { #matrix=new Array(9); constructor(a=1,b=0,c=0,d=0,e=1,f=0,g=0,h=0,i=1){this.#matrix=[a,b,c,d,e,f,g,h,i]} add(M){let[A,B,C,D,E,F,G,H,I]=this.#matrix,[a,b,c,d,e,f,g,h,i]=M.matrix;this.#matrix=[A+a,B+b,C+c,D+d,E+e,F+f,G+g,H+h,I+i];return this}//加算 sub(M){let[A,B,C,D,E,F,G,H,I]=this.#matrix,[a,b,c,d,e,f,g,h,i]=M.matrix;this.#matrix=[A-a,B-b,C-c,D-d,E-e,F-f,G-g,H-h,I-i];return this}//減算 scalarMultiple(n=1){let[A,B,C,D,E,F,G,H,I]=this.#matrix;this.#matrix=[A*n,B*n,C*n,D*n,E*n,F*n,G*n,H*n,I*n];return this}//スカラー倍 multiply (M) {let[A,B,C,D,E,F,G,H,I]=this.#matrix,[a,b,c,d,e,f,g,h,i]=M.matrix;this.#matrix=[A*a+B*d+C*g,A*b+B*e+C*h,A*c+B*f+C*i,D*a+E*d+F*g,D*b+E*e+F*h,D*c+E*f+F*i,G*a+H*d+I*g,G*b+H*e+I*h,G*c+H*f+I*i];return this}//乗算 get transpose(){let[A,B,C,D,E,F,G,H,I]=this.#matrix;return new this.constructor(A,D,G,B,E,H,C,F,I)}//転置 get det(){let[A,B,C,D,E,F,G,H,I]=this.#matrix;return A*E*I-A*F*H+B*F*G-B*D*I+C*D*H-C*E*G}//行列式(determinant) get inverse(){let[A,B,C,D,E,F,G,H,I]=this.#matrix,e=1/this.det;return new this.constructor((E*I-F*H)*e,-(B*I-C*H)*e,(B*F-C*E)*e,-(D*I-F*G)*e,(A*I-C*G)*e,-(A*F-C*D)*e,(D*H-E*G)*e,-(A*H-B*G)*e,(A*E-B*D)*e)}//逆数 }
JavaScript で「順列」と「組合わせ」を計算する
//組合わせ c= 1:重複あり, 0:重複なし function combinations (a = [ ], b = a.length, c = 1) { return 2 > b ? a.map(a =>[a]) : a.flatMap ((d,e)=> combinations (a.slice (e+1-c), b-1, c).map (e=> [d,...e])); } //順列 c= 1:重複あり, 0:重複なし function permutations (a =[ ], b = a.length, c = 1) { return 2 > b ? a.map (a => [a]) : a.flatMap ((d,e,[...f]) => permutations ((f.splice (e,1-c), f), b - 1, c).map(a => [d, ...a])); } let ary = [1,2,3,4]; console.log(combinations(ary,3)); console.log(Permutations(ary,3,0));
1行で書く
//組合わせ c= 1:重複あり, 0:重複なし const permutations=((F=(a=[],b=a.length,c=1)=>2>b?a.map(d=>[d]):a.flatMap((d,e,[...f])=> F((f.splice(e,1-c),f),b-1,c).map(a=>[d,...a])))=>F)(); //順列 c= 1:重複あり, 0:重複なし const combinations=((f=(a=[],b=a.length,c=1)=>2>b?a.map(d=>[d]):a.flatMap((d,e)=>f(a.slice (e+1-c),b-1,c).map(a=>[d,...a])))=>f)();
「組合わせ」を返す関数
function mathematics (ary = [ ], n = ary.length) { if (1 === n) { return ary.map (a=> [a]); } else { return ary.flatMap ((a, i)=> mathematics (ary.slice (i), n -1).map (b=> [a,...b])) } } console.log (mathematics ([1,2,3],3)); /* [1,1,1], [1,1,2], [1,1,3], [1,2,2], [1,2,3], [1,3,3], [2,2,2], [2,2,3], [3,3,3] */
やっぱり1行で済ます
const mathematics=((f=(a=[],b=a.length)=>b==1?a.map(c=>[c]):a.flatMap((c,d)=>f(a.slice(d),b-1).map(d=>[c,...d])))=>f)();
で目的は
Javascript で、0000 から 9999 までの表を作りたい。 -HTML の <table- JavaScript | 教えて!goo
<!DOCTYPE html> <title></title> <meta charset="utf-8"> <style> td { text-align: right; width: 6em;} </style> <body> <table border="1" id="T"></table> <script> const ary2tbody=(a,b=document.createElement('tbody'))=>a.reduce((b,a)=>(a.reduce((c,d)=>(c.insertCell().append(d||''),c),b.insertRow()),b),b), transpose=a=>a[0].map((_,i)=>a.map(b=>b[i])), mathematics=((f=(a=[],b=a.length)=>b==1?a.map(c=>[c]):a.flatMap((c,d)=>f(a.slice(d),b-1).map(d=>[c,...d])))=>f)(), sum=(a,b)=>a+b; const A = [1, 2, 3, 4, 5, 6, 7, 8, 9]; const B = [[0], ...mathematics (A,1), ...mathematics (A,2), ...mathematics (A,3), ...mathematics (A,4)]; const C = [...Array(10)].map (()=>[]); B.forEach (a=> C[a.reduce (sum, 0) % 10].push (a ? a.join``.padStart (4,0) : '')); ary2tbody (transpose(C), T); </script>