UNPKG

ziko

Version:

A versatile JavaScript library offering a rich set of Hyperscript Based UI components, advanced mathematical utilities, interactivity ,animations, client side routing and more ...

5,608 lines 160 kB
/*
  Project: ziko.js
  Author: Zakaria Elalaoui
  Date : Thu Dec 25 2025 01:19:21 GMT+0100 (UTC+01:00)
  Git-Repo : https://github.com/zakarialaoui10/ziko.js
  Git-Wiki : https://github.com/zakarialaoui10/ziko.js/wiki
  Released under MIT License
*/

'use strict';

const { PI: PI$1, E } = Math;
const EPSILON=Number.EPSILON;

const is_primitive = value => typeof value !== 'object' && typeof value !== 'function' || value === null;

const mapfun=(fun,...X)=>{
    const Y=X.map(x=>{
        if(is_primitive(x) || x?.__mapfun__) return fun(x)
        if(x instanceof Array) return x.map(n=>mapfun(fun,n));
        if(ArrayBuffer.isView(x)) return x.map(n=>fun(n));
        if(x instanceof Set) return new Set(mapfun(fun,...[...x]));
        if(x instanceof Map) return new Map([...x].map(n=>[n[0],mapfun(fun,n[1])]));
        if(x.isMatrix?.()) return new x.constructor(x.rows, x.cols, mapfun(x.arr.flat(1)))
        else if(x instanceof Object){
            return Object.fromEntries(
                Object.entries(x).map(
                    n=>n=[n[0],mapfun(fun,n[1])]
                )
            )
        }
    });
   return Y.length==1? Y[0]: Y; 
};

const apply_fun = (x, fn) => {
    if (x.isComplex?.()) return new x.constructor(
        fn(x.a),
        fn(x.b)
    )
    if (x.isMatrix?.()) return new x.constructor(
        x.rows,
        x.cols,
        x.arr.flat(1).map(fn)
    )
    if (x instanceof Array) mapfun(fn, ...x);
    return fn(x)
};

const base2base = (value, fromBase, toBase) => {

    const dec = parseInt(value, fromBase);
    if (Number.isNaN(dec)) throw new TypeError('Invalid value for the given base');

    return dec.toString(toBase);
};

const arithmetic_helper=(op, x, y)=>{
    if(typeof x === 'number'){
        if(typeof y === 'number'){
            switch(op){
                case 'add' : return x + y;
                case 'sub' : return x - y;
                case 'mul' : return x * y;
                case 'div' : return x / y;
                case 'modulo' : return x % y;
            }
        }
        if(y?.isComplex?.()) x = new y.constructor(x, 0);
        if(y?.isMatrix?.()) x = y.constructor.nums(y.rows, y.cols, x);
        return x[op](y)
    }
    if(x?.isComplex?.()){
        if(typeof y === 'number' || y?.isComplex?.()) return x.clone()[op](y);
        if(y?.isMatrix?.()){
            x = y.constructor.nums(y.rows, y.cols, x);
            return x.clone()[op](y)
        }
    }
    if(x?.isMatrix?.()){
        return x.clone()[op](y)
    }    
};
const add=(a,...b)=>{
    let res = a;
    for(let i=0; i<b.length; i++)
        res = arithmetic_helper('add', res, b[i]);
    return res;
};
const sub=(a,...b)=>{
    let res = a;
    for(let i=0; i<b.length; i++)
        res = arithmetic_helper('sub', res, b[i]);
    return res;
};
const mul=(a,...b)=>{
    let res = a;
    for(let i=0; i<b.length; i++)
        res = arithmetic_helper('mul', res, b[i]);
    return res;
};
const div=(a,...b)=>{
    let res = a;
    for(let i=0; i<b.length; i++)
        res = arithmetic_helper('div', res, b[i]);
    return res;
};
const modulo=(a,...b)=>{
    let res = a;
    for(let i=0; i<b.length; i++)
        res = arithmetic_helper('modulo', res, b[i]);
    return res;
};

const min = (...x) => Math.min(...x);
const max = (...x) => Math.max(...x);

const binomial = (n, k) =>{
  if(n !== Math.floor(n)) return TypeError('n must be an integer');
  if(k !== Math.floor(k)) return TypeError('k must be an integer');
  if (n < 0) return TypeError('n must be non-negative');
  if (k < 0 || n < 0 || k > n) return 0;
  if (k > n - k) k = n - k;
    let c = 1, i;
    for (i = 0; i < k; i++)
        c = c * (n - i) / (i + 1);
    return c;
};

const mean = (...x) => x.reduce((a, b) => a + b) / x.length;

const variance = (...x) => {
  const n = x.length;
  if (n === 0) return NaN;
  const x_mean = mean(...x);
  return x.reduce((sum, xi) => sum + (xi - x_mean) ** 2, 0) / n;
};

const std = (...x) => Math.sqrt(variance(...x));

const accum_sum = (...x) => {
  let result = [];
  let total = 0, i, n = x.length;
  for(i = 0; i < n ; i++){
    total = add(total, x[i]);
    result.push(total);
  }
  return result;
};

const accum_prod = (...x) => {
  let result = [];
  let prod = 1, i, n = x.length;
  for(i = 0; i < n ; i++){
    prod = mul(prod, x[i]);
    result.push(prod);
  }
  return result;
};

const percentile = (X, p) => {
  if (X.length === 0) 
    return NaN;
  let a = [...X].sort((x, y) => x - y);
  let index = (p / 100) * (a.length - 1);
  let i = Math.floor(index);
  let f = index - i;
  if (i === a.length - 1) 
    return a[i]; 
  return a[i] * (1 - f) + a[i + 1] * f;
};

const median = X => percentile(X, 50);

class Random {
    static int(a, b){
        return Math.floor(this.float(a, b));
    }
    static float(a, b){
        return b !== undefined
            ? Math.random() * (b - a) + a
            : Math.random() * a;
    }
    static bin(){
        return this.int(2);
    }
    static oct(){
        return this.int(8);
    }
    static dec(){
        return this.int(10);
    }
    static hex(){
        return base2base(this.int(16), 10, 16);
    }
    static char(upperCase = false){
        const i = upperCase
            ? this.int(65, 91)
            : this.int(97, 123);
        return String.fromCharCode(i);
    }
    static bool(){
        return Boolean(this.int(2));
    }
    static get color(){
        return {
            hex : () =>
                `#${this.int(0xffffff).toString(16).padStart(6, '0')}`,

            hexa : () => {
                const [r,g,b,a] = Array.from(
                    {length:4},
                    () => this.int(0xff).toString(16).padStart(2,'0')
                );
                return `#${r}${g}${b}${a}`;
            },
            rgb : () => {
                const [r,g,b] = Array.from({length:3}, () => this.int(0xff));
                return `rgb(${r}, ${g}, ${b})`;
            },
            rgba : () => {
                const [r,g,b] = Array.from({length:3}, () => this.int(0xff));
                const a = Math.random().toFixed(2);
                return `rgba(${r}, ${g}, ${b}, ${a})`;
            },
            hsl : () => {
                const h = this.int(360);
                const s = this.int(100);
                const l = this.int(100);
                return `hsl(${h}, ${s}%, ${l}%)`;
            },
            hsla : () => {
                const h = this.int(360);
                const s = this.int(100);
                const l = this.int(100);
                const a = Math.random().toFixed(2);
                return `hsla(${h}, ${s}%, ${l}%, ${a})`;
            },
            gray : () => {
                const g = this.int(0xff);
                return `rgb(${g}, ${g}, ${g})`;
            }
        };
    }
    static get sample(){
        const R = this;
        return {
            int   : (n,a,b) => Array.from({length:n}, () => R.int(a,b)),
            float : (n,a,b) => Array.from({length:n}, () => R.float(a,b)),
            char  : (n,upper=false) => Array.from({length:n}, () => R.char(upper)),
            bool  : n => Array.from({length:n}, () => R.bool()),
            bin   : n => Array.from({length:n}, () => R.bin()),
            oct   : n => Array.from({length:n}, () => R.oct()),
            dec   : n => Array.from({length:n}, () => R.dec()),
            hex   : n => Array.from({length:n}, () => R.hex()),
            get color(){
                return {
                    hex  : n => Array.from({length:n}, () => R.color.hex()),
                    hexa : n => Array.from({length:n}, () => R.color.hexa()),
                    rgb  : n => Array.from({length:n}, () => R.color.rgb()),
                    rgba : n => Array.from({length:n}, () => R.color.rgba()),
                    hsl  : n => Array.from({length:n}, () => R.color.hsl()),
                    hsla : n => Array.from({length:n}, () => R.color.hsla()),
                    gray : n => Array.from({length:n}, () => R.color.gray())
                };
            },
            choice : (n, choices, p) =>
                Array.from({length:n}, () => R.choice(choices, p))
        };
    }
    static shuffle(arr){
        return [...arr].sort(() => 0.5 - Math.random());
    }
    static choice(choices = [1,2,3], p = new Array(choices.length).fill(1 / choices.length)){
        const acc = accum_sum(...p).map(v => v * 100);
        const pool = new Array(100);
        pool.fill(choices[0], 0, acc[0]);
        for(let i=1;i<choices.length;i++)
            pool.fill(choices[i], acc[i-1], acc[i]);
        return pool[this.int(pool.length)];
    }
}


globalThis.Random = Random;

// // (upperCase) => upperCase ? : String.fromCharCode(rand_int(97,120))
// class Random {
//     static string(length,upperCase){
//         return length instanceof Array?
//             new Array(this.int(...length)).fill(0).map(() => this.char(upperCase)).join(""):
//             new Array(length).fill(0).map(() => this.char(upperCase)).join("");
//     }

// }
// export{Random}

const complex_constructor = (Complex, a, b) => {
    let _a, _b;
  if (a instanceof Complex) {
    _a = a.a;
    _b = a.b;
  } 
  else if (typeof a === "object") {
    if ("a" in a && "b" in a) {
      _a = a.a;
      _b = a.b;
    } 
    else if ("a" in a && "z" in a) {
      _a = a.a;
      _b = Math.sqrt(a.z ** 2 - a.a ** 2);
    } 
    else if ("a" in a && "phi" in a) {
      _a = a.a;
      _b = a.a * Math.tan(a.phi);
    } 
    else if ("b" in a && "z" in a) {
      _b = a.b;
      _a = Math.sqrt(a.z ** 2 - a.b ** 2);
    } 
    else if ("b" in a && "phi" in a) {
      _b = b;
      _a = a.b / Math.tan(a.phi);
    } 
    else if ("z" in a && "phi" in a) {
      _a = +a.z * Math.cos(a.phi).toFixed(15);
      _b = +a.z * Math.sin(a.phi).toFixed(15);
    }
  } 
  else if (typeof a === "number" && typeof b === "number") {
    _a = +a.toFixed(32);
    _b = +b.toFixed(32);
  }
  return [_a, _b]
};

class Complex{
    constructor(a = 0, b = 0) {
        [
            this.a,
            this.b
        ] = complex_constructor(Complex, a, b);
    }
    get __mapfun__(){
        return true
    }
    isComplex(){
        return true
    }
    toString(){
        let str = "";
        if (this.a !== 0)
          this.b >= 0
            ? (str = `${this.a}+${this.b}*i`)
            : (str = `${this.a}-${Math.abs(this.b)}*i`);
        else
          this.b >= 0
            ? (str = `${this.b}*i`)
            : (str = `-${Math.abs(this.b)}*i`);
        return str;
    }
    serialize() {
        return JSON.stringify({
            type : 'complex',
            data : this
        });
    }
    static deserialize(json){
        if(typeof json === 'string') json = JSON.parse(json);
        let {data, type} = json;
        return (type === 'complex' && ('a' in data) && ('b' in data))
            ? new Complex(data.a, data.b)
            : TypeError('Not a valid complex')
    } 
    toFixed(n){
        this.a = + this.a.toFixed(n);
        this.b = + this.b.toFixed(n);
        return this; 
    }  
    toPrecision(n){
        this.a = + this.a.toPrecision(n);
        this.b = + this.b.toPrecision(n);
        return this; 
    }  
    clone() {
        return new Complex(this.a, this.b);
    }
    get z(){
        return Math.hypot(this.a,this.b);    
    }
    get phi(){
        return Math.atan2(this.b , this.a);        
    }
    static zero() {
        return new Complex(0, 0);
    }
    static fromPolar(z, phi) {
        return new Complex(
            +(z * cos(phi)).toFixed(13), 
            +(z * sin(phi)).toFixed(13)
        );
    }
    
    static get random(){
        return {
            int : (a, b)=> new Complex(...Random.sample.int(2, a, b) ),
            float : (a, b)=> new Complex(...Random.sample.float(2, a, b) ),
        }
    }
    static twiddle(K, N){
        const phi = -2 * Math.PI * K / N;
        return new Complex(
            Math.cos(phi), 
            Math.sin(phi)
        );
    }
    get conj() {
        return new Complex(this.a, -this.b);
    }
    get inv() {
        return new Complex(
            this.a / Math.hypot(this.a, this.b),
            -this.b / Math.hypot(this.a, this.b)
        );
    }
    add(...c) {
        for (let i = 0; i < c.length; i++) {
            if (typeof c[i] === "number") c[i] = new Complex(c[i], 0);
            this.a += c[i].a;
            this.b += c[i].b;
        }
        return this;
    }
    sub(...c) {
        for (let i = 0; i < c.length; i++) {
            if (typeof c[i] === "number") c[i] = new Complex(c[i], 0);
            this.a -= c[i].a;
            this.b -= c[i].b;
        }
        return this;
    }
    mul(...c){
        let {z, phi} = this;
        for (let i = 0; i < c.length; i++) {
            if (typeof c[i] === "number") c[i] = new Complex(c[i], 0);
            z *= c[i].z;
            phi += c[i].phi;
        }
        this.a = z * Math.cos(phi);
        this.b = z * Math.sin(phi);  
        return this.toFixed(8);
    }
    div(...c){
        let {z, phi} = this;
        for (let i = 0; i < c.length; i++) {
            if (typeof c[i] === "number") c[i] = new Complex(c[i], 0);
            z /= c[i].z;
            phi -= c[i].phi;
        }
        this.a = z * Math.cos(phi);
        this.b = z * Math.sin(phi);  
        return this.toFixed(8);    }
    modulo(...c) {
        for (let i = 0; i < c.length; i++) {
            if (typeof c[i] === "number") c[i] = new Complex(c[i], 0);
            this.a %= c[i].a;
            this.b %= c[i].b;
        }
        return this;
    }
    pow(...c){
        let {z, phi} = this;
        for (let i = 0; i < c.length; i++) {
            if (typeof c[i] === "number") c[i] = new Complex(c[i], 0);
            z *= Math.exp(c[i].a * Math.log(z) - c[i].b * phi);
            phi += c[i].b * Math.log(z) + c[i].a * phi;
        }
        this.a = z * Math.cos(phi);
        this.b = z * Math.sin(phi);  
        return this;
    }
    get expo() {
        return [this.z, this.phi];
    }
    nthr(n=2){
        return complex({z: this.z ** (1/n), phi: this.phi / n});
    }
    get sqrt(){
        return this.nthr(2);
    }
    get cbrt(){
        return this.nthr(3);
    }
    get log(){
        return complex(this.z, this.phi);
    }
    get cos(){
        return complex(
            Math.cos(this.a) * Math.cosh(this.b),
            Math.sin(this.a) * Math.sinh(this.b)
        )
    }
    get sin(){
        return complex(
            Math.sin(this.a) * Math.cosh(this.b),
            Math.cos(this.a) * Math.sinh(this.b)
        )
    }
    get tan(){
        const D=cos(this.a*2)+cosh(this.b*2);
        return complex(
            Math.sin(2 * this.a) / D,
            Math.sinh(2 * this.b) / D
        );
    }
}
const complex=(a,b)=>{
    if((a instanceof Array||ArrayBuffer.isView(a)) && (b instanceof Array||ArrayBuffer.isView(a)))return a.map((n,i)=>complex(a[i],b[i]));
    if(a.isMatrix?.() && b.isMatrix?.()){
        if((a.shape[0]!==b.shape[0])||(a.shape[1]!==b.shape[1]))return Error(0)
        const arr=a.arr.map((n,i)=>complex(a.arr[i],b.arr[i]));
        return new a.constructor(a.rows,a.cols,...arr)
    }
    return new Complex(a,b)
};

const PRECESION = 8;

const abs = (...x) => mapfun(
    x =>{
        if(x.isComplex?.()) return x.z;
        return Math.abs(x)
    },
    ...x
);

const pow$1 = (...x) => {
    const n = x.pop();
    return mapfun(
        x => {
            if(x.isComplex?.()) {
                if(n.isComplex?.()) return new x.constructor({
                    z: Math.exp(n.a * Math.log(x.z) - n.b * x.phi),
                    phi: n.b * Math.log(x.z) + n.a * x.phi
                })
                return new x.constructor({z: x.z ** n, phi: x.phi * n});
            }
            if(n.isComplex?.()) return new x.constructor({
                    z: Math.exp(n.a * Math.log(x)),
                    phi: n.b * Math.log(x)
            })
            return Math.pow(x, n)
        },
        ...x
    )
};

const sqrt$2 = (...x) => mapfun(
    x=>{
        if(x.isComplex?.()) 
            return new x.constructor({z: x.z**(1/2), phi: x.phi/2});
        if(x < 0) return complex(0, Math.sqrt(-x)).toFixed(PRECESION)
        return + Math.sqrt(x).toFixed(PRECESION);
    },
    ...x
);

const cbrt = (...x) => mapfun(
    x=>{
        if(x.isComplex?.()) 
            return new x.constructor({z: x.z**(1/3), phi: x.phi/3}).toFixed(PRECESION)
        return + Math.cbrt(x).toFixed(PRECESION);
    },
    ...x
);

const nthr = (...x) => {
    const n = x.pop();
    if(typeof n !== 'number') throw Error('nthr expects a real number n');
    return mapfun(
        x => {
            if(x.isComplex?.()) return new x.constructor({z: x.z ** (1/n), phi: x.phi / n});
            if(x<0) return n %2 ===2 
                ? complex(0, (-x)**(1/n)).toFixed(PRECESION)
                : + (-1 * (-x)**(1/n)).toFixed(PRECESION)                
            return + (x**(1/n)).toFixed(PRECESION)
        },
        ...x
    )
};

const croot = (...x) =>{
    const c = x.pop();
    if(!c.isComplex?.()) throw Error('croot expect Complex number as root')
    return mapfun(
        x => {
            if(typeof x === 'number') x = new c.constructor(x, 0);
            const {a : c_a, b : c_b} = c;
            const {z, phi} = x;
            const D = Math.hypot(c_a, c_b);
            const A = Math.exp((Math.log(z)*c_a + phi*c_b)/D);
            const B = (phi*c_a - Math.log(z)*c_b)/D;
            return new c.constructor(
                A * Math.cos(B),
                A * Math.sin(B)
            ).toFixed(PRECESION)
        },
        ...x
    )
};

const exp$1 = (...x) => mapfun(
    x => {
        if(x.isComplex?.()) return new x.constructor(
            Math.exp(x.a) * Math.cos(x.b),
            Math.exp(x.a) * Math.sin(x.b)
        ).toFixed(PRECESION);
        return + Math.exp(x).toFixed(PRECESION)
    }
    ,...x
);

const ln = (...x) => mapfun(
    x => {
        if(x.isComplex?.()) return new x.constructor(
            Math.log(x.z),
            x.phi
        ).toFixed(PRECESION);
        return + Math.log(x).toFixed(PRECESION)
    }
    ,...x
);

const sign = (...x) => mapfun(
    x => {
        if(x.isComplex?.()){
            const {z, phi} = x;
            if(z===0) return new x.constructor(0, 0);
            return new x.constructor({z:1, phi})
        }
        return Math.sign(x)
    }
    ,...x
);

const floor = (...x) => mapfun(
    x => {
        if(x.isComplex?.()) return new x.constructor(
            Math.floor(x.a),
            Math.floor(x.b)
        )
        return Math.floor(x)
    },
    ...x
);
const ceil = (...x) => mapfun(
    x => {
        if(x.isComplex?.()) return new x.constructor(
            Math.ceil(x.a),
            Math.ceil(x.b)
        )
        return Math.ceil(x)
    },
    ...x
);
const round = (...x) => mapfun(
    x => {
        if(x.isComplex?.()) return new x.constructor(
            Math.round(x.a),
            Math.round(x.b)
        )
        return Math.round(x)
    },
    ...x
);

const trunc = (...x) => mapfun(
    x => {
        if(x.isComplex?.()) return new x.constructor(
            Math.trunc(x.a),
            Math.trunc(x.b)
        )
        return Math.trunc(x)
    },
    ...x
);

const fract = (...x) => mapfun(
    x => {
        if(x.isComplex?.()) return new x.constructor(
            x.a - Math.trunc(x.a),
            x.b - Math.trunc(x.b)
        )
        return x - Math.trunc(x)
    },
    ...x
);

const cos$3 = (...x) => mapfun(
    x => {
        if(x.isComplex?.()) return new x.constructor(
            Math.cos(x.a) * Math.cosh(x.b),
            -Math.sin(x.a) * Math.sinh(x.b)
        ).toFixed(PRECESION);
        return + Math.cos(x).toFixed(PRECESION)
    }
    ,...x
);

const sin$3 = (...x) => mapfun(
    x =>{
        if(x?.isComplex) return new x.constructor(
            Math.sin(x.a) * Math.cosh(x.b),
            Math.cos(x.a) * Math.sinh(x.b)
        ).toFixed(PRECESION);
        return + Math.sin(x).toFixed(PRECESION)
    }
    , ...x
);

const tan = (...x) => mapfun(
    x =>{
        if(x?.isComplex){
            const D = Math.cos(2*x.a) + Math.cosh(2*x.b);
            return new x.constructor(
                Math.sin(2*x.a) / D,
                Math.sinh(2*x.b) / D
            ).toFixed(PRECESION);
        } 
        return + Math.tan(x).toFixed(PRECESION)
    },
    ...x
);

const sec = (...x) => mapfun(
    x => {
        if(x.isComplex?.()) ;
        return + (1 / Math.cos(x)).toFixed(PRECESION)
    }
    ,...x
);

const acos$1 = (...x) => mapfun(
    x =>{
        if(x?.isComplex){
            const { a, b } = x;
            const Rp = Math.hypot(a + 1, b);
            const Rm = Math.hypot(a - 1, b);
            globalThis.Rp = Rp;
            globalThis.Rm = Rm;
            return new x.constructor(
                Math.acos((Rp - Rm) / 2),
                -Math.acosh((Rp + Rm) / 2),
            ).toFixed(PRECESION)
        } 
        return + Math.acos(x).toFixed(PRECESION) 
    },
    ...x
);

const asin = (...x) => mapfun(
    x => {
        if(x?.isComplex){
            const { a, b } = x;
            const Rp = Math.hypot(a + 1, b);
            const Rm = Math.hypot(a - 1, b);
            return new x.constructor(
                Math.asin((Rp - Rm) / 2), 
                Math.acosh((Rp + Rm) / 2)
            ).toFixed(PRECESION);
        }
        return + Math.asin(x).toFixed(PRECESION);
    },
    ...x
);

const atan = (...x) => mapfun(
    x => {
        if(x?.isComplex){
            const { a, b } = x;
            return new x.constructor(
                Math.atan((a*2/(1-a**2-b**2)))/2,
                Math.log((a**2 + (1+b)**2)/(a**2 + (1-b)**2))/4
            ).toFixed(PRECESION)
        }
        return + Math.atan(x).toFixed(PRECESION);
    },
    ...x
);

const acot = (...x) => mapfun(
    x => {
        if(x?.isComplex){
            const { a, b } = x;
            return new x.constructor(
                Math.atan(2*a/(a**2+(b-1)*(b+1)))/2,
                Math.log((a**2 + (b-1)**2)/(a**2 + (b+1)**2))/4   
            ).toFixed(PRECESION)
        }
        return + (Math.PI/2 - Math.atan(x)).toFixed(PRECESION);
    },
    ...x
);


const cosh$2 = (...x) => mapfun(
    x =>{
        if(x?.isComplex) return new x.constructor(
            Math.cosh(x.a) * Math.cos(x.b),
            Math.sinh(x.a) * Math.sin(x.b)
        ).toFixed(PRECESION); 
        return + Math.cosh(x).toFixed(PRECESION)
    },
    ...x
);
const sinh$1 = (...x) => mapfun(
    x =>{
        if(x?.isComplex) return new x.constructor(
            Math.sinh(x.a) * Math.cos(x.b),
            Math.cosh(x.a) * Math.sin(x.b)
        ).toFixed(PRECESION); 
        return + Math.sinh(x).toFixed(PRECESION)
    },
    ...x
);
const tanh = (...x) => mapfun(
    x =>{
        if(x?.isComplex){
            const D = Math.cosh(2*a) + Math.cos(2*b);
            return new x.constructor(
                Math.sinh(2*a) / D,
                Math.sin(2*b) / D
            ).toFixed(PRECESION)
        } 
        return + Math.tanh(x).toFixed(PRECESION)
    },
    ...x
);

const coth = (...x) => mapfun(
    x =>{
        if(x?.isComplex){
            const {a, b} = x;
            const D = (Math.sinh(a)**2)*(Math.cos(b)**2) + (Math.cosh(a)**2)*(Math.sin(b)**2); 
            return new x.constructor(
                Math.cosh(a) * Math.sinh(a) / D,
                - Math.sin(b) * Math.cos(b) / D
            ).toFixed(PRECESION)
        } 
        return + (1 / Math.tanh(x)).toFixed(PRECESION)
    },
    ...x
);

const acosh = (...x) => mapfun(
    x =>{
        if(x?.isComplex){
            return ln(x.clone().add(sqrt$2(x.clone().mul(x.clone()).sub(1))))
        } 
        return + Math.acosh(x).toFixed(PRECESION)
    },
    ...x
);

const asinh = (...x) => mapfun(
    x =>{
        if(x?.isComplex){
            return ln(x.clone().add(sqrt$2(x.clone().mul(x.clone()).add(1))))
        } 
        return + Math.asinh(x).toFixed(PRECESION)
    },
    ...x
);

const atanh = (...x) => mapfun(
    x =>{
        if(x?.isComplex); 
        return + Math.atanh(x).toFixed(PRECESION)
    },
    ...x
);

const sig = (...x) => mapfun(
    x =>{
        if(x?.isComplex); 
        return 1/(1 + Math.exp(-x)).toFixed(PRECESION)
    },
    ...x
);

const deg2rad = (...deg) => mapfun(x => x * Math.PI / 180, ...deg);
const rad2deg = (...rad) => mapfun(x => x / Math.PI * 180, ...rad);

const norm = (x, min, max) => apply_fun(
    x, 
    v => min !== max ? (v - min) / (max - min) : 0
);
const lerp = (x, min, max) => apply_fun(
    x, 
    v => (max - min) * v + min
);
const clamp = (x, min, max) => apply_fun(
    x, 
    v => Math.min(Math.max(v, min), max)
);
const map$1 = (x, a, b, c, d) => apply_fun(
    x, 
    v => lerp(norm(v, a, b), c, d)
);

const hypot = (...x) => {
  const c0 = x.find(a => a.isComplex?.());
  if (c0) {
    const W = x.map(n => n.isComplex?.() ? n : new c0.constructor(n, 0));
    return Math.hypot(...W.map(c => c.z));
  }
  return Math.hypot(...x);
};


const atan2 = (y, x, rad = true) => {
    if (y instanceof Array && !(x instanceof Array))
        return mapfun(n => atan2(n, x, rad), ...y);

    if (x instanceof Array && !(y instanceof Array))
        return mapfun(n => atan2(y, n, rad), ...x);

    if (y instanceof Array && x instanceof Array)
        return y.map((v, i) => atan2(v, x[i], rad));

    const phi = Math.atan2(y, x);
    return rad ? phi : phi * 180 / Math.PI;
};

const not = x => {
    if(x.isComplex?.()) return new x.constructor(not(x.a), not(x.b))
    if(x.isMatrix?.()) return new x.constructor(x.rows, x.cols, x.arr.flat(1).map(not))
    return + !x;
};
const handle_complex_and_matrix = (x, operation) => {
    if (x.every(n => n.isComplex?.())) {
        const Re = x.map(n => n.a);
        const Im = x.map(n => n.b);
        return new x[0].constructor(
            operation(...Re),
            operation(...Im)
        );
    }

    if (x.every(n => n.isMatrix?.())) {
        if (!x.every(mat => mat.rows === x[0].rows && mat.cols === x[0].cols)) {
            return TypeError('All matrices must have the same shape');
        }

        const { rows, cols } = x[0];
        const Y = Array.from({ length: rows }, (_, i) =>
            Array.from({ length: cols }, (_, j) =>
                operation(...x.map(mat => mat.arr[i][j]))
            )
        );
        return new x[0].constructor(Y);
    }

    return null;  // Return null if no Complex or Matrix found
};

const and = (...x) => {
    const result = handle_complex_and_matrix(x, and);
    if (result !== null) return result;
    return x.reduce((n, m) => (n &= m), 1);
};

const or = (...x) => {
    const result = handle_complex_and_matrix(x, or);
    if (result !== null) return result;
    return x.reduce((n, m) => (n |= m), 0);
};

const xor = (...x) => {
    const result = handle_complex_and_matrix(x, xor);
    if (result !== null) return result;
    return x.reduce((n, m) => (n ^= m), 0);
};

const nand = (...x) => not(and(...x));
const nor = (...x) => not(or(...x));
const xnor = (...x) => not(xor(...x));

const matrix_constructor = (Matrix, rows, cols, element) => {
  if (rows instanceof Matrix) {
    arr = rows.arr;
    rows = rows.rows;
    cols = rows.cols;
  } 
  else {
    let arr = [], i, j;
    if (rows instanceof Array) {
        arr = rows;
        rows = arr.length;
        cols = arr[0].length;
    } 
    else {
      for (i = 0; i < rows; i++) {
        arr.push([]);
        arr[i].push(new Array(cols));
        for (j = 0; j < cols; j++) {
          arr[i][j] = element[i * cols + j];
          if (element[i * cols + j] == undefined) arr[i][j] = 0;
        }
      }
    }
    return [
        rows,
        cols,
        arr
    ]
  }
};

const maintain_indexes = (Matrix, oldRows) =>{
    for (let i = 0; i < Matrix.arr.length; i++) {
        Object.defineProperty(Matrix, i, {
            value: Matrix.arr[i],
            writable: true,
            configurable: true,
            enumerable: false
        });
    }
    for (let i = Matrix.arr.length; i < oldRows; i++) {
        delete Matrix[i];
    }
};

function matrix_inverse(M) {
    if(M.row !== M.cols) throw Error('is not a square matrix"')
    if (M.det === 0) throw Error("determinant should not equal 0");
    const { arr } = M;
    if (arr.length !== arr[0].length) return;
    var i = 0, ii = 0, j = 0, dim = arr.length, e = 0;
    var I = [], C = [];
    for (i = 0; i < dim; i += 1) {
        I[I.length] = [];
        C[C.length] = [];
        for (j = 0; j < dim; j += 1) {
            if (i == j) I[i][j] = 1;
            else I[i][j] = 0;
            C[i][j] = arr[i][j];
        }
    }
    for (i = 0; i < dim; i += 1) {
        e = C[i][i];
        if (e == 0) {
            for (ii = i + 1; ii < dim; ii += 1) {
                if (C[ii][i] != 0) {
                    for (j = 0; j < dim; j++) {
                        e = C[i][j];
                        C[i][j] = C[ii][j];
                        C[ii][j] = e;
                        e = I[i][j];
                        I[i][j] = I[ii][j];
                        I[ii][j] = e;
                    }
                    break;
                }
            }
            e = C[i][i];
            if (e == 0) return;
        }
        for (j = 0; j < dim; j++) {
            C[i][j] = C[i][j] / e;
            I[i][j] = I[i][j] / e;
        }
        for (ii = 0; ii < dim; ii++) {
            if (ii == i) {
                continue;
            }
            e = C[ii][i];
            for (j = 0; j < dim; j++) {
                C[ii][j] -= e * C[i][j];
                I[ii][j] -= e * I[i][j];
            }
        }
    }
    return new M.constructor(I);
}

function matrix_det(M) {
    if (!M.isSquare) return new Error("is not square matrix");
    if (M.rows == 1) return M.arr[0][0];
    function determinat(M) {
        if (M.length == 2) {
            if (M.flat(1).some((n) => n?.isMatrix?.())) {
                console.warn("Tensors are not completely supported yet ...");
                return;
            }
            return sub(mul(M[0][0],M[1][1]),mul(M[0][1],M[1][0]))
        }
        var answer = 0;
        for (var i = 0; i < M.length; i++) {
            //console.log(M[0][i]);
            /*answer = answer.add(
                pow(-1, i)
                    .mul(M[0][i])
                    .mul(determinat(deleteRowAndColumn(M, i)))
            );*/
            //const to_be_added=add(mul(pow(-1, i),mul(M[0][i],determinat(deleteRowAndColumn(M, i)))));
            const to_be_added=add(mul(pow$1(-1, i),mul(M[0][i],determinat(deleteRowAndColumn(M, i)))));
            answer=add(answer,to_be_added);
        }
        return answer;
    }
    return determinat(M.arr);
}
function deleteRowAndColumn(M, index) {
    var temp = [];
    for (let i = 0; i < M.length; i++) temp.push(M[i].slice(0));
    temp.splice(0, 1);
    for (let i = 0; i < temp.length; i++) temp[i].splice(index, 1);
    return temp;
}

function hstack(M1, M2){
    M1 = M1.clone();
    M2 = M2.clone();
    if (M1.rows !== M2.rows) return;
    let newArr = M1.arr;
    for (let i = 0; i < M1.rows; i++) 
        for (let j = M1.cols; j < M1.cols + M2.cols; j++) 
            newArr[i][j] = M2.arr[i][j - M1.cols];
    M1.cols += M2.cols;
    return new M1.constructor(M1.rows, M1.cols, newArr.flat(1));
}

function vstack(M1, M2){
    M1 = M1.clone();
    M2 = M2.clone();
    if (M1.cols !== M2.cols) return;
    let newArr = M1.arr;
    for (let i = M1.rows; i < M1.rows + M2.rows; i++) {
        newArr[i] = [];
        for (let j = 0; j < M1.cols; j++) newArr[i][j] = M2.arr[i - M1.rows][j];
    }
    M1.rows += M2.rows;
    return new M1.constructor(M1.rows, M1.cols, newArr.flat(1));
}

class Matrix{
    constructor(rows, cols, element = [] ) {
        [
            this.rows, 
            this.cols, 
            this.arr
        ] = matrix_constructor(Matrix, rows, cols, element);
        maintain_indexes(this);
    }
    isMatrix(){
        return true
    }
    clone() {
        return new Matrix(this.rows, this.cols, this.arr.flat(1));
    }
    toComplex(){
        this.arr = mapfun(
            x => x?.isComplex?.() ? x : new Complex(x, 0),
            ...this.arr
        );
        maintain_indexes(this);
        return this;
    }
    [Symbol.iterator]() {
      return this.arr[Symbol.iterator]();
    }
    get size() {
        return this.rows * this.cols;
    } 
    get shape() {
        return [this.rows, this.cols];
    }
    // toString(){
    //     return arr2str(this.arr,false);
    // }
    at(i = 0, j = undefined) {
        if(i < 0) i += this.rows;
        if(i < 0 || i >= this.rows) throw new Error('Row index out of bounds');
        if(j === undefined) return this.arr[i];
        if(j < 0) j += this.cols;
        if(j < 0 || j >= this.cols) throw new Error('Column index out of bounds');
        return this.arr[i][j];
    }
    slice(r0=0, c0=0, r1 = this.rows-1, c1 = this.cols-1) {
        if(r1 < 0) r1 = this.rows + r1;
        if(c1 < 0 ) c1 = this.cols + c1;
        let newRow = r1 - r0,
            newCol = c1 - c0;
        let newArr = new Array(newCol);
        for (let i = 0; i < newRow; i++) {
            newArr[i] = [];
            for (let j = 0; j < newCol; j++) 
                newArr[i][j] = this.arr[i + r0][j + c0];
        }
        this.arr = newArr;
        maintain_indexes(this.rows);
        this.rows = newRow;
        this.cols = newCol;
        return this;
    }
    reshape(newRows, newCols) {
        if(!(newRows * newCols === this.rows * this.cols)) throw Error('size not matched');
        const oldRows = this.rows;
        Object.assign(this, new Matrix(newRows, newCols, this.arr.flat(1)));
        maintain_indexes(oldRows);
        return this;
    }
    get T() {
        let transpose = [];
        for (let i = 0; i < this.arr[0].length; i++) {
            transpose[i] = [];
            for (let j = 0; j < this.arr.length; j++) 
                transpose[i][j] = this.arr[j][i];
        }
        return new Matrix(this.cols, this.rows, transpose.flat(1));
    }
    get det() {
        return matrix_det(this)
    }
    get inv() {
        return matrix_inverse(this)
    }
    // normalize names
    static eye(size) {
        let result = new Matrix(size, size);
        for (let i = 0; i < size; i++) 
            for (let j = 0; j < size; j++) i === j ? (result.arr[i][j] = 1) : (result.arr[i][j] = 0);
        return result;
    }
    static zeros(rows, cols) {
        let result = new Matrix(rows, cols);
        for (let i = 0; i < rows; i++) 
            for (var j = 0; j < cols; j++) result.arr[i][j] = 0;
        return result;
    }
    static ones(rows, cols) {
        let result = new Matrix(rows, cols);
        for (let i = 0; i < rows; i++) 
            for (let j = 0; j < cols; j++) result.arr[i][j] = 1;
        return result;
    }
    static nums(rows, cols, number) {
        let result = new Matrix(rows, cols);
        for (let i = 0; i < rows; i++) 
            for (let j = 0; j < cols; j++) result.arr[i][j] = number;
        return result;
    }
    static get random(){
        return {
            int : (r, c, a, b)=> new Matrix(
                r,
                c,
                Random.sample.int(r*c, a, b)
            ),
            float : (r, c, a,)=> new Matrix(
                r,
                c,
                Random.sample.float(r*c, a, b)
            ),
        }
    }
    get range(){
        return {
            map : (xmin, xmax, ymin, ymax) => {
                this.arr = map$1(this.arr, xmin, xmax, ymin, ymax);
                return this;
            },
            norm : (min, max) => {
                this.arr = norm(this.arr, min, max);
                return this;
            },
            lerp : (min, max) => {
                this.arr = lerp(this.arr, min, max);
                return this;
            },
            clamp : (min, max) => {
                this.arr = clamp(this.arr, min, max);
                return this;
            },

        }
    }
    hstack(...matrices) {
        const M=[this, ...matrices].reduce((a,b)=>hstack(a, b));
        Object.assign(this, M);
        maintain_indexes(this);
        return this;
    }
    vstack(...matrices){
        const M=[this, ...matrices].reduce((a,b)=>vstack(a, b));
        Object.assign(this, M);
        maintain_indexes(this);
        return this;
    }
    hqueue(...matrices){
        const M=[this, ...matrices].reverse().reduce((a,b)=>hstack(a, b));
        Object.assign(this, M);
        maintain_indexes(this);
        return this;
    }
    vqueue(...matrices){
        const M=[this,...matrices].reverse().reduce((a, b)=>vstack(a, b));
        Object.assign(this, M);
        maintain_indexes(this);
        return this;
    }
    forEach(fn){
        this.arr.flat(1).forEach(fn);
        return this;
    }
    forEachRow(fn){
        this.arr.forEach(fn);
        return this;
    }
    forEachCol(fn){
        this.clone().T.forEachRow(fn);
        return this
    }
    map(fn){
        const arr = this.arr.flat(1).map(fn);
        return new Matrix(
            this.rows, 
            this.cols,
            arr
        )
    }
    mapRows(fn = ()=>{}){
        this.arr = this.arr.map(fn);
        return this;
    }
    mapCols(fn){
        return this.clone().T.mapRows(fn).T;
    }
    sort(fn = ()=>{}){
        const arr = this.arr.flat(1).sort(fn);
        return new Matrix(
            this.rows, 
            this.cols,
            arr
        )  
    }
    shuffle(){
        return this.sort(() => 0.5-Math.random())
    }
    sortRows(fn = ()=>{}){
        this.arr = this.arr.map(row => row.sort(fn));
        return this;
    }
    shuffleRows(){
        return this.sortRows(() => 0.5-Math.random())
    }
    sortCols(fn){
        return this.clone().T.sortRows(fn).T;
    }
    shuffleCols(){
        return this.sortCols(() => 0.5-Math.random())
    }
    reduce(fn, initialValue){
        const value = initialValue 
            ? this.arr.flat(1).reduce(fn, initialValue) 
            : this.arr.flat(1).reduce(fn);
        return new Matrix([[value]])
    }
    reduceRows(fn, initialValue){
        const values = initialValue 
            ? this.arr.map(row => row.reduce(fn, initialValue)) 
            : this.arr.map(row => row.reduce(fn)); 
        return new Matrix(1, this.cols, values)
    }
    reduceCols(fn, initialValue){
        return this.T.reduceRows(fn, initialValue).T
    }
    filterRows(fn){
        const mask = this.arr.map(n => n.some(m => fn(m)));
        const arr = [];
        let i;
        for(i = 0; i < mask.length; i++)
            if(mask[i]) arr.push(this.arr[i]);
        return new Matrix(arr)
    }
    filterCols(fn){
        const arr = this.T.filterRows(fn);
        return new Matrix(arr).T
    }
    every(fn){
        return this.arr.flat(1).every(fn)
    }
    everyRow(fn){
        return this.arr.map(n => n.every(fn))
    }
    everyCol(fn){
        return this.T.arr.map(n => n.every(fn))
    }
    some(fn){
        return this.arr.flat(1).some(fn)
    }
    someRow(fn){
        return this.arr.map(n => n.some(fn))
    }
    someCol(fn){
        return this.T.arr.map(n => n.some(fn))
    }
    // Checkers
    get isSquare() {
        return this.rows === this.cols;
    }
    get isSym() {
        if (!this.isSquare) return false;
        for (let i = 0; i < this.rows; i++) {
            for (let j = i + 1; j < this.cols; j++) {
                if (this.arr[i][j] !== this.arr[j][i]) return false;
            }
        }
        return true;
    }
    get isAntiSym() {
        if (!this.isSquare) return false;
        const n = this.rows;
        for (let i = 0; i < n; i++) {
            if (this.arr[i][i] !== 0) return false;
            for (let j = i + 1; j < n; j++) {
                if (this.arr[i][j] !== -this.arr[j][i]) return false;
            }
        }
        return true;
    }
    get isDiag() {
        if (!this.isSquare) return false;
        const n = this.rows;
        for (let i = 0; i < n; i++) {
            for (let j = i + 1; j < n; j++) {
                if (this.arr[i][j] !== 0 || this.arr[j][i] !== 0) return false;
            }
        }
        return true;
    }
    get isOrtho() {
        if (!this.isSquare) return false;
        return this.isDiag && (this.det == 1 || this.det == -1);
    }
    get isIdemp() {
        if (!this.isSquare) return false;
        const n = this.rows;
        const A = this.arr;
        // Compute A * A
        const MM = [];
        for (let i = 0; i < n; i++) {
            MM[i] = [];
            for (let j = 0; j < n; j++) {
                let sum = 0;
                for (let k = 0; k < n; k++) {
                    sum += A[i][k] * A[k][j];
                }
                MM[i][j] = sum;
            }
        }
        // Check if A * A == A
        for (let i = 0; i < n; i++) {
            for (let j = 0; j < n; j++) {
                if (MM[i][j] !== A[i][j]) return false;
            }
        }
        return true;
    }

    get isUpperTri() {
        if (!this.isSquare) return false;
        const n = this.rows;
        for (let i = 1; i < n; i++) {
            for (let j = 0; j < i; j++) {
                if (this.arr[i][j] !== 0) return false;
            }
        }
        return true;
    }
    get isLowerTri() {
        if (!this.isSquare) return false;
        const n = this.rows;
        for (let i = 0; i < n - 1; i++) {
            for (let j = i + 1; j < n; j++) {
                if (this.arr[i][j] !== 0) return false;
            }
        }
        return true;
    }
    toPrecision(p) {
        for (let i = 0; i < this.cols; i++) 
            for (let j = 0; j < this.rows; j++) 
                this.arr[i][j] = +this.arr[i][j].toPrecision(p);
        return this;
    }
    toFixed(p) {
        for (let i = 0; i < this.cols; i++) 
            for (let j = 0; j < this.rows; j++) 
                this.arr[i][j] = +this.arr[i][j].toFixed(p);
        return this;
    }
    // max2min() {
    //     let newArr = this.arr.flat(1).max2min;
    //     return new Matrix(this.rows, this.cols, newArr);
    // }
    // min2max() {
    //     let newArr = this.arr.flat(1).min2max;
    //     return new Matrix(this.rows, this.cols, newArr);
    // }
    // count(n) {
    //     return this.arr.flat(1).count(n);
    // }
    splice(r0,c0,deleteCount,...items){
        
    }
    getRows(ri, rf = ri + 1) {
        return this.slice(ri, 0, rf, this.cols);
    }
    getCols(ci, cf = ci + 1) {
        return this.slice(0, ci, this.rows, cf);
    }
    #arithmetic(fn, ...matr){
        for (let k = 0; k < matr.length; k++) {
            if (typeof matr[k] == "number" || matr[k]?.isComplex?.()) matr[k] = Matrix.nums(this.rows, this.cols, matr[k]);
            for (let i = 0; i < this.rows; i++) 
                for (var j = 0; j < this.cols; j++) 
                    this.arr[i][j] = fn(this.arr[i][j], matr[k].arr[i][j]);
        }
        return new Matrix(this.rows, this.cols, this.arr.flat(1));  
    }
    add(...matr) {
        return this.#arithmetic(add, ...matr)
    }
    sub(...matr) {
        return this.#arithmetic(sub, ...matr)
    }
    mul(...matr) {
        return this.#arithmetic(mul, ...matr)
    }
    div(...matr) {
        return this.#arithmetic(div, ...matr)
    }
    modulo(...matr) {
        return this.#arithmetic(modulo, ...matr)
    }
    dot(matrix) {
        var res = [];
        for (var i = 0; i < this.arr.length; i++) {
            res[i] = [];
            for (var j = 0; j < matrix.arr[0].length; j++) {
                res[i][j] = 0;
                for (var k = 0; k < this.arr[0].length; k++) {
                    res[i][j] = add(
                        res[i][j],
                        mul(this.arr[i][k],matrix.arr[k][j])
                        );
                }
            }
        }
        return new Matrix(this.arr.length, matrix.arr[0].length, res.flat(1));
    }
    pow(n) {
        let a = this.clone(),
            p = this.clone();
        for (let i = 0; i < n - 1; i++) p = p.dot(a);
        return p;
    }
    sum(){
        let S = 0;
        for (let i = 0; i < this.rows; i++) 
            for (let j = 0; j < this.cols; j++) 
                S = add(S, this.arr[i][j]);
        return S;
    }
    prod(){
        let S = 1;
        for (let i = 0; i < this.rows; i++) 
            for (let j = 0; j < this.cols; j++) 
                S = mul(S, this.arr[i][j]);
        return S;
    }
    hasComplex(){
        return this.arr.flat(Infinity).some((n) => n instanceof Complex);
    }
    get min() {
        if (this.hasComplex()) console.error("Complex numbers are not comparable");
        let minRow = [];
        for (let i = 0; i < this.rows; i++) 
            minRow.push(Math.min(...this.arr[i]));
        return Math.min(...minRow);
    }
    get max() {
        if (this.hasComplex()) console.error("Complex numbers are not comparable");
        let maxRow = [];
        for (let i = 0; i < this.rows; i++) 
            maxRow.push(Math.max(...this.arr[i]));
        return Math.max(...maxRow);
    }
    get minRows() {
        if (this.hasComplex()) console.error("Complex numbers are not comparable");
        let minRow = [];
        for (let i = 0; i < this.rows; i++) 
            minRow.push(Math.min(...this.arr[i]));
        return minRow;
    }
    get maxRows() {
        if (this.hasComplex()) console.error("Complex numbers are not comparable");
        let maxRow = [];
        for (let i = 0; i < this.rows; i++) 
            maxRow.push(Math.max(...this.arr[i]));
        return maxRow;
    }
    get minCols() {
        if (this.hasComplex()) console.error("Complex numbers are not comparable");
        return this.T.minRows;
    }
    get maxCols() {
        if (this.hasComplex()) console.error("Complex numbers are not comparable");
        return this.T.maxRows;
    }
    static fromVector(v) {
        return new Matrix(v.length, 1, v);
    }
    serialize() {
        const arr = mapfun(x => x.serialize?.() || x, ...this.arr);
        return JSON.stringify({
            type : 'matrix',
            data : {
                rows : this.rows,
                cols : this.cols,
                arr,
            }
        });
    }
    static deserialize(json) {
        if (typeof json == "string") json = JSON.parse(json);
        const {type, data} = json;
        if(type !== 'matrix') return TypeError('Not a valid Matrix')
        let {arr} = data;
        arr = mapfun(x => {
            if(typeof x === 'string') {
                const x_obj = JSON.parse(x);
                const {type} = x_obj;
                if(type === 'complex') return Complex.deserialize(x_obj)
            }
            return x
        }, ...arr);
        return new Matrix(arr)
    }
    flip(){
        return this.flipeH().flipeV()
    }
    flipeH(){
        this.arr = this.arr.map(row => [...row].reverse());
        maintain_indexes(this);
        return this;
    }
    flipeV(){
        this.arr = this.arr.reverse();
        maintain_indexes(this);
        return this;
    }
}


const matrix=(r, c, element)=>new Matrix(r, c, element);
const matrix2=(...element)=>new Matrix(2, 2, element);
const matrix3=(...element)=>new Matrix(3, 3, element);
const matrix4=(...element)=>new Matrix(4, 4, element);

const zeros=(n)=>new Array(n).fill(0);
const ones=(n)=>new Array(n).fill(1);
const nums=(num,n)=>new Array(n).fill(num);

const arange=(a, b, step , include = false)=>{
    let tab = [];
    if(a<b){
        for (let i = a; include?i<=b:i<b; i += step) tab.push((i * 10) / 10);
    }
    else {
        for(let i = a; include?i>=b:i>b; i -= step) tab.push((i * 10) / 10);
    }
    return tab;
};
const linspace=(a,b,n=abs(b-a)+1,endpoint=true)=>{
    if(Math.floor(n)!==n)return;
    if([a,b].every(n=>typeof n==="number")){
        const [max,min]=[a,b].sort((a,b)=>b-a);
        var Y = [];
        let step ;
        endpoint ? step = (max - min) / (n - 1) : step = (max - min) / n;
        for (var i = 0; i < n; i++) {
            a<b?Y.push(min+step*i):Y.push(max-step*i);
        }
        return Y
    }

    if([a,b].some(n=>n.isComplex?.())){
        const z1 = new n.constructor(a);
        const z2 = new n.constructor(b);
        n=n||Math.abs(z1.a-z2.a)+1;
        const X=linspace(z1.a,z2.a,n,endpoint);
        const Y=linspace(z1.b,z2.b,n,endpoint);
        let Z=new Array(n).fill(null);
        Z=Z.map((n,i)=> new n.constructor(X[i],Y[i]));
        return Z;
    }
};
const logspace=(a,b,n=b-a+1,base=E,endpoint=true)=>{
    return linspace(a,b,n,endpoint).map(n=>pow$1(base,n))
};
const geomspace=(a,b,n=abs(b-a)+1,endpoint=true)=>{
    if(Math.floor(n)!==n)return;
    if([a,b].every(n=>typeof n==="number")){
        const [max,min]=[a,b].sort((a,b)=>b-a);
        let base;
        endpoint ? base = nthr(max/min,n-1) : base = nthr(max/min,n) ;
        const Y = [min];
        for (let i = 1; i < n; i++) {
            Y.push(Y[i-1]*base);
        }
        return a<b?Y:Y.reverse()
    }

    if([a,b].some(n=>n.isComplex?.())){
        const z1 = new n.constructor(a);
        const z2 = new n.constructor(b);
        n=n||Math.abs(z1.a-z2.a)+1;
        let base;
        endpoint ? base = nthr(z2.div(z1),n-1) : base = nthr(z2.div(z1),n) ;
        const Y = [z1];
        for (let i = 1; i < n; i++) {
            Y.push(mul(Y[i-1],base));
        } 
        return Y;
    }
};

/** @module Math */
/**
 * Checks if a value is within the specified range.
 * @function
 * @param {number} x - The value to check.
 * @param {number} a - The start of the range.
 * @param {number} b - The end of the range.
 * @returns {boolean} Returns true if the value is within the range [a, b], otherwise false.
 */
const inRange = (x, a, b) => {
    const [min, max] = [Math.min(a, b), Math.max(a, b)];
    return x >= min && x <= max;
};

/**
 * Checks if two numbers are approximately equal within a given error margin.
 * @param {number} a - The first number.
 * @param {number} b - The second number.
 * @param {number} [Err=0.0001] - The maximum acceptable difference between the two numbers.
 * @returns {boolean} Returns true if the two numbers are approximately equal within the specified error margin, otherwise false.
 */
const isApproximatlyEqual = (a, b, Err = 0.0001) => {
    return Math.abs(a - b) <= Err;
};

/** @module Math */

/**
 * Computes the cartesian product of two arrays.
 * @param {Array} a - The first array.
 * @param {Array} b - The second array.
 * @returns {Array} Returns an array representing the cartesian product of the input arrays.
 */
const cartesianProduct = (a, b) => a.reduce((p, x) => [...p, ...b.map((y) => [x, y])], []);

/**
 * Computes the greatest common divisor (GCD) of two numbers.
 * @param {number} n1 - The first number.
 * @param {number} n2 - The second number.
 * @returns {number} Returns the greatest common divisor of the two input numbers.
 */
const pgcd = (n1, n2) => {
    let i,
        pgcd = 1;
    if (n1 == floor(n1) && n2 == floor(n2)) {
        for (i = 2; i <= n1 && i <= n2; ++i) {
            if (n1 % i == 0 && n2 % i == 0) pgcd = i;
        }
        return pgcd;
    } else console.log("error");
};

/**
 * Computes the least common multiple (LCM) of two numbers.
 * @param {number} n1 - The first number.
 * @param {number} n2 - The second number.
 * @returns {number} Returns the least common multiple of the two input numbers.
 */
const ppcm = (n1, n2) => {
    let ppcm;
    if (n1 == floor(n1) && n2 == floor(n2)) {
        ppcm = n1 > n2 ? n1 : n2;
        while (true) {
            if (ppcm % n1 == 0 && ppcm % n2 == 0) break;
            ++ppcm;
        }
        return ppcm;
    } else console.log("error");
};

// import { mapfun } from "../mapfun/index.js";
// import { 
//     add,
//     sub,
//     mul,
//     div,
//     modulo
//  } from "./arithmetic.js";
const Utils={
    // add,
    // sub,
    // mul,
    // div,
    // modulo,

    zeros,
    ones,
    nums,
    // norm,
    // lerp,
    // map,
    // clamp,
    arange,
    linspace,
    logspace,
    geomspace,

    // sum,
    // prod,
    // accum,

    cartesianProduct,
    ppcm,
    pgcd,

    // deg2rad,
    // rad2deg,

    inRange,
    isApproximatlyEqual
};

class UINode {
    constructor(node){
        this.cache = {
            node
        };
    }
    isZikoUINode(){
        return true
    }
    get node(){
        return this.cache.node;
    } 
}

// globalThis.node = (node) => new UINode(node);

function parseQueryParams$1(queryString) {
    const params = {};
    queryString.replace(/[A-Z0-9]+?=([\w|:|\/\.]*)/gi, (match) => {
        const [key, value] = match.split('=');
        params[key] = value;
    });
    return params;
}

function defineParamsGetter$1(target ){
    Object.defineProperties(target, {
        'QueryParams': {
            get: function() {
                return parseQueryParams$1(globalThis.location.search.substring(1));
            },
            configurable: false,
            enumerable: true 
        },
        'HashParams': {
            get: function() {
                const hash = globalThis.location.hash.substring(1);
                return hash.split("#");
            },
            configurable: false,
            enumerable: true 
        }
    });
}

class UIStore extends Array {
    constructor(...args) {
        super(...args); 
    }
    clear(){
        this.length = 0;
        return this;
    }
    getItemById(id) {
        return this.find(n => n.element.id === id);
    }
    getItemsByTagName(tag) {
        return this.filter(n => n.element.tagName.toLowerCase() === tag.toLowerCase());
    }
    getElementsByClassName(className) {
        return this.filter(n => n.element.classList?.contains(className));
    }
    querySelector(selector) {
        const el = globalThis?.document?.querySelector(selector);
        if (!el) return null;
        return this.find(ui => ui.element === el) || null;
    }
    querySelectorAll(selector) {
        const els = globalThis?.document?.querySelectorAll(selector);
        return Array.from(els)
            .map(el => this.find(ui => ui.element === el))
            .filter(Boolean);
    }
}

// create the singleton
const __UI__ = new UIStore();

const __Config__ = {
    default:{
        target:null,
        render:true,
        math:{
            mode:"deg"
        }
    },
    setDefault:function(pairs){
        const keys=Object.keys(pairs);
        const values=Object.values(pairs);
        for(let i=0; i<keys.length; i++) this.default[keys[i]]=values[i];
    },
    init:()=>{
        // document.documentElement.setAttribute("data-engine","zikojs")
    },
    renderingMode :"spa",
    isSSC : false,
};

const __HYDRATION__ = {
    store : new Map(),
    index : 0,
    register: function(component){
        this.store.set(this.index++ , component);
    },
    reset(){
        this.index = 0;
        this.store.clear();
    }
    
};

const __CACHE__ = {
    ui_index : 0,
    get_ui_index:function(){
        return this.ui_index ++
    },
    register_ui: function(UIElement){
        
    }
};

class UseIPC {
    #channel;
    #eventData;
    #handlers;
    #uuid;
    #subscribers;
    #currentRooms;
    constructor(name = "") {
        this.#channel = new BroadcastChannel(name);
        this.#eventData = new Map();
        this.#handlers = new Map(); // Map<event, Array<{fn, rooms}>>
        this.#uuid = "ziko-channel:" + (Math.random()*10e16);  // To Be Replaced by UUID
        this.#subscribers = new Set([this.#uuid]);
        this.#currentRooms = new Set(); 
        this.#channel.addEventListener("message", (e) => {
            const { last_sent_event, userId, eventData, rooms } = e.data;
            if (userId === this.#uuid) return; // ignore own messages
            // broadcast if no rooms, else check intersection
            if (rooms && rooms.length && !rooms.some(r => this.#currentRooms.has(r))) return;
            this.#subscribers.add(userId);
            this.#eventData = new Map(eventData);
            const handlersList = this.#handlers.get(last_sent_event);
            if (!handlersList) return;
            handlersList.forEach(({ fn, rooms: handlerRooms }) => {
                // trigger if listener has no room filter, or intersects subscriber rooms
                if (!handlerRooms || handlerRooms.length === 0 ||
                    !rooms || rooms.some(r => handlerRooms.includes(r))) {
                    fn(this.#eventData.get(last_sent_event));
                }
            });
        });
    }

    emit(event, data, rooms) {
        this.#eventData.set(event, data);
        if(typeof rooms === 'string') rooms = [rooms];
        this.#channel.postMessage({
            eventData: Array.from(this.#eventData.entries()),
            last_sent_event: event,
            userId: this.#uuid,
            rooms: rooms && rooms.length ? rooms : undefined
        });
        return this;
    }
    on(event, handler = console.log, rooms) {
        if (!this.#handlers.has(event)) this.#handlers.set(event, []);
        if(typeof rooms === 'string') rooms = [rooms];
        this.#handlers.get(event).push({ fn: handler, rooms });
        return this;
    }
    off(event, handler) {
        if (!this.#handlers.has(event)) return this;
        this.#handlers.set(
            event,
            this.#handlers.get(event).filter(h => h.fn !== handler)
        );
        return this;
    }
    once(event, handler, rooms) {
        const wrapper = (data) => {
            handler(data);
            this.off(event, wrapper);
        };
        this.on(event, wrapper, rooms);
        return this;
    }
    join(...rooms) {
        rooms.forEach(r => this.#currentRooms.add(r));
        return this;
    }
    leave(...rooms) {
        if (!rooms.length) this.#currentRooms.clear();
        else rooms.forEach(r => this.#currentRooms.delete(r));
        return this;
    }
    close() {
        this.#channel.close();
        return this;
    }
}

const useIPC = (name) => new UseIPC(name);

class UseStorage {
    constructor(storage, globalKey, initialValue, use_channel = true) {
        this.cache = {
            storage,
            globalKey,
            channel: use_channel ? useIPC(`Ziko:useStorage-${globalKey}`) : null,
            oldItemKeys: new Set()
        };

        this.#init(initialValue, use_channel);
    }

    get items() {
        const raw = this.cache.storage.getItem(this.cache.globalKey);
        if (!raw) return {};
        try {
            return JSON.parse(raw);
        } catch {
            return {};
        }
    }

    #maintain() {
        const items = this.items;
        this.cache.oldItemKeys.forEach(k => delete this[k]);
        for (const key in items) {
            this[key] = items[key];
            this.cache.oldItemKeys.add(key);
        }
    }
    #init(initialValue, use_channel) {
        if (use_channel && this.cache.channel) this.cache.channel.on("Ziko-Storage-Updated", () => this.#maintain());
        if (!initialValue) {
            this.#maintain();
            return;
        }
        if (this.cache.storage.getItem(this.cache.globalKey)) {
            const existing = this.items;
            Object.keys(existing).forEach(k => this.cache.oldItemKeys.add(k));
            this.#maintain();
        } 
        else this.set(initialValue);
    }

    set(data) {
        this.cache.storage.setItem(this.cache.globalKey, JSON.stringify(data));
        if (this.cache.channel) this.cache.channel.emit("Ziko-Storage-Updated", data);
        this.#maintain();
        return this;
    }

    add(data) {
        this.set({
            ...this.items,
            ...data
        });
        return this;
    }
    remove(...keys) {
        const items = { ...this.items };
        keys.forEach(key => {
            delete items[key];
            delete this[key];
            this.cache.oldItemKeys.delete(key);
        });
        this.set(items);
        return this;
    }
    get(key) {
        return this.items[key];
    }
    clear() {
        this.cache.storage.removeItem(this.cache.globalKey);
        this.cache.oldItemKeys.forEach(k => delete this[k]);
        this.cache.oldItemKeys.clear();
        this.#maintain();
        return this;
    }
    onStorageUpdated(callback) {
        if (this.cache.channel) {
            this.cache.channel.on("Ziko-Storage-Updated", callback);
        }
        return this;
    }
}

// factory functions
const useLocaleStorage = (key, initialValue, use_channel = true) =>
    new UseStorage(localStorage, key, initialValue, use_channel);

const useSessionStorage = (key, initialValue, use_channel = true) =>
    new UseStorage(sessionStorage, key, initialValue, use_channel);

var __State__ = {
    store : new Map(),
    index : 0,
    session_storage : null,
    register: function(state){
        if(!undefined.SSR && undefined.DEV){
            if(!this.session) this.session_storage = useSessionStorage('ziko-state', {});
            const savedValue = this.session_storage.get(this.index);
            if(!savedValue) this.session_storage.add({[this.index] : state.value});
            else state.value = savedValue;
        }
        this.store.set(this.index++, state);
    },
    update: function(index, value){
       if(!undefined.SSR && undefined.DEV){
            this.session_storage.add({[index] : value});
        } 
    },

};

function __init__global__(){
    if ( !globalThis?.__Ziko__ ){
        globalThis.__Ziko__ = {
                    __UI__,
                    __HYDRATION__,
                    __State__,
                    __Config__,
                    __CACHE__,
                };
        defineParamsGetter$1(__Ziko__);
    }
}

__init__global__();
class UIElementCore extends UINode{
  constructor(){
    super();
  }
  init(element, name, type, render){
    this.target = globalThis.__Ziko__.__Config__.default.target||globalThis?.document?.body;
    if(typeof element === "string") {
      switch(type){
        case "html" : {
          element = globalThis?.document?.createElement(element);
          // console.log('1')
        } break;
        case "svg" : {
          element = globalThis?.document?.createElementNS("http://www.w3.org/2000/svg", element); 
          // console.log('2')
        } break;
        default : throw Error("Not supported")
      }
    }
    else this.target = element?.parentElement;
    Object.assign(this.cache, {
      name,
      isInteractive : false,
      parent:null,
      isBody:false,
      isRoot:false,
      isHidden: false,
      isFrozzen:false,
      legacyParent : null,
      attributes: {},
      filters: {},
      temp:{}
    });
    this.events = {
      ptr:null,
      mouse:null,
      wheel:null,
      key:null,
      drag:null,
      drop:null,
      click:null,
      clipboard:null,
      focus:null,
      swipe:null,
      custom:null,
    };
    this.observer={
      resize:null,
      intersection:null
    };
    if(element) Object.assign(this.cache,{element});
    // useDefaultStyle && this.style({ 
    //   position: "relative",
    //   boxSizing:"border-box",
    //   margin:0,
    //   padding:0,
    //   width : "auto",
    //   height : "auto"
    //  });
    this.items = new UIStore();
    globalThis.__Ziko__.__UI__[this.cache.name]?globalThis.__Ziko__.__UI__[this.cache.name]?.push(this):globalThis.__Ziko__.__UI__[this.cache.name]=[this];
    element && render && this?.render?.();
    globalThis.__Ziko__.__UI__.push(this);
  }
  get element(){
    return this.cache.element;
  }
    [Symbol.iterator]() {
    return this.items[Symbol.iterator]();
  }
  maintain() {
    for (let i = 0; i < this.items.length; i++) {
      Object.defineProperty(this, i, {
        value: this.items[i],
        writable: true,
        configurable: true,
        enumerable: false 
        });
    }
  }
  isInteractive(){
    return this.cache.isInteractive;
  }
  isUIElement(){
    return true;
  }
}

function register_to_class(target, ...mixins){
    mixins.forEach(n => _register_to_class_(target, n));
}
function _register_to_class_(target, mixin) {
  const descriptors = Object.getOwnPropertyDescriptors(mixin);
  for (const key of Reflect.ownKeys(descriptors)) {
    const desc = descriptors[key];
    if ('get' in desc || 'set' in desc || typeof desc.value !== 'function') {
      Object.defineProperty(Object.getPrototypeOf(target), key, desc);
    } else if (typeof desc.value === 'function') {
      if (!Object.getPrototypeOf(target).hasOwnProperty(key)) {
        Object.defineProperty(Object.getPrototypeOf(target), key, desc);
      }
    }
  }
}

// export function mount(target = this.target) {
//   if(this.isBody) return ;
//   if(target?.isUIElement)target=target.element;
//   this.target=target;
//   this.target?.appendChild(this.element);
//   return this;
// }
// export function unmount(){
//   if(this.cache.parent)this.cache.parent.remove(this);
//   else if(this.target?.children?.length && [...this.target?.children].includes(this.element)) this.target.removeChild(this.element);
//   return this;
// }

// export function mountAfter(target = this.target, t = 1) {
//   setTimeout(() => this.mount(), t);
//   return this;
// }
// export function unmountAfter(t = 1) {
//   setTimeout(() => this.unmount(), t);
//   return this;
// }

function mount(target = this.target, delay = 0) {
    if (delay > 0) {
        setTimeout(() => this.mount(target, 0), delay);
        return this;
    }

    if (this.isBody) return this;

    if (target?.isUIElement) target = target.element;
    this.target = target;

    this.target?.appendChild(this.element);
    return this;
}

function unmount(delay = 0) {
    if (delay > 0) {
        setTimeout(() => this.unmount(0), delay);
        return this;
    }

    if (this.cache.parent) {
        this.cache.parent.remove(this);
    } else if (
        this.target?.children?.length &&
        [...this.target.children].includes(this.element)
    ) {
        this.target.removeChild(this.element);
    }

    return this;
}

var LifecycleMethods = /*#__PURE__*/Object.freeze({
   __proto__: null,
   mount: mount,
   unmount: unmount
});

if(!globalThis.__Ziko__) __init__global__();

function useState(initialValue) {
    
    const {store, index} = __Ziko__.__State__;
    __Ziko__.__State__.register({
            value : initialValue,
            subscribers : new Set(),
            paused : false
    });

    let current = store.get(index);

    function getValue() {
        return {
            value: current.value,
            isStateGetter: () => true,
            _subscribe: (fn) => current.subscribers.add(fn),
        };
    }

    function setValue(newValue) {
        if (current.paused) return;
        if (typeof newValue === "function") {
            newValue = newValue(current.value);
        }
        if (newValue !== current.value) {
            current.value = newValue;
            current.subscribers.forEach(fn => fn(current.value));
            __Ziko__.__State__.update(index, newValue);
        }
    }

    const controller = {
        pause: () => { current.paused = true; },
        resume: () => { current.paused = false; },
        clear: () => { current.subscribers.clear(); },
        force: (newValue) => {
            if (typeof newValue === "function") newValue = newValue(current.value);
            current.value = newValue;
            current.subscribers.forEach(fn => fn(current.value));
        },
        getSubscribers: () => new Set(current.subscribers),
    };

    return [getValue, setValue, controller];
}


const isStateGetter = (arg) => {
    return typeof arg === 'function' && arg?.()?.isStateGetter?.();
};

const camel2hyphencase$1 = (text = '') => text.replace(/[A-Z]/g, match => '-' + match.toLowerCase());

const is_camelcase$1 = (text = '') =>{
    if (text.length === 0) return false; 
    const camelCasePattern = /^[a-z][a-zA-Z0-9]*$/;
    return camelCasePattern.test(text);
};

class ZikoUIText extends UINode {
    constructor(...value) {
      super("span", "text", false, ...value);
      this.element = globalThis?.document?.createTextNode(...value);
    }
    isText(){
      return true
    }
}
const text = (...str) => new ZikoUIText(...str);

async function __addItem__(adder, pusher, ...ele) {
  if (this.cache.isFrozzen) {
    console.warn("You can't append new item to frozzen element");
    return this;
  }
  for (let i = 0; i < ele.length; i++) {
    if (["number", "string"].includes(typeof ele[i])) ele[i] = text(ele[i]);
        // Fix Items Latter
    if (ele[i] instanceof Function) {
     const getter = ele[i]();
      if (getter.isStateGetter) {
        ele[i] = text(getter.value);
        getter._subscribe(
            (newValue) => (ele[i].element.textContent = newValue),
            ele[i] 
        );
        // this.element.appendChild(textNode);
      }
    }
    if (typeof globalThis?.Node === "function" && ele[i] instanceof globalThis?.Node) ele[i] = new this.constructor(ele[i]);
    if (ele[i]?.isZikoUINode) {
        ele[i].cache.parent = this;
        this.element?.[adder](ele[i].element);
        ele[i].target = this.element;
        this.items[pusher](ele[i]);
    } 
    else if(ele[i] instanceof Promise){
      const UIEle = await ele[i];
      UIEle.cache.parent = this;
      this.element?.[adder](UIEle.element);
      UIEle.target = this.element;
      this.items[pusher](UIEle);
    }
    else if (ele[i] instanceof Object) {
      if (ele[i]?.style) this.style(ele[i]?.style);
      if (ele[i]?.attr) {
        Object.entries(ele[i].attr).forEach((n) =>
          this.setAttr("" + n[0], n[1]),
        );
      }
    }
  }
  this.maintain();
  return this;
}
function _set_attrs_(name, value){
    if(globalThis.SVGAElement && this.element instanceof globalThis.SVGAElement) name = is_camelcase$1(name) ? camel2hyphencase$1(name) : name;
    if(this?.attr[name] && this?.attr[name]===value) return;
    if(isStateGetter(value)){
        const getter = value();
        getter._subscribe(
            (newValue) => this.element?.setAttribute(name, newValue),
            this 
        );
    }
    else this.element?.setAttribute(name, value);
    Object.assign(this.cache.attributes, {[name]:value});   
}

// import { 
//   is_camelcase,
//   camel2hyphencase
//  } from '../../data/string/index.js'

function setAttr(name, value) {
  if(name instanceof Object){
    const [names,values]=[Object.keys(name),Object.values(name)];
    for(let i=0;i<names.length;i++){
      if(values[i] instanceof Array)value[i] = values[i].join(" ");
      _set_attrs_.call(this, names[i], values[i]);
    }
  }
  else {
    if(value instanceof Array) value = value.join(" ");
    _set_attrs_.call(this, name, value);
  }
  return this;
}
function removeAttr(...names) {
  for(let i=0;i<names.length;i++)this.element?.removeAttribute(names[i]);
  return this;
}
function getAttr(name){
  name = is_camelcase(name) ? camel2hyphencase(name) : name;
  return this.element.attributes[name].value;
}
function setContentEditable(bool = true) {
  this.setAttr("contenteditable", bool);
  return this;
}

var AttrsMethods = /*#__PURE__*/Object.freeze({
   __proto__: null,
   getAttr: getAttr,
   removeAttr: removeAttr,
   setAttr: setAttr,
   setContentEditable: setContentEditable
});

function append(...ele) {
  __addItem__.call(this, "append", "push", ...ele);
  return this;
}
function prepend(...ele) {
  this.__addItem__.call(this, "prepend", "unshift", ...ele);
  return this;
}
function insertAt(index, ...ele) {
  if (index >= this.element.children.length) this.append(...ele);
  else
    for (let i = 0; i < ele.length; i++) {
      if (["number", "string"].includes(typeof ele[i])) ele[i] = text(ele[i]);
      this.element?.insertBefore(ele[i].element, this.items[index].element);
      this.items.splice(index, 0, ele[i]);
    }
  return this;
}
function remove(...ele) {
  const remove = (ele) => {
    if (typeof ele === "number") ele = this.items[ele];
    if (ele?.isUIElement) this.element?.removeChild(ele.element);
    this.items = this.items.filter((n) => n !== ele);
  };
  for (let i = 0; i < ele.length; i++) remove(ele[i]);
  for (let i = 0; i < this.items.length; i++)
    Object.assign(this, { [[i]]: this.items[i] });
  // Remove from item
  return this;
}
function clear(){
  this?.items?.forEach(n=>n.unmount());
  this.element.innerHTML = "";
  return this;
}
function replaceElementWith(new_element){
    this.cache.element.replaceWith(new_element);
    this.cache.element = new_element;

    // To do : Dispose Events and States 
    return this
}
function after(ui){
  if(ui?.isUIElement) ui=ui.element;
  this.element?.after(ui);
  return this;
}
function before(ui){
  if(ui?.isUIElement) ui=ui.element;
  this.element?.before(ui);
  return this;
}

var DomMethods = /*#__PURE__*/Object.freeze({
   __proto__: null,
   after: after,
   append: append,
   before: before,
   clear: clear,
   insertAt: insertAt,
   prepend: prepend,
   remove: remove,
   replaceElementWith: replaceElementWith
});

const EventsMap = {
    'Click' : [
        'Click',
        'DblClick',
        'ClickAway'
    ],
    'Ptr' : [
        'PtrMove',
        'PtrDown',
        'PtrUp',
        'PtrLeave',
        'PtrEnter',
        'PtrOut',
        'PtrCancel'
    ],
    'Mouse' : [
        'MouseMove',
        'MouseDown',
        'MouseUp',
        'MouseEnter',
        'MouseLeave',
        'MouseOut'
    ],
    // 'Touch' : [],
    'Key' : [
        'KeyDown',
        'KeyPress',
        'KeyUp'
    ],
    'Clipboard':[
        'Copy',
        'Cut',
        'Paste'
    ],
    'Focus':[
        'focus',
        'blur'
    ],
    'Drag':[
        "Drag",
        "DragStart",
        "DragEnd",
        "Drop"
    ],
    'Wheel': [
        'Wheel'
    ],
    // 'Media':[

    // ],
    // 'Hash':[
    //     "HashChange"
    // ]

    'View':[
        'EnterView',
        'ExitView',
        'ResizeView'
    ],
    'Swipe':[
        'SwipeLeft',
        'SwipeUp',
        'SwipeRight',
        'SwipeDown'
    ]
};

function event_controller(e, event_name, details_setter, customizer) {
    this.cache.currentEvent = event_name;
    this.cache.event = e;

    details_setter?.call(this);
    if (customizer?.hasOwnProperty('prototype')) customizer?.call(this);
    else customizer?.call(null, this);

    if (this.cache.preventDefault[event_name]) e.preventDefault();
    if (this.cache.stopPropagation[event_name]) e.stopPropagation();
    if (this.cache.stopImmediatePropagation[event_name]) e.stopImmediatePropagation();

    // Call the single callback if it exists
    this.cache.callbacks[event_name]?.(this);
}

function toggle_event_listener(method, ...events) {
    const keys = events.length === 0
        ? Object.keys(this.cache.paused) 
        : events;
    keys.forEach(key => {
        if (!this.cache.paused.hasOwnProperty(key)) return;
        this.targetElement?.[method](
            key,
            this.cache.__controllers__[key],
            this.cache.options[key]
        );
        this.cache.paused[key] = method === 'removeEventListener';
    });
    return this;
}
const getEvent=(event = "")=>{
    if(event.startsWith("Ptr"))return `pointer${event.split("Ptr")[1].toLowerCase()}`;
    return event.toLowerCase()
};

class ZikoEvent {
    constructor(signature, target = null, Events = [], details_setter, customizer){
        this.target = target;
        this.cache = {
            signature,
            currentEvent : null,
            event: null,
            options : {},
            preventDefault : {},
            stopPropagation : {},
            stopImmediatePropagation : {},
            paused : {},
            callbacks : {},
            __controllers__:{}
        };
        if (Events) this._register_events(Events, details_setter, customizer);
    }
    _register_events(Events, details_setter, customizer, REGISTER_METHODES = true) {
        const events = Events?.map(n => getEvent(n));
        events?.forEach((event, i) => {
            this.cache.preventDefault[event] = false;
            this.cache.options[event] = {};
            this.cache.paused[event] = false;
            this.cache.__controllers__[event] = (e) =>
                event_controller.call(this, e, event, details_setter, customizer);
            if (REGISTER_METHODES) {
                this[`on${Events[i]}`] = (callback) =>
                    this.__onEvent(event, this.cache.options[event], {}, callback);
            }
        });
        return this;
    }
    __onEvent(event, options, dispose, callback) {
        if (!callback) return this;
        this.cache.callbacks[event] = callback;
        this.__handle(event, this.cache.__controllers__[event], options, dispose);
        return this;
    }
    get targetElement(){
        return this.target?.element;
    }
    get isParent(){
        return this.target?.element === this.event?.srcElement;
    }
    get item(){
        return this.target.find(n => n.element == this.event?.srcElement)?.[0];
    }
    get currentEvent(){
        return this.cache.currentEvent;
    }
    get event(){
        return this.cache.event;
    }
    get detail(){
        return this.cache.event.detail
    }
    setTarget(UI){
        this.target = UI;
        return this;
    }
    __handle(event, handler, options){
        this.targetElement?.addEventListener(event, handler, options);
        return this;
    }
    #override(method, ...events) {
        const keys = events.length === 0 ? Object.keys(this.cache[method]) : events;
        keys.forEach(e => {
            if (this.cache[method].hasOwnProperty(e)) this.cache[method][e] = true;
        });
        return this;
    }
    preventDefault(...events) {
        return this.#override('preventDefault', ...events);
    }
    stopPropagation(...events) {
        return this.#override('stopPropagation', ...events);
    }
    stopImmediatePropagation(...events) {
        return this.#override('stopImmediatePropagation', ...events);
    }
    setEventOptions(event, options){
        const evt = getEvent(event);
        this.pause();
        Object.assign(this.cache.options[evt], options);
        this.resume();
        return this;
    }
    pause(...events) {
        return toggle_event_listener.call(this, 'removeEventListener', ...events)
    }
    resume(...events) {
        return toggle_event_listener.call(this, 'addEventListener', ...events);
    }
    dispose(){
        this.pause();
        this.target.events[this.cache.signature] = null;
        return this;
    }
}

function key_details_setter(){
    switch(this.currentEvent){
        case "keydown" : {
            this.kd = this.event.key;
        } break;
        case "keypress" : {
            this.kp = this.event.key;
        } break;
        case "keyup" : {
            this.ku = this.event.key;
        } break;

    }
}

function ptr_details_setter() {
    const rect = this.targetElement.getBoundingClientRect();
    const e = this.event;
    let x = (e.clientX - rect.left) | 0;
    let y = (e.clientY - rect.top) | 0;

    if(this.cache.useNormalisedCoordinates){
        const w = this.targetElement.clientWidth;
        const h = this.targetElement.clientHeight;
        x = +((x / w) * 2 - 1).toFixed(8);
        y = +((y / h) * -2 + 1).toFixed(8);
    }
    switch (this.currentEvent) {

        case "pointerdown":
            this.dx = x;
            this.dy = y;
            this.isDown = true;
            break;

        case "pointermove":
            this.mx = x;
            this.my = y;
            this.isMoving = true;
            break;

        case "pointerup":
            this.ux = x;
            this.uy = y;
            this.isDown = false;
            this.isMoving = false;
            break;
    }
}

function mouse_details_setter() {
    const rect = this.targetElement.getBoundingClientRect();
    const e = this.event;
    let x = (e.clientX - rect.left) | 0;
    let y = (e.clientY - rect.top) | 0;

    if(this.cache.useNormalisedCoordinates){
        const w = this.targetElement.clientWidth;
        const h = this.targetElement.clientHeight;
        x = +((x / w) * 2 - 1).toFixed(8);
        y = +((y / h) * -2 + 1).toFixed(8);
    }

    switch (this.currentEvent) {

        case "mousedown":
            this.dx = x;
            this.dy = y;
            this.isDown = true;
            break;

        case "mousemove":
            this.mx = x;
            this.my = y;
            this.isMoving = true;
            break;

        case "mouserup":
            this.ux = x;
            this.uy = y;
            this.isDown = false;
            this.isMoving = false;
            break;
    }
}

function touch_details_setter() {
    const e = this.event;
    const touch = e.touches?.[0] || e.changedTouches?.[0];

    if (!touch) return; // should never happen but safe

    const rect = this.targetElement.getBoundingClientRect();
    let x = touch.clientX - rect.left;
    let y = touch.clientY - rect.top;

    if(this.cache.useNormalisedCoordinates){
        const w = this.targetElement.clientWidth;
        const h = this.targetElement.clientHeight;
        x = +((x / w) * 2 - 1).toFixed(8);
        y = +((y / h) * -2 + 1).toFixed(8);
    }

    switch (this.currentEvent) {
        case "touchstart":
            this.dx = x;
            this.dy = y;
            this.isDown = true;
            break;

        case "touchmove":
            this.mx = x;
            this.my = y;
            this.isMoving = true;
            break;

        case "touchend":
            this.ux = x;
            this.uy = y;
            this.isDown = false;
            break;
    }
}

class CoordinatesBasedEvent extends ZikoEvent{
    constructor(signature, target = null, Events = [], details_setter, customizer){
        super(signature, target, Events, details_setter, customizer);
        Object.assign(this.cache,{
            useNormalisedCoordinates : false
        });
        this.isDown = false;
        this.isMoving = false;
        this.dx = 0;
        this.dy = 0;
        this.mx = 0;
        this.my = 0;
        this.ux = 0;
        this.uy = 0;
    }
    get isDragging(){
        return this.isDown && this.isMoving
    }
    useNormalisedCoordinates(enable = true){
        this.cache.useNormalisedCoordinates = enable;
        return this;
    }
}

class ClickAwayEvent extends Event {
  constructor(originalEvent, targetElement) {
    super("clickaway", { bubbles: true, cancelable: true });
    this.originalEvent = originalEvent;
    this.targetElement = targetElement;
  }
}

function register_click_away_event(element) {
  console.log(element);
  function handler(e) {
    if (!element.contains(e.target)) {
      const clickAwayEvent = new ClickAwayEvent(e, element);
      element.dispatchEvent(clickAwayEvent);
    }
  }

  globalThis?.document?.addEventListener("click", handler);

  return () => globalThis?.document?.removeEventListener("click", handler);
  
}

// // Example usage
// const box = document.querySelector("#my-box");

// const stop = listenClickAway(box);

// box.addEventListener("clickaway", (e) => {
//   console.log("Clicked outside box!", e);
// });

// // later, you can stop listening:
// // stop();

const debounce=(fn,delay=1000)=>{
    let id;
    return (...args) => id ? clearTimeout(id) : setTimeout(()=>fn(...args),delay);
};
const throttle=(fn,delay)=>{
    let lastTime=0;
    return (...args) => {
        const now = new Date().getTime();
        if(now-lastTime < delay) return;
        lastTime = now;
        fn(...args); 
    }
};

class ViewEvent extends CustomEvent {
    constructor(type, detail, { bubbles = true, cancelable = true } = {}) {
        super(type, { detail, bubbles, cancelable });
    }
}

function register_view_event(
    element,
    {
        intersection = true,
        resize = true,
        threshold = 0,
        throttleResize = 100,
        throttleEnterExit = 0
    } = {}
) {
    let intersectionObserver, resizeObserver;
    const resizeCallback = entries => {
        for (let entry of entries) {
            const { width, height } = entry.contentRect;

            element.dispatchEvent(
                new ViewEvent("resizeview", {
                    width,
                    height,
                    entry
                })
            );
        }
    };

    const throttledResize = throttleResize > 0
        ? throttle(resizeCallback, throttleResize)
        : resizeCallback;

    const intersectionCallback = entries => {
        for (let entry of entries) {
            const type = entry.isIntersecting ? "enterview" : "exitview";
            element.dispatchEvent(new ViewEvent(type, entry));
        }
    };

    const throttledIntersections = throttleEnterExit > 0
        ? throttle(intersectionCallback, throttleEnterExit)
        : intersectionCallback;

    if (intersection) {
        intersectionObserver = new IntersectionObserver(throttledIntersections, { threshold });
        intersectionObserver.observe(element);
    }

    if (resize) {
        resizeObserver = new ResizeObserver(throttledResize);
        resizeObserver.observe(element);
    }

    // ---- UNREGISTER ----
    return () => {
        if (intersectionObserver) {
            intersectionObserver.unobserve(element);
            intersectionObserver.disconnect();
        }
        if (resizeObserver) {
            resizeObserver.unobserve(element);
            resizeObserver.disconnect();
        }
    };
}

class SwipeEvent extends CustomEvent {
  constructor(type, detail) {
    super(type, {
      detail,
      bubbles: true,
      cancelable: true
    });
  }
}

function register_swipe_event(
  element,
  threshold = 50,
  restraint = 100,
  allowedTime = 500
) {
  let startX = 0,
      startY = 0,
      startTime = 0,
      isPointerDown = false;

  function onPointerDown(e) {
    startX = e.clientX;
    startY = e.clientY;
    startTime = performance.now();
    isPointerDown = true;
  }

  function onPointerUp(e) {
    if (!isPointerDown) return;
    isPointerDown = false;

    const distX = e.clientX - startX;
    const distY = e.clientY - startY;
    const elapsed = performance.now() - startTime;

    let direction = null;
    let eventName = null;

    if (elapsed <= allowedTime) {
      if (Math.abs(distX) >= threshold && Math.abs(distY) <= restraint) {
        direction = distX < 0 ? "left" : "right";
        eventName = "swipe" + direction;
      } 
      else if (Math.abs(distY) >= threshold && Math.abs(distX) <= restraint) {
        direction = distY < 0 ? "up" : "down";
        eventName = "swipe" + direction;
      }
    }

    // Emit event
    if (eventName) {
      element.dispatchEvent(
        new SwipeEvent(eventName, {
          direction,
          distX,
          distY,
          originalEvent: e
        })
      );
    }
  }

  element.addEventListener("pointerdown", onPointerDown, { passive: true });
  element.addEventListener("pointerup", onPointerUp, { passive: true });

  return () => {
    element.removeEventListener("pointerdown", onPointerDown);
    element.removeEventListener("pointerup", onPointerUp);
  };
}

const bind_click_event = (target, customizer) => {
    register_click_away_event(target.element);
    return new ZikoEvent(
        'click',
        target,
        EventsMap.Click,
        null,
        customizer
    );
};
const bind_clipboard_event = (target, customizer) => new ZikoEvent(
    'clipboard',
    target,
    EventsMap.Clipboard,
    null,
    customizer
);
const bind_drag_event = (target, customizer) => new ZikoEvent(
    'drag',
    target,
    EventsMap.Drag,
    null,
    customizer
);
const bind_focus_event = (target, customizer) => new ZikoEvent(
    'focus',
    target,
    EventsMap.Focus,
    null,
    customizer
);
const bind_key_event = (target, customizer) => new ZikoEvent(
    'key',
    target,
    EventsMap.Key, 
    key_details_setter, 
    customizer
);
const bind_mouse_event = (target, customizer) => new CoordinatesBasedEvent(
    'mouse',
    target,
    EventsMap.Mouse,
    mouse_details_setter,
    customizer
);
const bind_pointer_event = (target, customizer) => new CoordinatesBasedEvent(
    'ptr',
    target,
    EventsMap.Ptr, 
    ptr_details_setter, 
    customizer
);
const bind_touch_event = (target, customizer) => new CoordinatesBasedEvent(
    'touch',
    target,
    EventsMap.Touch,
    touch_details_setter,
    customizer
);
const bind_wheel_event = (target, customizer) => new ZikoEvent(
    'wheel',
    target,
    EventsMap.Wheel,
    null,
    customizer
);

const bind_view_event = (target, customizer) => {
    register_view_event(target.element);
    return new ZikoEvent(
        'view',
        target, 
        EventsMap.View,
        null, 
        customizer
    )
};

const bind_swipe_event = (target, customizer) => {
    register_swipe_event(target.element);
    return new ZikoEvent(
        'swipe',
        target, 
        EventsMap.Swipe,
        null, 
        customizer
    )
};

class ZikoCustomEvent extends ZikoEvent{
    constructor(target, events, customizer){
        super('custom', target, events, details_setter, customizer);
    }
    _register_events(events){
        super._register_events(events, null, null, false);
        return this;
    }
    emit(event_name, detail = {}){
        const event = new CustomEvent(event_name, {
            detail,
            bubbles: true,
            cancelable: true
        });
        this.targetElement.dispatchEvent(event);
        return this;
    }
    on(event_name, ...callbacks){
        if(!this.cache.options.hasOwnProperty(event_name)) this._register_events([event_name]);
        this.__onEvent(event_name, this.cache.options[event_name], {}, ...callbacks);
        return this;
    }
}
function details_setter(){

}
const bind_custom_event = (target, events, customizer) => new ZikoCustomEvent(target, events, customizer);

const binderMap = {
  ptr: bind_pointer_event,
  mouse : bind_mouse_event,
  key: bind_key_event,
  click : bind_click_event,
  drag : bind_drag_event,
  clipboard : bind_clipboard_event,
  focus : bind_focus_event,
  wheel : bind_wheel_event,
  view : bind_view_event,
  swipe : bind_swipe_event
};

const EventsMethodes = {
  on(event_name,...callbacks){
    if(!this.events.custom)this.events.custom = bind_custom_event(this);
    this.events.custom.on(event_name,...callbacks);
    return this;
  },
  emit(event_name,detail={}){
    if(!this.events.custom)this.events.custom = bind_custom_event(this);
    this.events.custom.emit(event_name,detail);
    return this;
  }
};

Object.entries(EventsMap).forEach(([name, eventList]) => {
  const lname = name.toLowerCase();
  eventList.forEach(event => {
    const methodName = `on${event}`;
    EventsMethodes[methodName] = function (callbacks) {
      if (!this.events[lname]) this.events[lname] = binderMap[lname](this);
      this.events[lname][methodName](callbacks);
      return this;
    };
  });
});

function at(index) {
  return this.items.at(index);
}
function forEach(callback) {
  this.items.forEach(callback);
  return this;
}
function map(callback) {
  return this.items.map(callback);
}
function find(condition) {
  return this.items.filter(condition);
}

var IndexingMethods = /*#__PURE__*/Object.freeze({
   __proto__: null,
   at: at,
   find: find,
   forEach: forEach,
   map: map
});

function style(styles){
    if(!this.element?.style) return this;
    for(let key in styles){
        const value = styles[key];
        if(isStateGetter(value)){
            const getter = value();
            Object.assign(this.element.style, {[key] : getter.value});
            getter._subscribe(
                (newValue) => {
                    console.log({newValue});
                    Object.assign(this.element.style, {[key] : newValue});
                },
                // this 
            );
        }
        else Object.assign(this.element.style, {[key] : value});
    }
    return this;
}
function size(width, height){
    return this.style({width, height})
}
function hide(){

}
function show(){

}
function animate(keyframe, {duration=1000, iterations=1, easing="ease"}={}){
    this.element?.animate(keyframe,{duration, iterations, easing});
    return this;
}

var StyleMethods = /*#__PURE__*/Object.freeze({
   __proto__: null,
   animate: animate,
   hide: hide,
   show: show,
   size: size,
   style: style
});

// import { 
//   // useCustomEvent,
//   // useSwipeEvent,
//   // watchIntersection, 
//   // watchSize,
//   // watchAttr,
//   // watchChildren
// } from "../../--reactivity-deprecated/events/custom-event.js"
let UIElement$1 = class UIElement extends UIElementCore{
  constructor({element, name ='', type='html', render = __Ziko__.__Config__.default.render}={}){
    super();
    // console.log({type})
    // console.log(this)
    register_to_class(
      this, 
      LifecycleMethods,
      AttrsMethods, 
      DomMethods, 
      StyleMethods,
      IndexingMethods, 
      EventsMethodes
    );

    // console.log(EventsMethodes)
    if(element)this.init(element, name, type, render);
  }
  get element(){
    return this.cache.element;
  }
  isInteractive(){
    return this.cache.isInteractive;
  }
  useClient(directive){
    if(!this.cache.isInteractive){
      this.element.setAttribute('data-hydration-index', globalThis.__Ziko__.__HYDRATION__.index);
      globalThis.__Ziko__.__HYDRATION__.register(() => this);
      this.cache.isInteractive = true;
    }
    if(directive)this.element.setAttribute('data-hydration-directive', directive);
    return this;
  }
  // isUIElement(){
  //   return true;
  // }
  get st(){
    return this.cache.style;
  }
  get attr(){
    return this.cache.attributes;
  }
  get evt(){
    return this.events;
  }
  get html(){
    return this.element.innerHTML;
  }
  get text(){
    return this.element.textContent;
  }
  get isBody(){
    return this.element === globalThis?.document.body;
  }
  get parent(){
    return this.cache.parent;
  }
  get width(){
    return this.element.getBoundingClientRect().width;
  }
  get height(){
    return this.element.getBoundingClientRect().height;
  }
  get top(){
    return this.element.getBoundingClientRect().top;
  }
  get right(){
    return this.element.getBoundingClientRect().right;
  }
  get bottom(){
    return this.element.getBoundingClientRect().bottom;
  }
  get left(){
    return this.element.getBoundingClientRect().left;
  }
  // clone(render=false) {
  //   // UI.__proto__=this.__proto__;
  //   // if(this.items.length){
  //   //   const items = [...this.items].map(n=>n.clone());
  //   //   UI.append(...items);
  //   // }
  //   // else UI.element=this.element.cloneNode(true);
  //   // return UI.mount(render);
  // }
  // [Symbol.iterator]() {
  //   return this.items[Symbol.iterator]();
  // }
  // maintain() {
  //   for (let i = 0; i < this.items.length; i++) {
  //     Object.defineProperty(this, i, {
  //       value: this.items[i],
  //       writable: true,
  //       configurable: true,
  //       enumerable: false 
  //       });
  //   }
  // }
  // freeze(freeze){
  //   this.cache.isFrozzen=freeze;
  //   return this;
  // }
  // setTarget(tg) {
  //   if(this.isBody) return ;
  //   if (tg?.isUIElement) tg = tg.element;
  //   this.unmount();
  //   this.target = tg;
  //   this.mount();
  //   return this;
  // }
  // describe(label){
  //   if(label)this.setAttr("aria-label",label)
  // }
  // get children() {
  //   return [...this.element.children];
  // }
  // get cloneElement() {
  //   return this.element.cloneNode(true);
  // }
  // setClasses(...value) {
  //   this.setAttr("class", value.join(" "));
  //   return this;
  // }
  // get classes(){
  //   const classes=this.element.getAttribute("class");
  //   return classes===null?[]:classes.split(" ");
  // }
  // addClass() {
  //   /*this.setAttr("class", value);
  //       return this;*/
  // }
  // setId(id) {
  //   this.setAttr("id", id);
  //   return this;
  // }
  // get id() {
  //   return this.element.getAttribute("id");
  // }
  // To Fix
  // onKeysDown({keys=[],callback}={}){
  //   if(!this.events.key)this.events.key = useKeyEvent(this);
  //   this.events.key.handleSuccessifKeys({keys,callback});
  //   return this;
  // }
  // watchAttr(callback){
  //   if(!this.observer.attr)this.observer.attr = watchAttr(this,callback);
  //   return this;
  // }
  // watchChildren(callback){
  //   if(!this.observer.children)this.observer.children = watchChildren(this,callback);
  //   return this;
  // }
  // watchSize(callback)Remplaced By on onViewResize
  // watchIntersection(callback,config) Remplaced By onViewEnter and onViewExit

};

class UIView extends UIElement$1{
    constructor(...items){
        super({element : 'div', name : 'view'});
        this.append(...items);
    }
}

const View = (...items) => new UIView(...items);

const HTMLTags = [
  'a',
  'abb',
  'address',
  'area',
  'article',
  'aside',
  'audio',
  'b',
  'base',
  'bdi',
  'bdo',
  'blockquote',
  'br',
  'button',
  'canvas',
  'caption',
  'cite',
  'code',
  'col',
  'colgroup',
  'data',
  'datalist',
  'dd',
  'del',
  'details',
  'dfn',
  'dialog',
  'div',
  'dl',
  'dt',
  'em',
  'embed',
  'fieldset',
  'figcaption',
  'figure',
  'footer',
  'form',
  'h1',
  'h2',
  'h3',
  'h4',
  'h5',
  'h6',
  'header',
  'hgroup',
  'hr',
  'i',
  'iframe',
  'img',
  'ipnut',
  'ins',
  'kbd',
  'label',
  'legend',
  'li',
  'main',
  'map',
  'mark',
  'menu',
  'meter',
  'nav',
  'object',
  'ol',
  'optgroup',
  'option',
  'output',
  'p',
  'param',
  'picture',
  'pre',
  'progress',
  'q',
  'rp',
  'rt',
  'ruby',
  's',
  'samp',
  'search',
  'section',
  'select',
  'small',
  'source',
  'span',
  'strong',
  'sub',
  'summary',
  'sup',
  'table',
  'tbody',
  'td',
  'template',
  'textarea',
  'tfoot',
  'th',
  'thead',
  'time',
  'title',
  'tr',
  'track',
  'u',
  'ul',
  'var',
  'video',
  'wbr'
];

const SVGTags = [
    "svg", "g", "defs", "symbol", "use", "image", "switch",
    "rect", "circle", "ellipse", "line", "polyline", "polygon", "path",
    "text", "tspan", "textPath", "altGlyph", "altGlyphDef", "altGlyphItem", "glyph", "glyphRef",
    "linearGradient", "radialGradient", "pattern", "solidColor",
    "filter", "feBlend", "feColorMatrix", "feComponentTransfer", "feComposite", "feConvolveMatrix",
    "feDiffuseLighting", "feDisplacementMap", "feDropShadow", "feFlood", "feFuncA", "feFuncR", "feFuncG", "feFuncB",
    "feGaussianBlur", "feImage", "feMerge", "feMergeNode", "feMorphology", "feOffset", "feSpecularLighting",
    "feTile", "feTurbulence",
    "animate", "animateMotion", "animateTransform", "set",
    "script",
    "desc", "title", "metadata", "foreignObject"
  ];

// const h=(tag, attributes = {}, ...children)=> _h(tag, "html", attributes, ...children);
// const s=(tag, attributes = {}, ...children)=> _h(tag, "svg", attributes, ...children);

const tags = new Proxy({}, {
  get(target, prop) {
    if (typeof prop !== 'string') return undefined;
    let tag = prop.replaceAll("_","-").toLowerCase();
    let type ;
    if(HTMLTags.includes(tag)) type = 'html';
    if(SVGTags.includes(tag)) type = 'svg';
    return (...args)=>{
      // Fix undefined
      // console.log(isStateGetter(args[0]))
      // console.log(!!args)
      if(args.length === 0) {
        // console.log('length 0')
        return new UIElement$1({element : tag, name : tag, type})
      }
      if(
        ['string', 'number'].includes(typeof args[0]) 
        || args[0] instanceof UIElement$1 
        || (typeof args[0] === 'function' && args[0]().isStateGetter())
      ) return new UIElement$1({element : tag, name : tag, type}).append(...args);
      // console.log(args[0])
      return new UIElement$1({element : tag, type}).setAttr(args.shift()).append(...args)
    }
    // if(SVGTags.includes(tag)) return (...args) => new UIElement(tag,"",{el_type : "svg"}).append(...args);
    // return (...args)=>{
    //   if(!(args[0] instanceof UIElement) && args[0] instanceof Object){
    //     let attributes = args.shift()
    //     return new UIElement(tag).setAttr(attributes).append(...args)
    //   }
    //   return new UIElement(tag).append(...args);
    // }
    // // switch(tag){
    //   case "html"  : globalThis?.document?.createElement("html")
    //   case "head"  :
    //   case "style" :
    //   case "link"  :
    //   case "meta"  :
    //   case "srcipt":
    //   case "body"  : return null; break;
    //   default : return new UIElement(tag);
    // }
  }
});

class ZikoUIFlex extends UIElement$1 {
  constructor(tag = "div", w = "100%", h = "100%") {
    super({element : tag , name : "Flex"});
    this.direction = "cols";
    if (typeof w == "number") w += "%";
    if (typeof h == "number") h += "%";
    this.style({ width: w, height: h });
    this.style({ display: "flex" });
    // this.mount();
  }
  get isFlex(){
    return true;
  }
  resp(px,wrap = true) {
    this.wrap(wrap);
    if (this.element.clientWidth < px) this.vertical();
    else this.horizontal();
    return this;
  }
  setSpaceAround() {
    this.style({ justifyContent: "space-around" });
    return this;
  }
  setSpaceBetween() {
    this.style({ justifyContent: "space-between" });
    return this;
  }
  setBaseline() {
    this.style({ alignItems: "baseline" });
    return this;
  }
  gap(g) {
    if (this.direction === "row") this.style({ columnGap: g });
    else if (this.direction === "column") this.style({ rowGap: g });
    return this;
  }
  wrap(value = "wrap") {
    const values = ["no-wrap", "wrap","wrap-reverse"];
    this.style({
      flexWrap: typeof value === "string" ? value : values[+value],
    });
    return this;
  }
  _justifyContent(align = "center") {
    this.style({ justifyContent: align });
    return this;
  }
  vertical(x, y, order=1) {
    set_vertical.call(this,order);
    this.style({
      alignItems: typeof(x)==="number"?map_pos_x.call(this,x):x,
      justifyContent: typeof(y)=="number"?map_pos_y.call(this,y):y
    });
    return this;
  }
  horizontal(x, y, order=1) {
    set_horizontal.call(this,order);
    this.style({
      alignItems: typeof(y)=="number"?map_pos_y.call(this,y):y,
      justifyContent: typeof(x)==="number"?map_pos_x.call(this,x):x
    });
    return this;
  }
  show() {
    this.isHidden = false;
    this.style({ display: "flex" });
    return this;
  }
}

const Flex = (...UIElement) =>{
  let tag="div";
  if(typeof UIElement[0]==="string"){
    tag=UIElement[0];
    UIElement.pop();
  }
  return new ZikoUIFlex(tag).append(...UIElement);
};
function set_vertical(direction){
  direction == 1
    ? this.style({ flexDirection: "column" })
    : direction == -1 && this.style({ flexDirection: "column-reverse" });
  return this;
}
function set_horizontal(direction){
direction == 1
    ? this.style({ flexDirection: "row" })
    : direction == -1 && this.style({ flexDirection: "row-reverse" });
  return this;
}
function map_pos_x(align){
let pos = ["flex-start", "center", "flex-end"];
if (typeof align === "number") align = pos[align + 1];
return align;
}
function map_pos_y(align){
return map_pos_x(-align);
}

class UIElement extends UIElementCore{
    constructor({element, name, type, render}){
        super({element, name, type, render});
    }
}

class ZikoUISuspense extends UIElement{
    constructor(fallback_ui, callback){
        super({element : "div", name : "suspense"});
        this.setAttr({
            dataTemp : "suspense"
        });
        this.fallback_ui = fallback_ui;
        this.append(fallback_ui);
        (async ()=>{
            try{
                const ui = await callback();
                fallback_ui.unmount();
                this.append(ui);
            }
            catch(error){
                console.log({error});
            }
        })();
    }
}

const Suspense = (fallback_ui, callback) => new ZikoUISuspense(fallback_ui, callback);

class UIHTMLWrapper extends UIElement$1 {
    constructor(content){
        super({element : 'div', name : 'html_wrappper'});
        this.element.append(html2dom(content));
        this.style({
            display : 'contents'
        });
    }
}

function html2dom(htmlString) {
    if(globalThis?.DOMParser){
        const parser = new DOMParser();
        const doc = parser.parseFromString(`<div>${htmlString}</div>`, 'text/html');
        doc.body.firstChild.style.display = "contents";
        return doc.body.firstChild;
    }
}

const HTMLWrapper = (content) => new UIHTMLWrapper(content);

class UISVGWrapper extends UIElement$1 {
    constructor(content){
        super({element : 'div', name : 'html_wrappper'});
        this.element.append(svg2dom(content));
        this.style({
            display : 'contents'
        });
    }
}

function svg2dom(svgString) {
  if (typeof DOMParser !== "undefined") {
    const parser = new DOMParser();
    const doc = parser.parseFromString(svgString.trim(), "image/svg+xml");
    const svg = doc.documentElement;

    if (svg.nodeName === "parsererror") {
      throw new Error("Invalid SVG string");
    }
    if(svg.hasAttribute('xmlns')) return svg
    // TO Fix ...
    const {children, attributes} = svg;
    const element = document.createElementNS("http://www.w3.org/2000/svg", "svg");
    for(let {name, value} of attributes){
      element.setAttribute(name, value);
    }
    element.append(...children);

    globalThis.svg = svg;
    globalThis.children = children; 
    globalThis.attributes = attributes;
    globalThis.element = element;
    return element;
  }
  throw new Error("DOMParser is not available in this environment");
}



const SVGWrapper = (content) => new UISVGWrapper(content);

function define_wc(name, UIElement, props = {}, { mode = 'open'} = {}) {
    if (globalThis.customElements?.get(name)) {
        console.warn(`Custom element "${name}" is already defined`);
        return;
    }
    if(name.search('-') === -1){
        console.warn(`"${name}" is not a valid custom element name`);
        return; 
    }
    globalThis.customElements?.define(
        name,
        class extends HTMLElement {
            static get observedAttributes() {
                return ['style', ...Object.keys(props)];
            }

            constructor() {
                super();
                this.attachShadow({ mode });
                this.props = {};
                this.mask = {
                    ...props,
                    // style: { type: Object }
                };
            }

            connectedCallback() {
                this.render();
            }

            render() {
                this.shadowRoot.innerHTML = '';
                const item = UIElement(this.props);
                if(item instanceof Array) item.forEach(n => n.mount(this.shadowRoot)); 
                else item.mount(this.shadowRoot);
            }

            attributeChangedCallback(name, _, newValue) {
                Object.assign(this.props, {
                    [name]: this.mask[name].type(newValue)
                });
                this.render();
            }
        }
    );
}

class UISwitch extends UIElement$1{
    constructor(key, cases){
        super();
        this.key = key; 
        this.cases = cases;
        this.init();
    }
    init(){
        Object.values(this.cases).filter(n=>n != this.current).forEach(n=>n.unmount());
        super.init(this.current.element);
    }
    get current(){
        const matched = Object.keys(this.cases).find(n => n == this.key) ?? 'default';
        return this.cases[matched]
    }
    updateKey(key){
        this.key = key;
        this.replaceElementWith(this.current.element);
        // this.cache.element.replaceWith(this.current.element)
        // this.cache.element = this.current.element;
        return this;
    }
    
}

const Switch=({key, cases})=> new UISwitch(key, cases);

// export const Switch=({key, cases}) => {
//     const matched = Object.keys(cases).find(n => n == key) ?? 'default';
//     return this.cases[matched]()
// }

const { PI, sqrt: sqrt$1, cos: cos$2, sin: sin$2, acos, pow } = Math;

const linear = t => t;

// --- Sin ---
const in_sin = t => 1 - cos$2((t * PI) / 2);
const out_sin = t => sin$2((t * PI) / 2);
const in_out_sin = t => -(cos$2(PI * t) - 1) / 2;

// --- Quad ---
const in_quad = t => t ** 2;
const out_quad = t => 1 - (1 - t) ** 2;
const in_out_quad = t =>
    t < 0.5 ? 2 * (t ** 2) : 1 - (-2 * t + 2) ** 2 / 2;

// --- Cubic ---
const in_cubic = t => t ** 3;
const out_cubic = t => 1 - (1 - t) ** 3;
const in_out_cubic = t =>
    t < 0.5 ? 4 * (t ** 3) : 1 - (-2 * t + 2) ** 3 / 2;

// --- Quart ---
const in_quart = t => t ** 4;
const out_quart = t => 1 - (1 - t) ** 4;
const in_out_quart = t =>
    t < 0.5 ? 8 * (t ** 4) : 1 - (-2 * t + 2) ** 4 / 2;

// --- Quint ---
const in_quint = t => t ** 5;
const out_quint = t => 1 - (1 - t) ** 5;
const in_out_quint = t =>
    t < 0.5 ? 16 * (t ** 5) : 1 - (-2 * t + 2) ** 5 / 2;

// --- Expo ---
const in_expo = t => (t === 0 ? 0 : 2 ** (10 * t - 10));
const out_expo = t => (t === 1 ? 1 : 1 - 2 ** (-10 * t));
const in_out_expo = t =>
    t === 0
        ? 0
        : t === 1
        ? 1
        : t < 0.5
        ? 2 ** (20 * t - 10) / 2
        : (2 - 2 ** (-20 * t + 10)) / 2;

// --- Circ ---
const in_circ = t => 1 - sqrt$1(1 - t ** 2);
const out_circ = t => sqrt$1(1 - (t - 1) ** 2);
const in_out_circ = t =>
    t < 0.5
        ? (1 - sqrt$1(1 - (2 * t) ** 2)) / 2
        : (sqrt$1(1 - (-2 * t + 2) ** 2) + 1) / 2;

// --- Arc ---
const arc = t => 1 - sin$2(acos(t));

// --- Back ---
const back = (t, x = 1) => (t ** 2) * ((x + 1) * t - x);

// --- Elastic ---
const elastic = t =>
    -2 * pow(2, 10 * (t - 1)) * cos$2((20 * PI * t) / 3 * t);

// --- Back variations ---
const in_back = (t, c1 = 1.70158, c3 = c1 + 1) =>
    c3 * pow(t, 3) - c1 * (t ** 2);

const out_back = (t, c1 = 1.70158, c3 = c1 + 1) =>
    1 + c3 * pow(t - 1, 3) + c1 * pow(t - 1, 2);

const in_out_back = (t, c1 = 1.70158, c2 = c1 * 1.525) =>
    t < 0.5
        ? (pow(2 * t, 2) * ((c2 + 1) * 2 * t - c2)) / 2
        : (pow(2 * t - 2, 2) * ((c2 + 1) * (t * 2 - 2) + c2) + 2) / 2;

// --- Elastic variations ---
const in_elastic = (t, c4 = (2 * PI) / 3) =>
    t === 0
        ? 0
        : t === 1
        ? 1
        : -pow(2, 10 * t - 10) * sin$2((t * 10 - 10.75) * c4);

const out_elastic = (t, c4 = (2 * PI) / 3) =>
    t === 0
        ? 0
        : t === 1
        ? 1
        : pow(2, -10 * t) * sin$2((t * 10 - 0.75) * c4) + 1;

const in_out_elastic = (t, c5 = (2 * PI) / 4.5) =>
    t === 0
        ? 0
        : t === 1
        ? 1
        : t < 0.5
        ? -(pow(2, 20 * t - 10) * sin$2((20 * t - 11.125) * c5)) / 2
        : (pow(2, -20 * t + 10) * sin$2((20 * t - 11.125) * c5)) / 2 + 1;

// --- Bounce ---
const in_bounce = (t, n1 = 7.5625, d1 = 2.75) =>
    1 - out_bounce(1 - t, n1, d1);

const out_bounce = (t, n1 = 7.5625, d1 = 2.75) => {
    if (t < 1 / d1) return n1 * t * t;
    if (t < 2 / d1) return n1 * (t -= 1.5 / d1) * t + 0.75;
    if (t < 2.5 / d1) return n1 * (t -= 2.25 / d1) * t + 0.9375;
    return n1 * (t -= 2.625 / d1) * t + 0.984375;
};

const in_out_bounce = (t, n1 = 7.5625, d1 = 2.75) =>
    t < 0.5
        ? out_bounce(1 - 2 * t, n1, d1) / 2
        : out_bounce(2 * t - 1, n1, d1) / 2;

// --- Step / Discrete ---
const step = (t, steps = 5) => Math.floor(t * steps) / steps;
const discret = (t, segments = 5) => Math.ceil(t * segments) / segments;

class TimeAnimation {
  constructor(callback, { ease = linear, step = 50, t0 = 0, start = true, duration = 3000 } = {}) {
    this.callback = callback;
    this.state = {
      isRunning: false,
      animationId: null,
      startTime: null,
      ease,
      step,
    //   interval: [t0, t1],
      autoStart: start,
      duration
    };

    this.t = 0;   // elapsed time
    this.tx = 0;  // normalized [0,1]
    this.ty = 0;  // eased value
    this.i = 0;   // frame index

    if (this.state.autoStart) {
      this.start();
    }
  }

  // ---- private loop handler ----
  #tick = () => {
    this.t += this.state.step;
    this.i++;

    this.tx = map$1(this.t, 0, this.state.duration, 0, 1);
    this.ty = this.state.ease(this.tx);

    this.callback(this);

    if (this.t >= this.state.duration) {
      clearInterval(this.state.animationId);
      this.state.isRunning = false;
    }
  };

  // ---- core runner ----
  #run(reset = true) {
    if (!this.state.isRunning) {
      if (reset) this.reset(false);

      this.state.isRunning = true;
      this.state.startTime = Date.now();
      this.state.animationId = setInterval(this.#tick, this.state.step);
    }
    return this;
  }

  // ---- lifecycle methods ----
  start() {
    return this.#run(true);
  }

  pause() {
    if (this.state.isRunning) {
      clearInterval(this.state.animationId);
      this.state.isRunning = false;
    }
    return this;
  }

  resume() {
    return this.#run(false);
  }

  stop() {
    this.pause();
    this.reset(false);
    return this;
  }

  reset(restart = true) {
    this.t = 0;
    this.tx = 0;
    this.ty = 0;
    this.i = 0;

    if (restart) this.start();
    return this;
  }
}

// Hook-style factory
const animation = (callback, {ease, t0, t1, start, duration} = {}) =>
  new TimeAnimation(callback, {ease, t0, t1, start, duration});

class Tick {
  constructor(fn, ms, count = Infinity, start) {
    this.ms = ms;
    this.fn = fn;
    this.count = count;
    this.frame = 1;
    this.id = null;
    this.running = false;
    if(start) this.start();
  }

  start() {
    if (!this.running) {
      this.running = true;
      this.frame = 1;
      this.id = setInterval(() => {
        if (this.frame > this.count) {
          this.stop();
          return;
        }
        this.fn.call(null, this);
        this.frame++;
      }, this.ms);
    }
    return this;
  }

  stop() {
    if (this.running) {
      this.running = false;
      clearInterval(this.id);
      this.id = null;
    }
    return this;
  }

  isRunning() {
    return this.running;
  }
}

// Helper factory
const tick = (fn, ms, count = Infinity, start = true) => new Tick(fn, ms, count, start);

class Clock extends Tick {
  constructor(tickMs = 1000 / 60) {
    super(tickMs, () => this._tick());
    this.elapsed = 0;
    this._lastTime = performance.now();
    this._callbacks = new Set();
  }

  _tick() {
    const now = performance.now();
    const delta = now - this._lastTime;
    this.elapsed += delta;
    this._lastTime = now;

    for (const cb of this._callbacks) {
      cb({ elapsed: this.elapsed, delta });
    }
  }

  onTick(cb) {
    this._callbacks.add(cb);
    return () => this._callbacks.delete(cb); 
  }

  reset() {
    this.elapsed = 0;
    this._lastTime = performance.now();
  }

  pause() {
    super.stop();
  }

  resume() {
    this._lastTime = performance.now();
    super.start();
  }
}

const clock = (tickMs) => new Clock(tickMs);


/* 

    const clock = new Clock(200);

    clock.onTick(({ elapsed, delta }) => {
      console.log(`Elapsed: ${elapsed.toFixed(0)}ms, Delta: ${delta.toFixed(0)}ms`);
    });

    clock.start();

    setTimeout(() => clock.pause(), 1000);  
    setTimeout(() => clock.resume(), 2000); 

*/

class TimeScheduler {
  constructor(tasks = [], { repeat = 1, loop = false } = {}) {
    this.tasks = tasks;
    this.repeat = repeat;
    this.loop = loop;

    this.stopped = false;
    this.running = false;

    // lifecycle hooks
    this.onStart = null;
    this.onTask = null;
    this.onEnd = null;
  }

  async run() {
    if (this.running) return;
    this.running = true;
    this.stopped = false;

    if (this.onStart) this.onStart();

    let repeatCount = this.repeat;

    do {
      for (const task of this.tasks) {
        if (this.stopped) return;

        if (Array.isArray(task)) {
          // Parallel tasks
          await Promise.all(
            task.map(({ fn, delay = 0 }) =>
              new Promise(async (resolve) => {
                if (delay > 0) await new Promise(r => setTimeout(r, delay));
                if (this.onTask) this.onTask(fn);
                await fn();
                resolve();
              })
            )
          );
        } else {
          // Single task
          const { fn, delay = 0 } = task;
          if (delay > 0) await new Promise(r => setTimeout(r, delay));
          if (this.onTask) this.onTask(fn);
          await fn();
        }
      }
    } while (this.loop && !this.stopped && (repeatCount === Infinity || repeatCount-- > 1));

    if (!this.stopped && this.onEnd) this.onEnd();
    this.running = false;
  }

  stop() {
    this.stopped = true;
    this.running = false;
  }

  addTask(task) {
    this.tasks.push(task);
  }

  clearTasks() {
    this.tasks = [];
  }
}

const Scheduler = (tasks, { repeat = null} = {}) => new TimeScheduler(tasks, { repeat});

const step_fps = (step_or_fps) => 1000 / step_or_fps;

const sleep= ms => new Promise(res => setTimeout(res, ms));
function timeout(ms, fn) {
  let id;
  const promise = new Promise((resolve) => {
    id = setTimeout(() => {
      if (fn) fn();
      resolve();
    }, ms);
  });

  return {
    id,
    clear: () => clearTimeout(id),
    promise
  };
}

class TimeLoop {
  constructor(callback, { step = 1000, t0 = 0, t1 = Infinity, autoplay = true } = {}) {
    this.callback = callback;
    this.cache = {
      isRunning: false,
      id: null,
      last_tick: null,
      step,
      t0,
      t1,
      autoplay,
      pauseTime: null,
      frame : 0,
    };

    if (autoplay) {
      t0 ? this.startAfter(t0) : this.start();
      if (t1 !== Infinity) this.stopAfter(t1);
    }
  }

  get frame(){
    return this.cache.frame;
  }
  get elapsed(){
    return this.cache.elapsed;
  }

  start() {
    if (!this.cache.isRunning) {
      this.cache.frame = 0;
      this.cache.isRunning = true;
      this.cache.last_tick = Date.now();
      this.animate();
    }
    return this;
  }

  pause() {
    if (this.cache.isRunning) {
      clearTimeout(this.cache.id);
      this.cache.isRunning = false;
      this.cache.pauseTime = Date.now();
    }
    return this;
  }

  resume() {
    if (!this.cache.isRunning) {
      this.cache.isRunning = true;
      if (this.cache.pauseTime) {
        // adjust start time so delta stays consistent
        const pausedDuration = Date.now() - this.cache.pauseTime;
        this.cache.last_tick += pausedDuration;
      }
      this.animate();
    }
    return this;
  }

  stop() {
    this.pause();
    this.cache.frame = 0;
    return this;
  }

  startAfter(t = 1000) {
    setTimeout(() => this.start(), t);
    return this;
  }

  stopAfter(t = 1000) {
    setTimeout(() => this.stop(), t);
    return this;
  }

  animate = () => {
    if (this.cache.isRunning) {
      const now = Date.now();
      const delta = now - this.cache.last_tick;

      if (delta >= this.cache.step) {
        this.cache.elapsed = now - (this.cache.t0 || 0);
        this.callback(this);
        this.cache.frame++;
        this.cache.last_tick = now - (delta % this.cache.step);
      }

      this.cache.id = setTimeout(this.animate, 0);
    }
  }
}

const loop = (callback, options = {}) => new TimeLoop(callback, options);


// Helpers
// const useFps = (fps) => 1000 / fps;

// const _loop = loop( e => {
//   console.log("Frame:", e.frame, " Elapsed: ", e.elapsed);
// });

const time_memory_Taken = (callback) => {
    const t0 = Date.now();
    const m0 = performance.memory.usedJSHeapSize;
    const result = callback();
    const t1 = Date.now();
    const m1 = performance.memory.usedJSHeapSize;
    const elapsedTime = t1 - t0;
    const usedMemory = m1 - m0;
    return { 
        elapsedTime,
        usedMemory, 
        result 
    };
  };

const waitForUIElm=(UIElement)=>{
    return new Promise(resolve => {
        if (UIElement.element) {
            return resolve(UIElement.element);
        }
  
        const observer = new MutationObserver(() => {
            if (UIElement.element) {
                resolve(UIElement.element);
                observer.disconnect();
            }
        });
  
        observer.observe(document?.body, {
            childList: true,
            subtree: true
        });
    });
  };
  const waitForUIElmSync=(UIElement,timeout=2000)=>{
    const t0=Date.now();
    while(Date.now()-t0<timeout){
      if(UIElement.element)return UIElement.element
    }
  };

// import Ease from "./ease.js";
const wait=(delayInMS)=>{
    return new Promise((resolve) => setTimeout(resolve, delayInMS));
};
const timeTaken = callback => {
    console.time('timeTaken');
    const r = callback();
    console.timeEnd('timeTaken');
    return r;
};

const preload=(url)=>{
    const xhr = new XMLHttpRequest();
    xhr.open("GET", url, false); 
    xhr.send();
    if (xhr.status === 200) {
      return xhr.responseText;
    } else {
      throw new Error(`Failed to fetch data from ${url}. Status: ${xhr.status}`);
    }
};

async function fetchdom(url='https://github.com/zakarialaoui10') {
  try {
    const response = await fetch(url);
    if (!response.ok) throw new Error(`HTTP error! status: ${response.status}`);
    const html = await response.text();
    const dom = new DOMParser().parseFromString(html, 'text/html');
    return dom.documentElement;
  } catch (err) {
    console.error('Failed to fetch DOM:', err);
    throw err;
  }
}

function fetchdomSync(url='https://github.com/zakarialaoui10') {
  try {
    const data = preload(url);
    const dom = new DOMParser().parseFromString(data, 'text/html');
    return dom.documentElement;
  } catch (err) {
    console.error('Failed to fetch DOM synchronously:', err);
    throw err;
  }
}

globalThis.fetchdom=fetchdom;
globalThis.fetchdomSync=fetchdomSync;

const csv2arr = (csv, delimiter = ",")=>csv.trim().trimEnd().split("\n").map(n=>n.split(delimiter));
const csv2matrix = (csv, delimiter = ",")=>new Matrix(csv2arr(csv,delimiter));
const csv2object = (csv, delimiter = ",") => {
    const [header, ...rows] = csv2arr(csv,delimiter);
    const result = rows.map(row => {
        const obj = {};
        header.forEach((key, index) => {
            obj[key] = row[index];
        });
        return obj;
    });
    return result;
};
const csv2json = (csv, delimiter = ",") => JSON.stringify(csv2object(csv,delimiter));
const csv2sql=(csv, Table)=>{
    const lines = csv.trim().trimEnd().split('\n').filter(n=>n);
    const columns = lines[0].split(',');
    let sqlQuery = `INSERT INTO ${Table} (${columns.join(', ')}) Values `;
    let sqlValues = [];
    for (let i = 1; i < lines.length; i++) {
      const values = lines[i].split(',');
      sqlValues.push(`(${values})`);
    }
    return sqlQuery+sqlValues.join(",\n");
  };

const _objects2arr=data=>data instanceof Array?[Object.keys(data[0]),...data.map(n=>Object.values(n))]:[Object.keys(data)];
const _objects2csv=(data,delimiter)=>_objects2arr(data).map(n=>n.join(delimiter)).join("\n");
const json2arr=json=>json instanceof Object?_objects2arr(json):_objects2arr(JSON.parse(json));
const json2csv=(json,delimiter=",")=>json instanceof Object?_objects2csv(json,delimiter):_objects2csv(JSON.parse(json),delimiter);
const json2csvFile=(json,delimiter)=>{
    const str=json2csv(json,delimiter);
    const blob=new Blob([str], { type: 'text/csv;charset=utf-8;' });
    return {
       str,
       blob,
       url:URL.createObjectURL(blob)
    }
};
const _processObject=(obj, indent)=>{
    const yml = [];
    if (Array.isArray(obj)) {
        obj.forEach(item => {
            if (typeof item === 'object' && item !== null) {
                yml.push(`${indent}-`);
                const nestedLines = _processObject(item, `${indent}  `);
                yml.push(...nestedLines);
            } else yml.push(`${indent}- ${item}`);
        });
    } else {
        for (const key in obj) {
            if (obj.hasOwnProperty(key)) {
                const value = obj[key];
                if (typeof value === 'object' && value !== null) {
                    yml.push(`${indent}${key}:`);
                    const nestedLines = _processObject(value, `${indent}  `);
                    yml.push(...nestedLines);
                } else {
                    yml.push(`${indent}${key}: ${value}`);
                }
            }
        }
    }
    return yml;
};
const _object2yml=(object,indent="")=>_processObject(object,indent).join('\n');
const json2yml=(json,indent)=>json instanceof Object?_object2yml(json,indent):_object2yml(JSON.parse(json),indent);
const json2ymlFile=(json,indent)=>{
    const str=json2yml(json,indent);
    const blob=new Blob([str], { type: 'text/yml;charset=utf-8;' });
    return {
       str,
       blob,
       url:URL.createObjectURL(blob)
    }
};
const json2xml=(json, indent = 1)=>{
    let xml = '';
    for (const key in json) {
      if (json.hasOwnProperty(key)) {
        const value = json[key];
        xml += '\n' + ' '.repeat(indent) + `<${key}>`;
        (typeof value === 'object') ? xml += json2xml(value, indent + 2) : xml += `${value}`;
        xml += `</${key}>`;
      }
    }
    return xml.trim();
  };
const json2xmlFile=(json,indent)=>{
    const str=json2xml(json,indent);
    const blob=new Blob([str], { type: 'text/xml;charset=utf-8;' });
    return {
       str,
       blob,
       url:URL.createObjectURL(blob)
    }
};

const svg2str=svg=>(new XMLSerializer()).serializeToString(svg);
const svg2ascii=svg=>btoa(svg2str(svg));
const svg2imgUrl=svg=>'data:image/svg+xml;base64,'+svg2ascii(svg);
const svg2img=(svg,render=true)=>tags.img(svg2imgUrl(svg)).mount(render);

// const obj2str=(object)=>{
//     const recursiveToString = (obj) => {
//       if (Array.isArray(obj)) return arr2str(obj);
//       if (typeof obj === 'object' && obj !== null) {
//         return `{ ${Object.entries(obj)
//           .map(([key, value]) => `${key}:${recursiveToString(value)}`)
//           .join(" , ")} }`;
//       }
//       return String(obj); 
//     };
//     return recursiveToString(object);
//   };
//   const obj2str = (object) => {
//     const recursiveToString = (obj, indentLevel = 0) => {
//       const indent = '  '.repeat(indentLevel);
//       const nextIndent = '  '.repeat(indentLevel + 1);
//       if(Array.isArray(obj)) return arr2str(obj, indentLevel);
//       if(obj instanceof Complex || obj instanceof Matrix) return obj.toString();
//       if (typeof obj === 'object' && obj !== null) {
//         const entries = Object.entries(obj)
//           .map(([key, value]) => `${nextIndent}${key}: ${recursiveToString(value, indentLevel + 1)}`)
//           .join(",\n");
  
//         return `{\n${entries}\n${indent}}`;
//       }
  
//       return String(obj);
//     };
  
//     return recursiveToString(object);
//   };
// const obj2str = (object, useIndentation = true, indentLevel = 0) => {
//     const recursiveToString = (obj, level = 0) => {
//       const indent = useIndentation ? '  '.repeat(level) : '';
//       const nextIndent = useIndentation ? '  '.repeat(level + 1) : '';
//     if (Array.isArray(obj)) return arr2str(obj, false, level);
//     if(obj instanceof Complex || obj instanceof Matrix) return obj.toString();
//     if (typeof obj === 'object' && obj !== null) {
//         const entries = Object.entries(obj)
//           .map(([key, value]) => useIndentation 
//             ? `${nextIndent}${key}: ${recursiveToString(value, level + 1)}` 
//             : `${key}: ${recursiveToString(value, level + 1)}`
//           ).join(useIndentation ? ",\n" : ", ");
  
//         return useIndentation 
//           ? `{\n${entries}\n${indent}}` 
//           : `{${entries}}`;
//       }
  
//       return String(obj);
//     };
  
//     return recursiveToString(object, indentLevel);
//   };

const obj2str=(obj)=>JSON.stringify(
  mapfun(n=>{
    if(["number","string","boolean","bigint"].includes(typeof n)) return String(n);
    if(n instanceof Complex || n instanceof Matrix) return n.toString();
    if(n instanceof Array) return arr2str(n)
  },
  obj), null, "  ")
  .replace(/"([^"]+)":/g, '$1:') // Remove Quotes from Keys 
  .replace(/: "([^"]+)"/g, ': $1'); // Remove Quotes from str values

const getMaxDepth = arr=> {
  if (!Array.isArray(arr)) return 0;
  let maxDepth = 1;
  for (const element of arr) {
    if (Array.isArray(element)) {
      const depth = getMaxDepth(element);
      if (depth + 1 > maxDepth) {
        maxDepth = depth + 1;
      }
    }
  }
  return maxDepth;
};
const arr2str = (arr) => {
  let level = 0;
  function arrStringify(arr) {
    let max = getMaxDepth(arr);
    let useIdentation = 0;
    if (arr.some((n) => Array.isArray(n))) {
      level++;
      useIdentation = 1;   
    }
    return (
      "[" +
      arr.map((n, i) => {
        if (["number", "string", "boolean", "bigint"].includes(typeof n))
          return String(n);
        if (n instanceof Complex) return n.toString();
        if (n instanceof Array) {
          return `\n${"  ".repeat(level)}${arrStringify(n)}${i === arr.length - 1 ? "\n" : ""}`;
        }
        if( n instanceof Object) return obj2str(n);
      }) 
      + `${"  ".repeat((max+level+1) * useIdentation)}]` 
    );
  }
  return arrStringify(arr);
};

const json2css=(json, indentLevel = 0)=>{
    json = trimKeys(json);
    let cssText = '';
    const indent = '  '.repeat(indentLevel); 

    for (let selector in json) {
        if (typeof json[selector] === 'object') {
            cssText += `${indent}${selector} {\n`;
            const properties = json[selector];
            for (let property in properties) {
                if (typeof properties[property] === 'object') {
                    cssText += json2css({ [property]: properties[property] }, indentLevel + 1);
                } else {
                    cssText += `${indent}  ${property.replace(/[A-Z]/g, match => '-' + match.toLowerCase())}: ${properties[property]};\n`;
                }
            }

            cssText += `${indent}}\n`; 
        }
    }

    return cssText;
};
function trimKeys(obj) {
    if (typeof obj !== 'object' || obj === null) {
        return obj; 
    }

    return Object.keys(obj).reduce((acc, key) => {
        const trimmedKey = key.trim();
        acc[trimmedKey] = trimKeys(obj[key]);
        return acc;
    }, Array.isArray(obj) ? [] : {});
}

// import { ZikoHead , useHead} from "../reactivity/hooks/head/index.js";
class ZikoApp {
    constructor({head = null, wrapper = null, target = null}){
        this.head = head;
        this.wrapper = wrapper;
        this.target = target;
        this.init();        
    }
    get isZikoApp(){
        return true;
    }
    init(){
        this.head && this.setHead(this.head);
        this.wrapper && this.setWrapper(this.wrapper);
        this.target && this.setTarget(this.target);
        if(this.wrapper && this.target)this.wrapper.mount(this.target);
    }
    setTarget(target){
        if(target instanceof HTMLElement) this.target = target;
        else if (typeof target === "string") this.target = globalThis?.document?.querySelector(target);
        return this;
    }
    setWrapper(wrapper){
        if(wrapper?.isUIElement) this.wrapper = wrapper;
        else if(typeof wrapper === "function") this.wrapper = wrapper();
        return this;
    }
    // setHead(head){
    //     if(head instanceof ZikoHead) this.head = head;
    //     else this.head = useHead(head);
    //     return this;  
    // }
    
}
const App = ({head, wrapper, target}) => new ZikoApp({head, wrapper, target});

function routesMatcher(mask, route) {
    const maskSegments = mask.split('/');
    const routeSegments = route.split('/');
    if (maskSegments.length !== routeSegments.length) {
        return false;
    }
    for (let i = 0; i < maskSegments.length; i++) {
        const maskSegment = maskSegments[i];
        const routeSegment = routeSegments[i];
        if (maskSegment.startsWith(':')) {
            continue;
        } else if (maskSegment !== routeSegment) {
            return false;
        }
    }    
    return true;
}
function dynamicRoutesParser(mask, route) {
    const maskSegments = mask.split('/');
    const routeSegments = route.split('/');
    const params = {};
    if (maskSegments.length !== routeSegments.length) {
        return params; 
    }
    for (let i = 0; i < maskSegments.length; i++) {
        const maskSegment = maskSegments[i];
        const routeSegment = routeSegments[i];
        if (maskSegment.startsWith(':')) {
            const paramName = maskSegment.slice(1); 
            params[paramName] = routeSegment;
        } else if (maskSegment !== routeSegment) {
            return {};
        }
    }
    return params;
}

function isDynamic(path) {
    const DynamicPattern = /:\w+/;    
    return DynamicPattern.test(path);
  }

// // Example usage:
// const mask = "app/lang/:lang/id/:id";
// const route = "app/lang/en/id/7";
// console.log(dynamicRoutesParser(mask, route)); // Should return { lang: "en", id: "7" }

class ZikoSPA extends ZikoApp{
    constructor({head, wrapper, target, routes}){
        super({head, wrapper, target});
        this.routes=new Map([
            ["404",text("Error 404")],
            ...Object.entries(routes)
        ]);
        this.clear();
        globalThis.onpopstate = this.mount(location.pathname);
    }
    clear(){
        [...this.routes].forEach(n=>{
            !isDynamic(n[0]) && n[1]?.isUIElement && n[1].unmount();
        });   
        // this.wrapper.clear();
        return this;
    }
    mount(path){
        const [mask, callback] = [...this.routes].find(route=>routesMatcher(route[0],path));
        let element ;
        if(isDynamic(mask)){
            const params = dynamicRoutesParser(mask, path);
            element = callback.call(this,params);
        }
        else {
            callback?.isUIElement && callback.mount(this.wrapper); 
            if(typeof callback === "function") element = callback();  
        }
        if(element?.isUIElement) element.mount(this.wrapper);
        // if(element?.isZikoApp) element.mount(this.wrapper);
        if(element instanceof Promise){
            element.then(e=>e.mount(this.wrapper));
        }
        globalThis.history.pushState({}, "", path);
        return this;
    }
}
const SPA=({head, wrapper, target, routes})=>new ZikoSPA({head, wrapper, target, routes});


/*
 // Static 
  S.get("/url",wrapper)
// Dynamique 
 s.get("/url/name/:name/id/:id",(path,name,id)=>handler())
// regEx
*/

function parseQueryParams(queryString) {
    const params = {};
    queryString.replace(/[A-Z0-9]+?=([\w|:|\/\.]*)/gi, (match) => {
        const [key, value] = match.split('=');
        params[key] = value;
    });
    return params;
}

function defineParamsGetter(target ){
    Object.defineProperties(target, {
        'QueryParams': {
            get: function() {
                return parseQueryParams(globalThis.location.search.substring(1));
            },
            configurable: false,
            enumerable: true 
        },
        'HashParams': {
            get: function() {
                const hash = globalThis.location.hash.substring(1);
                return hash.split("#");
            },
            configurable: false,
            enumerable: true 
        }
    });
}

// import.meta.glob('./src/pages/**/*.js')
async function FileBasedRouting(pages /* use import.meta.glob */){
   const routes = Object.keys(pages);
   const root = findCommonPath(routes);
   const pairs = {};
   for(let i=0; i<routes.length; i++){
      const module = await pages[routes[i]]();
      const component = await module.default;
      Object.assign(pairs,{[customPath(routes[i], root)]:component});
   }
   return SPA({
      target : document.body,
      routes : {
         "/" : ()=>{},
         ...pairs
      },
      wrapper : tags.section()
   })   
}
function customPath(inputPath, root = './src/pages', extensions = ['js', 'ts']) {
   if(root.at(-1)==="/") root = root.slice(0, -1);
   const normalizedPath = inputPath.replace(/\\/g, '/').replace(/\[(\w+)\]/g, '$1/:$1');
   const parts = normalizedPath.split('/');
   const rootParts = root.split('/');
   const rootIndex = parts.indexOf(rootParts.at(-1));
   if (rootIndex !== -1) {
       const subsequentParts = parts.slice(rootIndex + 1);
       const lastPart = parts.at(-1);
       const isIndexFile = lastPart === 'index.js' || lastPart === 'index.ts';
    //    console.log({extensions, subsequentParts, lastPart, isIndexFile, rootParts, rootIndex, parts})
       const hasValidExtension = extensions.some(ext => lastPart === `.${ext}` || lastPart.endsWith(`.${ext}`));
       if (isIndexFile) {
           return '/' + (subsequentParts.length > 1 ? subsequentParts.slice(0, -1).join('/') : '');
       }
       if (hasValidExtension) {
           return '/' + subsequentParts.join('/').replace(/\.(js|ts)$/, '');
       }
   }
   return '';
}
function findCommonPath(paths) {
   if (paths.length === 0) return '';
   const splitPaths = paths.map(path => path.split('/'));
   const minLength = Math.min(...splitPaths.map(parts => parts.length));
   let commonParts = [];
   for (let i = 0; i < minLength; i++) {
       const part = splitPaths[0][i]; 
       if (splitPaths.every(parts => parts[i] === part || parts[i].startsWith('['))) {
           commonParts.push(part);
       } else {
           break; 
       }
   }
   const commonPath = commonParts.join('/') + (commonParts.length ? '/' : '');
   return commonPath;
}

function useDerived(deriveFn, sources) {
    let value = deriveFn(...sources.map(s => s().value));
    const subscribers = new Set();

    sources.forEach(source => {
        const srcValue = source();
        srcValue._subscribe(() => {
            {
                const newVal = deriveFn(...sources.map(s => s().value));
                if (newVal !== value) {
                    value = newVal;
                    subscribers.forEach(fn => fn(value));
                }
            }
        }); 
    });
    return () => ({
        value,
        isStateGetter : () => true,
        _subscribe: (fn) => subscribers.add(fn)
    })
}

const useReactive = (nested_value) => mapfun(
    n => {
        const state = useState(n);
        return {
            get : state[0],
            set : state[1],
        }
    }, 
    nested_value
);

class UseThread {
    #worker;
    #callbacks = new Map();
    #idCounter = 0;

    constructor() {
        const workerCode = `
            this.onmessage = function(e) {
                const { id, funStr, args, close } = e.data;
                try {
                    const func = new Function("return " + funStr)();
                    const result = func(...args);
                    postMessage({ id, result });
                } catch (error) {
                    postMessage({ id, error: error.message });
                } finally {
                    if (close) self.close();
                }
            }
        `;
        const blob = new Blob([workerCode], { type: "text/javascript" });
        this.#worker = new Worker(URL.createObjectURL(blob));

        this.#worker.addEventListener("message", (e) => {
            const { id, result, error } = e.data;
            const callback = this.#callbacks.get(id);
            if (!callback) return;

            callback(result, error);
            this.#callbacks.delete(id);
        });
    }
    call(func, callback, args = [], close = true) {
        if (typeof func !== "function") throw new TypeError("func must be a function");
        const id = ++this.#idCounter;
        this.#callbacks.set(id, callback);

        this.#worker.postMessage({
            id,
            funStr: func.toString(),
            args,
            close
        });

        return this;
    }

    terminate() {
        this.#worker.terminate();
    }
}

const useThread = (func, callback, args = [], close = true) => new UseThread().call(func, callback, args, close);

class UseEventEmitter {
    constructor(maxListeners = 10) {
        this.events = {};
        this.maxListeners = maxListeners;
    }

    on(event, listener) {
        if (!this.events[event]) this.events[event] = [];
        this.events[event].push(listener);
        if (this.events[event].length > this.maxListeners) {
            console.warn(`Warning: Possible memory leak. Event '${event}' has more than ${this.maxListeners} listeners.`);
        }
        return this;
    }

    once(event, listener) {
        const wrapper = (...args) => {
            this.off(event, wrapper);
            listener(...args);
        };
        return this.on(event, wrapper);
    }

    off(event, listener) {
        const listeners = this.events[event];
        if (!listeners) return this;

        const index = listeners.indexOf(listener);
        if (index !== -1) {
            listeners.splice(index, 1);
        }

        return this;
    }

    emit(event, data) {
        const listeners = this.events[event];
        if (!listeners) return false;

        // Make a copy so removing listeners inside callbacks doesn't affect iteration
        [...listeners].forEach(listener => {
            try {
                listener(data);
            } catch (e) {
                console.error(`Error in listener for '${event}':`, e);
            }
        });

        return true;
    }
    remove(event){
        delete this.events[event];  
        return this; 
    }
    clear() {
        this.events = {};
        return this;
    }

    setMaxListeners(max) {
        this.maxListeners = max;
        return this;
    }
}

const useEventEmitter = (maxListeners) => new UseEventEmitter(maxListeners);

/*
 [
    {
        query: '(min-width: 600px)',
        callback: () => console.log(1)
    },
    {
        query: '(max-width: 300px)',
        callback: () => console.log(2)
    }
 ]
*/

class UseMediaQuery {
    #mediaQueryRules;
    #fallback;
    #lastCalledCallback = null;

    constructor(mediaQueryRules = [], fallback = () => {}) {
        this.#mediaQueryRules = mediaQueryRules;
        this.#fallback = fallback;

        this.#init();
    }

    // PRIVATE: check if ANY rule matches
    #checkAllRules() {
        return this.#mediaQueryRules.some(
            ({ query }) => globalThis.matchMedia(query).matches
        );
    }

    // PRIVATE: installs listeners and initial checks
    #init() {
        this.#mediaQueryRules.forEach(({ query, callback }) => {
            const mediaQueryList = globalThis.matchMedia(query);

            const checkMatches = () => {
                const anyMatch = this.#checkAllRules();

                if (mediaQueryList.matches) {
                    callback();
                    this.#lastCalledCallback = callback;
                } else if (!anyMatch && this.#lastCalledCallback !== this.#fallback) {
                    this.#fallback();
                    this.#lastCalledCallback = this.#fallback;
                }
            };

            checkMatches();
            mediaQueryList.addEventListener("change", checkMatches);
        });
    }
}

const useMediaQuery = (mediaQueryRules, fallback) =>
    new UseMediaQuery(mediaQueryRules, fallback);

class UseTitle {
    constructor(title = document.title, withEmitter = true) {
        this.cache = {
            emitter: null
        };

        if (withEmitter) this.useEventEmitter();
        this.set(title);
    }

    useEventEmitter() {
        this.cache.emitter = useEventEmitter();
        return this;
    }

    setTitle(title) {
        if (title !== document.title) {
            document.title = title;

            if (this.cache.emitter) {
                this.cache.emitter.emit("ziko:title-changed", title);
            }
        }
        return this;
    }

    get current() {
        return document.title;
    }

    onChange(callback) {
        if (this.cache.emitter) {
            this.cache.emitter.on("ziko:title-changed", callback);
        }
        return this;
    }
}

const useTitle = (title, withEmitter = true) => new UseTitle(title, withEmitter);

class UseRoot {
    constructor(PropsMap, { namespace = 'Ziko', ValidateCssProps = false } = {}) {
        this.currentPropsMap = PropsMap;
        this.namespace = namespace;
        this.ValidateCssProps = ValidateCssProps;
        this.use(PropsMap);
    }

    use(PropsMap) {
        if (this.ValidateCssProps) ValidateCssPropsFn(PropsMap);
        this.currentPropsMap = PropsMap;
        this.#maintain();
        return this;
    }

    #maintain() {
        const root = globalThis?.document?.documentElement?.style;
        for (const prop in this.currentPropsMap) {
            const cssProp = this.namespace ? `--${this.namespace}-${prop}` : `--${prop}`;
            root.setProperty(cssProp, this.currentPropsMap[prop]);

            Object.defineProperty(this, prop, {
                value: `var(${cssProp})`,
                writable: true,
                configurable: true,
                enumerable: false
            });
        }
    }
}

function ValidateCssPropsFn(PropsMap) {
    const validProps = new Set(Object.keys(document.documentElement.style));
    for (const key in PropsMap) {
        if (!validProps.has(key)) {
            throw new Error(`Invalid CSS property: "${key}"`);
        }
    }
}

const useRoot = (PropsMap, options = {}) => new UseRoot(PropsMap, options);


// Usage 

/*
const Styles = {
 S1 : {
  background : 'white',
  color : 'darkblue'
  border : '2px darkblue solid"'
 },
 S2 : {
  background : 'darkblue',
  color : 'white'
  border : '2px green solid"'
 }
}
const {use, border, background, color} = useRoot(Style.S1)

tags.p("Test useRoot ").style({
  border,
  color,
  background,
  padding : '10px'
})

*/

let {sqrt, cos: cos$1, sin: sin$1, exp, log, cosh: cosh$1, sinh} = Math;
// Math.abs = new Proxy(Math.abs, {
//   apply(target, thisArg, args) {
//     const x = args[0]
//       if(typeof x === 'number') return target.apply(thisArg, args);
//       if(x?.isComplex?.()){
//         const {a, b, z, phi} = x
//         const complex = (a, b) => new x.constructor(a, b) 
//         switch(target.name){
//           case 'abs' : return a.z;
//           case 'sqrt' : return complex(sqrt(z)*cos(phi/2),sqrt(z)*sin(phi/2));
//         }
//       }
      
//   }
// });


for (const key of Object.getOwnPropertyNames(Math)) {
  const fn = Math[key];
  if (typeof fn === "function") {
    Math[key] = new Proxy(fn, {
      apply(target, thisArg, args) {
        // console.log(target)
        const x = args[0];
        if(typeof x === 'number' || args.length === 0) return target.apply(thisArg, args);
        if(x?.isComplex?.()){
            const {a, b, z, phi} = x;
            const complex = (a, b) => new x.constructor(a, b);
            switch(target.name){
                case 'abs' : return x.z;
                case 'sqrt' : return complex(sqrt(z)*cos$1(phi/2),sqrt(z)*sin$1(phi/2));
                case 'log' : return complex(log(z), phi);
                case 'exp' : return complex(exp(a)*cos$1(b),exp(a)*sin$1(b));
                case 'cos' : return complex(cos$1(a)*cosh$1(b),-(sin$1(a)*sinh(b)));
                case 'sin' : return complex(sin$1(a)*cosh$1(b),cos$1(a)*sinh(b));
                case 'tan' : {
                    const DEN = cos$1(2*a)+cosh$1(2*b);
                    return complex(sin$1(2*a) /DEN, sinh(2*b)/DEN);
                }
                case 'cosh' : return complex(cosh$1(a)*cos$1(b),sinh(a)*sin$1(b));
                case 'sinh' : return complex(sinh(a)*cos$1(b),cosh$1(a)*sin$1(b));
                case 'tanh' : {
                    const DEN=cosh$1(2*a)+cos$1(2*b);
                    return complex(sinh(2*a)/DEN,sin$1(2*b)/DEN)
                }
                default : return target.apply(thisArg, args)
             }
        }
        // if( x.isMatrix?.()){
        //     const {rows, cols, arr} = x
        //     // return new x.constructor(rows, cols, arr.flat(1).map(n=>));
        // }
        throw new TypeError(`Math.${key} expects only numbers`);
      }
    });
  }
}

if(globalThis?.document){
    document?.addEventListener("DOMContentLoaded", __Ziko__.__Config__.init());
}

exports.App = App;
exports.Clock = Clock;
exports.Complex = Complex;
exports.E = E;
exports.EPSILON = EPSILON;
exports.FileBasedRouting = FileBasedRouting;
exports.Flex = Flex;
exports.HTMLWrapper = HTMLWrapper;
exports.Matrix = Matrix;
exports.PI = PI$1;
exports.Random = Random;
exports.SPA = SPA;
exports.SVGWrapper = SVGWrapper;
exports.Scheduler = Scheduler;
exports.Suspense = Suspense;
exports.Switch = Switch;
exports.Tick = Tick;
exports.TimeAnimation = TimeAnimation;
exports.TimeLoop = TimeLoop;
exports.TimeScheduler = TimeScheduler;
exports.UIElement = UIElement$1;
exports.UIHTMLWrapper = UIHTMLWrapper;
exports.UINode = UINode;
exports.UISVGWrapper = UISVGWrapper;
exports.UISwitch = UISwitch;
exports.UIView = UIView;
exports.UseRoot = UseRoot;
exports.UseThread = UseThread;
exports.Utils = Utils;
exports.View = View;
exports.ZikoApp = ZikoApp;
exports.ZikoEvent = ZikoEvent;
exports.ZikoSPA = ZikoSPA;
exports.ZikoUIFlex = ZikoUIFlex;
exports.ZikoUISuspense = ZikoUISuspense;
exports.ZikoUIText = ZikoUIText;
exports.abs = abs;
exports.accum_prod = accum_prod;
exports.accum_sum = accum_sum;
exports.acos = acos$1;
exports.acosh = acosh;
exports.acot = acot;
exports.add = add;
exports.and = and;
exports.animation = animation;
exports.apply_fun = apply_fun;
exports.arange = arange;
exports.arc = arc;
exports.arr2str = arr2str;
exports.asin = asin;
exports.asinh = asinh;
exports.atan = atan;
exports.atan2 = atan2;
exports.atanh = atanh;
exports.back = back;
exports.bind_click_event = bind_click_event;
exports.bind_clipboard_event = bind_clipboard_event;
exports.bind_drag_event = bind_drag_event;
exports.bind_focus_event = bind_focus_event;
exports.bind_key_event = bind_key_event;
exports.bind_mouse_event = bind_mouse_event;
exports.bind_pointer_event = bind_pointer_event;
exports.bind_swipe_event = bind_swipe_event;
exports.bind_touch_event = bind_touch_event;
exports.bind_view_event = bind_view_event;
exports.bind_wheel_event = bind_wheel_event;
exports.binomial = binomial;
exports.cartesianProduct = cartesianProduct;
exports.cbrt = cbrt;
exports.ceil = ceil;
exports.clamp = clamp;
exports.clock = clock;
exports.complex = complex;
exports.cos = cos$3;
exports.cosh = cosh$2;
exports.coth = coth;
exports.croot = croot;
exports.csv2arr = csv2arr;
exports.csv2json = csv2json;
exports.csv2matrix = csv2matrix;
exports.csv2object = csv2object;
exports.csv2sql = csv2sql;
exports.debounce = debounce;
exports.defineParamsGetter = defineParamsGetter;
exports.define_wc = define_wc;
exports.deg2rad = deg2rad;
exports.discret = discret;
exports.div = div;
exports.elastic = elastic;
exports.event_controller = event_controller;
exports.exp = exp$1;
exports.floor = floor;
exports.fract = fract;
exports.geomspace = geomspace;
exports.getEvent = getEvent;
exports.hypot = hypot;
exports.inRange = inRange;
exports.in_back = in_back;
exports.in_bounce = in_bounce;
exports.in_circ = in_circ;
exports.in_cubic = in_cubic;
exports.in_elastic = in_elastic;
exports.in_expo = in_expo;
exports.in_out_back = in_out_back;
exports.in_out_bounce = in_out_bounce;
exports.in_out_circ = in_out_circ;
exports.in_out_cubic = in_out_cubic;
exports.in_out_elastic = in_out_elastic;
exports.in_out_expo = in_out_expo;
exports.in_out_quad = in_out_quad;
exports.in_out_quart = in_out_quart;
exports.in_out_quint = in_out_quint;
exports.in_out_sin = in_out_sin;
exports.in_quad = in_quad;
exports.in_quart = in_quart;
exports.in_quint = in_quint;
exports.in_sin = in_sin;
exports.isApproximatlyEqual = isApproximatlyEqual;
exports.isStateGetter = isStateGetter;
exports.json2arr = json2arr;
exports.json2css = json2css;
exports.json2csv = json2csv;
exports.json2csvFile = json2csvFile;
exports.json2xml = json2xml;
exports.json2xmlFile = json2xmlFile;
exports.json2yml = json2yml;
exports.json2ymlFile = json2ymlFile;
exports.lerp = lerp;
exports.linear = linear;
exports.linspace = linspace;
exports.ln = ln;
exports.logspace = logspace;
exports.loop = loop;
exports.map = map$1;
exports.mapfun = mapfun;
exports.matrix = matrix;
exports.matrix2 = matrix2;
exports.matrix3 = matrix3;
exports.matrix4 = matrix4;
exports.max = max;
exports.mean = mean;
exports.median = median;
exports.min = min;
exports.modulo = modulo;
exports.mul = mul;
exports.nand = nand;
exports.nor = nor;
exports.norm = norm;
exports.nthr = nthr;
exports.nums = nums;
exports.obj2str = obj2str;
exports.ones = ones;
exports.or = or;
exports.out_back = out_back;
exports.out_bounce = out_bounce;
exports.out_circ = out_circ;
exports.out_cubic = out_cubic;
exports.out_elastic = out_elastic;
exports.out_expo = out_expo;
exports.out_quad = out_quad;
exports.out_quart = out_quart;
exports.out_quint = out_quint;
exports.out_sin = out_sin;
exports.percentile = percentile;
exports.pgcd = pgcd;
exports.pow = pow$1;
exports.ppcm = ppcm;
exports.preload = preload;
exports.rad2deg = rad2deg;
exports.round = round;
exports.sec = sec;
exports.sig = sig;
exports.sign = sign;
exports.sin = sin$3;
exports.sinh = sinh$1;
exports.sleep = sleep;
exports.sqrt = sqrt$2;
exports.std = std;
exports.step = step;
exports.step_fps = step_fps;
exports.sub = sub;
exports.svg2ascii = svg2ascii;
exports.svg2img = svg2img;
exports.svg2imgUrl = svg2imgUrl;
exports.svg2str = svg2str;
exports.tags = tags;
exports.tan = tan;
exports.tanh = tanh;
exports.text = text;
exports.throttle = throttle;
exports.tick = tick;
exports.timeTaken = timeTaken;
exports.time_memory_Taken = time_memory_Taken;
exports.timeout = timeout;
exports.toggle_event_listener = toggle_event_listener;
exports.trunc = trunc;
exports.useDerived = useDerived;
exports.useEventEmitter = useEventEmitter;
exports.useIPC = useIPC;
exports.useLocaleStorage = useLocaleStorage;
exports.useMediaQuery = useMediaQuery;
exports.useReactive = useReactive;
exports.useRoot = useRoot;
exports.useSessionStorage = useSessionStorage;
exports.useState = useState;
exports.useThread = useThread;
exports.useTitle = useTitle;
exports.variance = variance;
exports.wait = wait;
exports.waitForUIElm = waitForUIElm;
exports.waitForUIElmSync = waitForUIElmSync;
exports.xnor = xnor;
exports.xor = xor;
exports.zeros = zeros;