add num
This commit is contained in:
2
Makefile
2
Makefile
@@ -1,5 +1,5 @@
|
||||
debug: FORCE
|
||||
meson setup build_dbg -Dbuildtype=debug
|
||||
meson setup build_dbg -Dbuildtype=debugoptimized
|
||||
meson install --only-changed -C build_dbg
|
||||
|
||||
fast: FORCE
|
||||
|
||||
13
meson.build
13
meson.build
@@ -115,11 +115,20 @@ endif
|
||||
if host_machine.system() == 'linux'
|
||||
deps += cc.find_library('asound', required:true)
|
||||
deps += [dependency('x11'), dependency('xi'), dependency('xcursor'), dependency('egl'), dependency('gl')]
|
||||
deps += cc.find_library('blas', required:true)
|
||||
deps += cc.find_library('lapack', required:true)
|
||||
endif
|
||||
|
||||
if host_machine.system() == 'windows'
|
||||
deps += cc.find_library('d3d11')
|
||||
deps += cc.find_library('ws2_32', required:true)
|
||||
# For Windows, you may need to install OpenBLAS or Intel MKL
|
||||
# and adjust these library names accordingly
|
||||
deps += cc.find_library('openblas', required:false)
|
||||
if not cc.find_library('openblas', required:false).found()
|
||||
deps += cc.find_library('blas', required:false)
|
||||
deps += cc.find_library('lapack', required:false)
|
||||
endif
|
||||
deps += cc.find_library('dbghelp')
|
||||
deps += cc.find_library('winmm')
|
||||
deps += cc.find_library('setupapi')
|
||||
@@ -225,7 +234,7 @@ if host_machine.system() != 'emscripten'
|
||||
endif
|
||||
src += 'qjs_enet.c'
|
||||
|
||||
tracy_opts = ['fibers=true', 'no_exit=true', 'libunwind_backtrace=true']
|
||||
tracy_opts = ['fibers=true', 'no_exit=true', 'libunwind_backtrace=true', 'on_demand=true']
|
||||
add_project_arguments('-DTRACY_ENABLE', language:['c','cpp'])
|
||||
|
||||
# Try to find system-installed tracy first
|
||||
@@ -295,7 +304,7 @@ src += [
|
||||
'anim.c', 'config.c', 'datastream.c','font.c','HandmadeMath.c','jsffi.c','model.c',
|
||||
'render.c','simplex.c','spline.c', 'transform.c','cell.c', 'wildmatch.c',
|
||||
'sprite.c', 'rtree.c', 'qjs_nota.c', 'qjs_soloud.c', 'qjs_sdl.c', 'qjs_sdl_input.c', 'qjs_sdl_video.c', 'qjs_sdl_surface.c', 'qjs_math.c', 'qjs_geometry.c', 'qjs_transform.c', 'qjs_sprite.c', 'qjs_io.c', 'qjs_fd.c', 'qjs_os.c', 'qjs_actor.c',
|
||||
'qjs_qr.c', 'qjs_wota.c', 'monocypher.c', 'qjs_blob.c', 'qjs_crypto.c', 'qjs_time.c', 'qjs_http.c', 'qjs_rtree.c', 'qjs_spline.c', 'qjs_js.c', 'qjs_debug.c', 'picohttpparser.c', 'qjs_miniz.c', 'timer.c', 'qjs_socket.c', 'qjs_kim.c', 'qjs_utf8.c', 'qjs_fit.c', 'qjs_text.c'
|
||||
'qjs_qr.c', 'qjs_wota.c', 'monocypher.c', 'qjs_blob.c', 'qjs_crypto.c', 'qjs_time.c', 'qjs_http.c', 'qjs_rtree.c', 'qjs_spline.c', 'qjs_js.c', 'qjs_debug.c', 'picohttpparser.c', 'qjs_miniz.c', 'qjs_num.c', 'timer.c', 'qjs_socket.c', 'qjs_kim.c', 'qjs_utf8.c', 'qjs_fit.c', 'qjs_text.c'
|
||||
]
|
||||
# quirc src
|
||||
src += [
|
||||
|
||||
@@ -8,33 +8,6 @@ These are pure functions that return plain JavaScript objects representing
|
||||
drawing operations. No rendering or actor communication happens here.
|
||||
`
|
||||
|
||||
// Create a new command list
|
||||
draw.list = function() {
|
||||
var commands = []
|
||||
|
||||
return {
|
||||
// Add a command to this list
|
||||
push: function(cmd) {
|
||||
commands.push(cmd)
|
||||
},
|
||||
|
||||
// Get all commands
|
||||
get: function() {
|
||||
return commands
|
||||
},
|
||||
|
||||
// Clear all commands
|
||||
clear: function() {
|
||||
commands = []
|
||||
},
|
||||
|
||||
// Get command count
|
||||
length: function() {
|
||||
return commands.length
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Default command list for convenience
|
||||
var current_list = draw.list()
|
||||
|
||||
@@ -50,18 +23,17 @@ draw.get_list = function() {
|
||||
|
||||
// Clear current list
|
||||
draw.clear = function() {
|
||||
current_list.clear()
|
||||
current_list.length = 0
|
||||
}
|
||||
|
||||
// Get commands from current list
|
||||
draw.get_commands = function() {
|
||||
return current_list.get()
|
||||
return current_list
|
||||
}
|
||||
|
||||
// Helper to add a command
|
||||
function add_command(type, data) {
|
||||
var cmd = {cmd: type}
|
||||
Object.assign(cmd, data)
|
||||
data.cmd = type
|
||||
current_list.push(cmd)
|
||||
}
|
||||
|
||||
@@ -190,16 +162,14 @@ draw.image = function image(image, rect, rotation = 0, anchor = [0,0], shear = [
|
||||
|
||||
if (!('x' in rect && 'y' in rect)) throw Error('Must provide X and Y for image.')
|
||||
|
||||
info = Object.assign({}, image_info, info);
|
||||
|
||||
add_command("draw_image", {
|
||||
image: image,
|
||||
rect: rect,
|
||||
rotation: rotation,
|
||||
anchor: anchor,
|
||||
shear: shear,
|
||||
info: info,
|
||||
material: material
|
||||
image,
|
||||
rect,
|
||||
rotation,
|
||||
anchor,
|
||||
shear,
|
||||
info,
|
||||
material,
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@@ -27,14 +27,13 @@ graphics.Image = function(surfaceData) {
|
||||
this[GPU] = undefined;
|
||||
this[LOADING] = false;
|
||||
this[LASTUSE] = time.number();
|
||||
this.rect = {x:0, y:0, width:1, height:1};
|
||||
this.rect = {x:0, y:0, width:surfaceData.width, height:surfaceData.height};
|
||||
}
|
||||
|
||||
// Define getters and methods on the prototype
|
||||
Object.defineProperties(graphics.Image.prototype, {
|
||||
gpu: {
|
||||
get: function() {
|
||||
this[LASTUSE] = time.number();
|
||||
if (!this[GPU] && !this[LOADING]) {
|
||||
this[LOADING] = true;
|
||||
var self = this;
|
||||
@@ -67,9 +66,6 @@ Object.defineProperties(graphics.Image.prototype, {
|
||||
|
||||
cpu: {
|
||||
get: function() {
|
||||
this[LASTUSE] = time.number();
|
||||
// Note: Reading texture back from GPU requires async operation
|
||||
// For now, return the CPU data if available
|
||||
return this[CPU]
|
||||
}
|
||||
},
|
||||
@@ -253,7 +249,7 @@ graphics.texture = function texture(path) {
|
||||
if (typeof path !== 'string')
|
||||
throw new Error('need a string for graphics.texture')
|
||||
|
||||
var id = path.split(':')[0]
|
||||
var id = path //.split(':')[0]
|
||||
if (cache.has(id)) return cache.get(id)
|
||||
|
||||
var ipath = res.find_image(id)
|
||||
|
||||
@@ -3,11 +3,21 @@ var io = use('io');
|
||||
var transform = use('transform');
|
||||
var rasterize = use('rasterize');
|
||||
var time = use('time')
|
||||
var num = use('num');
|
||||
|
||||
// Frame timing variables
|
||||
var frame_times = []
|
||||
var frame_time_index = 0
|
||||
var max_frame_samples = 60
|
||||
var frame_start_time = 0
|
||||
var average_frame_time = 0
|
||||
|
||||
var game = args[0]
|
||||
|
||||
var video
|
||||
|
||||
var cnf = use('accio/config')
|
||||
|
||||
$_.start(e => {
|
||||
if (e.type !== 'greet') return
|
||||
video = e.actor
|
||||
@@ -17,73 +27,83 @@ $_.start(e => {
|
||||
if (gameactor) return
|
||||
gameactor = e.actor
|
||||
$_.couple(gameactor)
|
||||
loop()
|
||||
}, args[0])
|
||||
start_pipeline()
|
||||
}, args[0], $_)
|
||||
})
|
||||
}, 'prosperon/sdl_video', {
|
||||
title: "Prosperon",
|
||||
width:500,
|
||||
height:500
|
||||
})
|
||||
}, 'prosperon/sdl_video', cnf)
|
||||
|
||||
log.console('starting...')
|
||||
|
||||
var input = use('input')
|
||||
|
||||
var geometry = use('geometry')
|
||||
|
||||
function worldToScreenRect({x,y,width,height}, camera, winW, winH) {
|
||||
var bl = worldToScreenPoint([x,y], camera, winW, winH)
|
||||
var tr = worldToScreenPoint([x+width, y+height], camera, winW, winH)
|
||||
|
||||
function updateCameraMatrix(camera, winW, winH) {
|
||||
// world→NDC
|
||||
const sx = 1 / camera.size[0];
|
||||
const sy = 1 / camera.size[1];
|
||||
const ox = camera.pos[0] - camera.size[0] * camera.anchor[0];
|
||||
const oy = camera.pos[1] - camera.size[1] * camera.anchor[1];
|
||||
|
||||
// NDC→pixels
|
||||
const vx = camera.viewport.x * winW;
|
||||
const vy = camera.viewport.y * winH;
|
||||
const vw = camera.viewport.width * winW;
|
||||
const vh = camera.viewport.height * winH;
|
||||
|
||||
// final “mat” coefficients
|
||||
// [ a 0 c ]
|
||||
// [ 0 e f ]
|
||||
// [ 0 0 1 ]
|
||||
camera.a = sx * vw;
|
||||
camera.c = vx - camera.a * ox;
|
||||
camera.e = -sy * vh;
|
||||
camera.f = vy + vh + sy * vh * oy;
|
||||
|
||||
// and store the inverses so we can go back cheaply
|
||||
camera.ia = 1 / camera.a;
|
||||
camera.ic = -camera.c * camera.ia;
|
||||
camera.ie = 1 / camera.e;
|
||||
camera.if = -camera.f * camera.ie;
|
||||
}
|
||||
|
||||
//---- forward transform ----
|
||||
function worldToScreenPoint(pos, camera) {
|
||||
return {
|
||||
x: Math.min(bl.x, tr.x),
|
||||
y: Math.min(bl.y, tr.y),
|
||||
width: Math.abs(tr.x - bl.x),
|
||||
height: Math.abs(tr.y - bl.y)
|
||||
}
|
||||
x: camera.a * pos[0] + camera.c,
|
||||
y: camera.e * pos[1] + camera.f
|
||||
};
|
||||
}
|
||||
|
||||
function worldToScreenPoint([wx, wy], camera, winW, winH) {
|
||||
// 1) world‐window origin (bottom‐left)
|
||||
const worldX0 = camera.pos[0] - camera.size[0] * camera.anchor[0];
|
||||
const worldY0 = camera.pos[1] - camera.size[1] * camera.anchor[1];
|
||||
|
||||
// 2) normalized device coords [0..1]
|
||||
const ndcX = (wx - worldX0) / camera.size[0];
|
||||
const ndcY = (wy - worldY0) / camera.size[1];
|
||||
|
||||
// 3) map into pixel‐space via the fractional viewport
|
||||
const px = camera.viewport.x * winW
|
||||
+ ndcX * (camera.viewport.width * winW);
|
||||
const py = camera.viewport.y * winH
|
||||
+ (1 - ndcY) * (camera.viewport.height * winH);
|
||||
|
||||
return [ px, py ];
|
||||
//---- inverse transform ----
|
||||
function screenToWorldPoint(pos, camera) {
|
||||
return {
|
||||
x: camera.ia * pos[0] + camera.ic,
|
||||
y: camera.ie * pos[1] + camera.if
|
||||
};
|
||||
}
|
||||
|
||||
function screenToWorldPoint([px, py], camera, winW, winH) {
|
||||
// 1) undo pixel→NDC within the camera’s viewport
|
||||
const ndcX = (px - camera.viewport.x * winW)
|
||||
/ (camera.viewport.width * winW)
|
||||
const ndcY = 1 - (py - camera.viewport.y * winH)
|
||||
/ (camera.viewport.height * winH)
|
||||
//---- rectangle (two corner) ----
|
||||
function worldToScreenRect(rect, camera) {
|
||||
// map bottom-left and top-right
|
||||
const x1 = camera.a * rect.x + camera.c;
|
||||
const y1 = camera.e * rect.y + camera.f;
|
||||
const x2 = camera.a * (rect.x + rect.width) + camera.c;
|
||||
const y2 = camera.e * (rect.y + rect.height) + camera.f;
|
||||
|
||||
// 2) compute the world‐window origin (bottom‐left)
|
||||
const worldX0 = camera.pos[0]
|
||||
- camera.size[0] * camera.anchor[0]
|
||||
const worldY0 = camera.pos[1]
|
||||
- camera.size[1] * camera.anchor[1]
|
||||
|
||||
// 3) map NDC back to world coords
|
||||
return [
|
||||
ndcX * camera.size[0] + worldX0,
|
||||
ndcY * camera.size[1] + worldY0
|
||||
]
|
||||
// pick mins and abs deltas
|
||||
const x0 = x1 < x2 ? x1 : x2;
|
||||
const y0 = y1 < y2 ? y1 : y2;
|
||||
return {
|
||||
x: x0,
|
||||
y: y0,
|
||||
width: x2 > x1 ? x2 - x1 : x1 - x2,
|
||||
height: y2 > y1 ? y2 - y1 : y1 - y2
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
var camera = {
|
||||
size: [500,500],//{width:500,height:500}, // pixel size the camera "sees", like its resolution
|
||||
size: [640,320],//{width:500,height:500}, // pixel size the camera "sees", like its resolution
|
||||
pos: [250,250],//{x:0,y:0}, // where it is
|
||||
fov:50,
|
||||
near_z:0,
|
||||
@@ -104,10 +124,15 @@ var gameactor
|
||||
|
||||
var images = {}
|
||||
|
||||
var renderer_commands = []
|
||||
|
||||
// Convert high-level draw commands to low-level renderer commands
|
||||
function translate_draw_commands(commands) {
|
||||
if (!graphics) return
|
||||
var renderer_commands = []
|
||||
|
||||
updateCameraMatrix(camera,500,500)
|
||||
|
||||
renderer_commands.length = 0
|
||||
|
||||
commands.forEach(function(cmd) {
|
||||
if (cmd.material && cmd.material.color) {
|
||||
@@ -201,7 +226,8 @@ function translate_draw_commands(commands) {
|
||||
|
||||
case "draw_image":
|
||||
var img = graphics.texture(cmd.image)
|
||||
if (!img.gpu) break
|
||||
var gpu = img.gpu
|
||||
if (!gpu) break
|
||||
|
||||
cmd.rect.width ??= img.width
|
||||
cmd.rect.height ??= img.height
|
||||
@@ -210,9 +236,9 @@ function translate_draw_commands(commands) {
|
||||
renderer_commands.push({
|
||||
op: "texture",
|
||||
data: {
|
||||
texture_id: img.gpu.id,
|
||||
texture_id: gpu.id,
|
||||
dst: cmd.rect,
|
||||
src: {x:0,y:0,width:img.width,height:img.height},
|
||||
src: img.rect
|
||||
}
|
||||
})
|
||||
break
|
||||
@@ -236,60 +262,128 @@ function translate_draw_commands(commands) {
|
||||
return renderer_commands
|
||||
}
|
||||
|
||||
function loop(time)
|
||||
{
|
||||
send(video, {kind:'input', op:'get'}, e => {
|
||||
for (var event of e) {
|
||||
if (event.type === 'quit')
|
||||
$_.stop()
|
||||
}
|
||||
})
|
||||
|
||||
send(gameactor, {kind:'update', dt:1/60}, e => {
|
||||
send(gameactor, {kind:'draw'}, draw_commands => {
|
||||
var batch_commands = []
|
||||
|
||||
batch_commands.push({
|
||||
op: "set",
|
||||
prop: "drawColor",
|
||||
value: [0.1,0.1,0.15,1]
|
||||
})
|
||||
|
||||
// Clear the screen
|
||||
batch_commands.push({
|
||||
op: "clear"
|
||||
})
|
||||
|
||||
if (draw_commands && draw_commands.length > 0) {
|
||||
var renderer_commands = translate_draw_commands(draw_commands)
|
||||
batch_commands = batch_commands.concat(renderer_commands)
|
||||
}
|
||||
|
||||
batch_commands.push({
|
||||
op: "present"
|
||||
})
|
||||
|
||||
send(video, {
|
||||
kind: "renderer",
|
||||
op: "batch",
|
||||
data: batch_commands
|
||||
}, _ => {
|
||||
})
|
||||
var parseq = use('parseq', $_.delay)
|
||||
|
||||
// Wrap `send(actor,msg,cb)` into a parseq “requestor”
|
||||
// • on success: cb(data) → value=data, reason=undefined
|
||||
// • on failure: cb(undefined,err)
|
||||
function rpc_req(actor, msg) {
|
||||
return (cb, _) => {
|
||||
send(actor, msg, data => {
|
||||
if (data.error)
|
||||
cb(undefined, data)
|
||||
else
|
||||
cb(data)
|
||||
})
|
||||
})
|
||||
|
||||
$_.delay(loop, 1/30)
|
||||
}
|
||||
}
|
||||
|
||||
var game_rec = parseq.sequence([
|
||||
rpc_req(gameactor, {kind:'update', dt:1/60}),
|
||||
rpc_req(gameactor, {kind:'draw'})
|
||||
])
|
||||
|
||||
var pending_draw = null
|
||||
var pending_next = null
|
||||
var last_time = time.number()
|
||||
var frames = []
|
||||
var frame_avg = 0
|
||||
|
||||
// 1) input runs completely independently
|
||||
function poll_input() {
|
||||
send(video, {kind:'input', op:'get'}, evs => {
|
||||
for (let ev of evs) if (ev.type === 'quit') $_.stop()
|
||||
})
|
||||
$_.delay(poll_input, 1/60)
|
||||
}
|
||||
|
||||
// 2) helper to build & send a batch, then call done()
|
||||
function create_batch(draw_cmds, done) {
|
||||
const batch = [
|
||||
{op:'set', prop:'drawColor', value:[0.1,0.1,0.15,1]},
|
||||
{op:'clear'}
|
||||
]
|
||||
if (draw_cmds && draw_cmds.length)
|
||||
batch.push(...translate_draw_commands(draw_cmds))
|
||||
|
||||
batch.push(
|
||||
{op:'debugText', data:{pos:{x:10,y:10}, text:`Frame: ${frame_avg.toFixed(2)}ms`}},
|
||||
{op:'present'}
|
||||
)
|
||||
|
||||
send(video, {kind:'renderer', op:'batch', data:batch}, () => {
|
||||
// update FPS
|
||||
const now = time.number()
|
||||
const dt = now - last_time
|
||||
last_time = now
|
||||
|
||||
frames.push(dt)
|
||||
if (frames.length > 60) frames.shift()
|
||||
let sum = 0
|
||||
for (let f of frames) sum += f
|
||||
frame_avg = sum / frames.length * 1000
|
||||
|
||||
done(dt)
|
||||
})
|
||||
}
|
||||
|
||||
// 3) kick off the very first update→draw
|
||||
function start_pipeline() {
|
||||
poll_input()
|
||||
send(gameactor, {kind:'update', dt:1/60}, () => {
|
||||
send(gameactor, {kind:'draw'}, cmds => {
|
||||
pending_draw = cmds
|
||||
render_step()
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
function render_step() {
|
||||
// a) fire off the next update→draw immediately
|
||||
const dt = time.number() - last_time
|
||||
send(gameactor, {kind:'update', dt}, () =>
|
||||
send(gameactor, {kind:'draw'}, cmds => pending_next = cmds)
|
||||
)
|
||||
|
||||
// c) render the current frame
|
||||
create_batch(pending_draw, ttr => { // time to render
|
||||
// only swap in when there's a new set of commands
|
||||
if (pending_next) {
|
||||
pending_draw = pending_next
|
||||
pending_next = null
|
||||
}
|
||||
|
||||
// d) schedule the next render step
|
||||
const render_dur = time.number() - last_time
|
||||
const wait = Math.max(0, 1/60 - ttr)
|
||||
$_.delay(render_step, 0)
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
$_.receiver(e => {
|
||||
if (e.type === 'quit')
|
||||
$_.stop()
|
||||
|
||||
if (e.type.includes('mouse')) {
|
||||
if (e.pos)
|
||||
e.pos = screenToWorldPoint(e.pos, camera, 500, 500)
|
||||
if (e.type) {
|
||||
if (e.type === 'quit')
|
||||
$_.stop()
|
||||
|
||||
if (e.d_pos)
|
||||
e.d_pos.y *= -1
|
||||
if (e.type.includes('mouse')) {
|
||||
if (e.pos)
|
||||
e.pos = screenToWorldPoint(e.pos, camera, 500, 500)
|
||||
|
||||
if (e.d_pos)
|
||||
e.d_pos.y *= -1
|
||||
}
|
||||
}
|
||||
|
||||
switch(e.op) {
|
||||
case 'resolution':
|
||||
log.console(json.encode(e))
|
||||
send(video, {
|
||||
kind:'renderer',
|
||||
op:'set',
|
||||
prop:'logicalPresentation',
|
||||
value: {...e}
|
||||
})
|
||||
break
|
||||
}
|
||||
})
|
||||
|
||||
@@ -82,6 +82,10 @@ var use_dyn = hidden.use_dyn
|
||||
var enet = hidden.enet
|
||||
var nota = hidden.nota
|
||||
|
||||
// Wota decode timing tracking
|
||||
var wota_decode_times = []
|
||||
var last_wota_flush = 0
|
||||
|
||||
// Strip hidden from cell so nothing else can access it
|
||||
delete cell.hidden
|
||||
|
||||
@@ -831,4 +835,6 @@ $_.clock(_ => {
|
||||
throw new Error('Program must not return anything');
|
||||
})
|
||||
|
||||
log.console(`startup took ${time.number()-st_now}`)
|
||||
|
||||
})()
|
||||
@@ -515,8 +515,11 @@ void actor_turn(cell_rt *actor)
|
||||
SDL_LockMutex(actor->mutex);
|
||||
|
||||
#ifdef TRACY_ENABLE
|
||||
if (tracy_profiling_enabled)
|
||||
int entered = 0;
|
||||
if (tracy_profiling_enabled && TracyCIsConnected) {
|
||||
TracyCFiberEnter(actor->name);
|
||||
entered = 1;
|
||||
}
|
||||
#endif
|
||||
|
||||
actor->state = ACTOR_RUNNING;
|
||||
@@ -560,7 +563,7 @@ void actor_turn(cell_rt *actor)
|
||||
actor->state = ACTOR_IDLE;
|
||||
|
||||
#ifdef TRACY_ENABLE
|
||||
if (tracy_profiling_enabled)
|
||||
if (tracy_profiling_enabled && entered)
|
||||
TracyCFiberLeave(actor->name);
|
||||
#endif
|
||||
|
||||
|
||||
@@ -1532,6 +1532,7 @@ JS_SetPrototype(js, js_##NAME, PARENT); \
|
||||
|
||||
JSValue js_layout_use(JSContext *js);
|
||||
JSValue js_miniz_use(JSContext *js);
|
||||
JSValue js_num_use(JSContext *js);
|
||||
|
||||
JSValue js_graphics_use(JSContext *js) {
|
||||
JSValue mod = JS_NewObject(js);
|
||||
@@ -1578,7 +1579,9 @@ void ffi_load(JSContext *js)
|
||||
|
||||
JS_FreeValue(js, js_blob_use(js)); // juice blob
|
||||
|
||||
m_seedRand(&rt->mrand, time(NULL));
|
||||
uint64_t rr;
|
||||
randombytes(&rr,4);
|
||||
m_seedRand(&rt->mrand, rr);
|
||||
|
||||
// cell modules
|
||||
arrput(rt->module_registry, MISTLINE(time));
|
||||
@@ -1594,6 +1597,7 @@ void ffi_load(JSContext *js)
|
||||
arrput(rt->module_registry, MISTLINE(http));
|
||||
arrput(rt->module_registry, MISTLINE(crypto));
|
||||
arrput(rt->module_registry, MISTLINE(miniz));
|
||||
arrput(rt->module_registry, MISTLINE(num));
|
||||
arrput(rt->module_registry, MISTLINE(kim));
|
||||
arrput(rt->module_registry, MISTLINE(utf8));
|
||||
arrput(rt->module_registry, MISTLINE(fit));
|
||||
|
||||
@@ -546,22 +546,3 @@ JSValue js_enet_use(JSContext *ctx)
|
||||
JS_SetPropertyFunctionList(ctx, export_obj, js_enet_funcs, countof(js_enet_funcs));
|
||||
return export_obj;
|
||||
}
|
||||
|
||||
static int js_enet_init(JSContext *ctx, JSModuleDef *m)
|
||||
{
|
||||
return JS_SetModuleExport(ctx, m, "default", js_enet_use(ctx));
|
||||
}
|
||||
|
||||
#ifdef JS_SHARED_LIBRARY
|
||||
#define JS_INIT_MODULE js_init_module
|
||||
#else
|
||||
#define JS_INIT_MODULE js_init_module_enet
|
||||
#endif
|
||||
|
||||
JSModuleDef *JS_INIT_MODULE(JSContext *ctx, const char *module_name)
|
||||
{
|
||||
JSModuleDef *m = JS_NewCModule(ctx, module_name, js_enet_init);
|
||||
if (!m) return NULL;
|
||||
JS_AddModuleExport(ctx, m, "default");
|
||||
return m;
|
||||
}
|
||||
|
||||
@@ -882,23 +882,3 @@ JSValue js_imgui_use(JSContext *js)
|
||||
return imgui;
|
||||
}
|
||||
}
|
||||
|
||||
static int js_init_imgui(JSContext *js, JSModuleDef *m) {
|
||||
JS_SetModuleExportList(js, m, js_imgui_funcs, sizeof(js_imgui_funcs)/sizeof(JSCFunctionListEntry));
|
||||
JS_SetModuleExport(js, m, "default", js_imgui_use(js));
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef JS_SHARED_LIBRARY
|
||||
#define JS_INIT_MODULE js_init_module
|
||||
#else
|
||||
#define JS_INIT_MODULE js_init_module_imgui
|
||||
#endif
|
||||
|
||||
JSModuleDef *JS_INIT_MODULE(JSContext *js, const char *name) {
|
||||
JSModuleDef *m;
|
||||
m = JS_NewCModule(js, name, js_init_imgui);
|
||||
if (!m) return NULL;
|
||||
JS_AddModuleExport(js, m, "default");
|
||||
return m;
|
||||
}
|
||||
|
||||
@@ -30,12 +30,12 @@ static JSClassDef js_writer_class = {
|
||||
|
||||
static mz_zip_archive *js2reader(JSContext *js, JSValue v)
|
||||
{
|
||||
return JS_GetOpaque2(js, v, js_reader_class_id);
|
||||
return JS_GetOpaque(v, js_reader_class_id);
|
||||
}
|
||||
|
||||
static mz_zip_archive *js2writer(JSContext *js, JSValue v)
|
||||
{
|
||||
return JS_GetOpaque2(js, v, js_writer_class_id);
|
||||
return JS_GetOpaque(v, js_writer_class_id);
|
||||
}
|
||||
|
||||
static JSValue js_miniz_read(JSContext *js, JSValue self, int argc, JSValue *argv)
|
||||
@@ -397,21 +397,3 @@ JSValue js_miniz_use(JSContext *js)
|
||||
JS_SetPropertyFunctionList(js, export, js_miniz_funcs, sizeof(js_miniz_funcs)/sizeof(JSCFunctionListEntry));
|
||||
return export;
|
||||
}
|
||||
|
||||
static int js_miniz_init(JSContext *ctx, JSModuleDef *m) {
|
||||
JS_SetModuleExport(ctx, m, "default",js_miniz_use(ctx));
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef JS_SHARED_LIBRARY
|
||||
#define JS_INIT_MODULE js_init_module
|
||||
#else
|
||||
#define JS_INIT_MODULE js_init_module_miniz
|
||||
#endif
|
||||
|
||||
JSModuleDef *JS_INIT_MODULE(JSContext *ctx, const char *module_name) {
|
||||
JSModuleDef *m = JS_NewCModule(ctx, module_name, js_miniz_init);
|
||||
if (!m) return NULL;
|
||||
JS_AddModuleExport(ctx, m, "default");
|
||||
return m;
|
||||
}
|
||||
|
||||
@@ -349,26 +349,8 @@ static const JSCFunctionListEntry js_nota_funcs[] = {
|
||||
JS_CFUNC_DEF("decode", 1, js_nota_decode),
|
||||
};
|
||||
|
||||
static int js_nota_init(JSContext *ctx, JSModuleDef *m) {
|
||||
JS_SetModuleExportList(ctx, m, js_nota_funcs, sizeof(js_nota_funcs)/sizeof(JSCFunctionListEntry));
|
||||
return 0;
|
||||
}
|
||||
|
||||
JSValue js_nota_use(JSContext *js) {
|
||||
JSValue export = JS_NewObject(js);
|
||||
JS_SetPropertyFunctionList(js, export, js_nota_funcs, sizeof(js_nota_funcs)/sizeof(JSCFunctionListEntry));
|
||||
return export;
|
||||
}
|
||||
|
||||
#ifdef JS_SHARED_LIBRARY
|
||||
#define JS_INIT_MODULE js_init_module
|
||||
#else
|
||||
#define JS_INIT_MODULE js_init_module_nota
|
||||
#endif
|
||||
|
||||
JSModuleDef *JS_INIT_MODULE(JSContext *ctx, const char *module_name) {
|
||||
JSModuleDef *m = JS_NewCModule(ctx, module_name, js_nota_init);
|
||||
if (!m) return NULL;
|
||||
JS_AddModuleExportList(ctx, m, js_nota_funcs, sizeof(js_nota_funcs)/sizeof(JSCFunctionListEntry));
|
||||
return m;
|
||||
}
|
||||
|
||||
607
source/qjs_num.c
Normal file
607
source/qjs_num.c
Normal file
@@ -0,0 +1,607 @@
|
||||
#include "quickjs.h"
|
||||
#include <string.h>
|
||||
#include <math.h>
|
||||
|
||||
#ifdef __APPLE__
|
||||
#include <Accelerate/Accelerate.h>
|
||||
#else
|
||||
#include <cblas.h>
|
||||
#include <lapacke.h>
|
||||
#endif
|
||||
|
||||
static JSClassID js_matrix_class_id;
|
||||
static JSClassID js_array_class_id;
|
||||
|
||||
typedef struct {
|
||||
double *data;
|
||||
int rows;
|
||||
int cols;
|
||||
} matrix_t;
|
||||
|
||||
typedef struct {
|
||||
double *data;
|
||||
int size;
|
||||
} array_t;
|
||||
|
||||
static void js_matrix_finalizer(JSRuntime *rt, JSValue val) {
|
||||
matrix_t *mat = JS_GetOpaque(val, js_matrix_class_id);
|
||||
if (mat) {
|
||||
js_free_rt(rt, mat->data);
|
||||
js_free_rt(rt, mat);
|
||||
}
|
||||
}
|
||||
|
||||
static void js_array_finalizer(JSRuntime *rt, JSValue val) {
|
||||
array_t *arr = JS_GetOpaque(val, js_array_class_id);
|
||||
if (arr) {
|
||||
js_free_rt(rt, arr->data);
|
||||
js_free_rt(rt, arr);
|
||||
}
|
||||
}
|
||||
|
||||
static JSClassDef js_matrix_class = {
|
||||
"Matrix",
|
||||
.finalizer = js_matrix_finalizer,
|
||||
};
|
||||
|
||||
// Forward declaration for exotic methods
|
||||
static const JSClassExoticMethods js_array_exotic;
|
||||
|
||||
static JSClassDef js_array_class = {
|
||||
"Array",
|
||||
.finalizer = js_array_finalizer,
|
||||
.exotic = &js_array_exotic,
|
||||
};
|
||||
|
||||
static matrix_t *js2matrix(JSContext *ctx, JSValue v) {
|
||||
return JS_GetOpaque(v, js_matrix_class_id);
|
||||
}
|
||||
|
||||
static array_t *js2array(JSContext *ctx, JSValue v) {
|
||||
return JS_GetOpaque(v, js_array_class_id);
|
||||
}
|
||||
|
||||
static JSValue js_matrix_constructor(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv) {
|
||||
if (argc < 1)
|
||||
return JS_ThrowTypeError(ctx, "Matrix constructor requires an array argument");
|
||||
|
||||
if (!JS_IsArray(ctx, argv[0]))
|
||||
return JS_ThrowTypeError(ctx, "Matrix constructor requires an array argument");
|
||||
|
||||
JSValue length_val = JS_GetPropertyStr(ctx, argv[0], "length");
|
||||
int32_t rows;
|
||||
JS_ToInt32(ctx, &rows, length_val);
|
||||
JS_FreeValue(ctx, length_val);
|
||||
|
||||
if (rows == 0)
|
||||
return JS_ThrowRangeError(ctx, "Matrix cannot have 0 rows");
|
||||
|
||||
// Get first row to determine columns
|
||||
JSValue first_row = JS_GetPropertyUint32(ctx, argv[0], 0);
|
||||
if (!JS_IsArray(ctx, first_row)) {
|
||||
JS_FreeValue(ctx, first_row);
|
||||
return JS_ThrowTypeError(ctx, "Matrix rows must be arrays");
|
||||
}
|
||||
|
||||
JSValue col_length_val = JS_GetPropertyStr(ctx, first_row, "length");
|
||||
int32_t cols;
|
||||
JS_ToInt32(ctx, &cols, col_length_val);
|
||||
JS_FreeValue(ctx, col_length_val);
|
||||
JS_FreeValue(ctx, first_row);
|
||||
|
||||
if (cols == 0)
|
||||
return JS_ThrowRangeError(ctx, "Matrix cannot have 0 columns");
|
||||
|
||||
matrix_t *mat = js_malloc(ctx, sizeof(matrix_t));
|
||||
if (!mat)
|
||||
return JS_EXCEPTION;
|
||||
|
||||
mat->rows = rows;
|
||||
mat->cols = cols;
|
||||
mat->data = js_malloc(ctx, sizeof(double) * rows * cols);
|
||||
if (!mat->data) {
|
||||
js_free(ctx, mat);
|
||||
return JS_EXCEPTION;
|
||||
}
|
||||
|
||||
// Fill matrix data
|
||||
for (int i = 0; i < rows; i++) {
|
||||
JSValue row = JS_GetPropertyUint32(ctx, argv[0], i);
|
||||
if (!JS_IsArray(ctx, row)) {
|
||||
js_free(ctx, mat->data);
|
||||
js_free(ctx, mat);
|
||||
JS_FreeValue(ctx, row);
|
||||
return JS_ThrowTypeError(ctx, "All matrix rows must be arrays");
|
||||
}
|
||||
|
||||
JSValue row_length_val = JS_GetPropertyStr(ctx, row, "length");
|
||||
int32_t row_cols;
|
||||
JS_ToInt32(ctx, &row_cols, row_length_val);
|
||||
JS_FreeValue(ctx, row_length_val);
|
||||
|
||||
if (row_cols != cols) {
|
||||
js_free(ctx, mat->data);
|
||||
js_free(ctx, mat);
|
||||
JS_FreeValue(ctx, row);
|
||||
return JS_ThrowTypeError(ctx, "All matrix rows must have the same length");
|
||||
}
|
||||
|
||||
for (int j = 0; j < cols; j++) {
|
||||
JSValue elem = JS_GetPropertyUint32(ctx, row, j);
|
||||
double val;
|
||||
if (JS_ToFloat64(ctx, &val, elem)) {
|
||||
js_free(ctx, mat->data);
|
||||
js_free(ctx, mat);
|
||||
JS_FreeValue(ctx, elem);
|
||||
JS_FreeValue(ctx, row);
|
||||
return JS_EXCEPTION;
|
||||
}
|
||||
mat->data[i * cols + j] = val;
|
||||
JS_FreeValue(ctx, elem);
|
||||
}
|
||||
JS_FreeValue(ctx, row);
|
||||
}
|
||||
|
||||
JSValue obj = JS_NewObjectClass(ctx, js_matrix_class_id);
|
||||
JS_SetOpaque(obj, mat);
|
||||
return obj;
|
||||
}
|
||||
|
||||
static JSValue js_array_constructor(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv) {
|
||||
if (argc < 1)
|
||||
return JS_ThrowTypeError(ctx, "Array constructor requires an array argument");
|
||||
|
||||
if (!JS_IsArray(ctx, argv[0]))
|
||||
return JS_ThrowTypeError(ctx, "Array constructor requires an array argument");
|
||||
|
||||
JSValue length_val = JS_GetPropertyStr(ctx, argv[0], "length");
|
||||
int32_t size;
|
||||
JS_ToInt32(ctx, &size, length_val);
|
||||
JS_FreeValue(ctx, length_val);
|
||||
|
||||
if (size == 0)
|
||||
return JS_ThrowRangeError(ctx, "Array cannot have 0 elements");
|
||||
|
||||
array_t *arr = js_malloc(ctx, sizeof(array_t));
|
||||
if (!arr)
|
||||
return JS_EXCEPTION;
|
||||
|
||||
arr->size = size;
|
||||
arr->data = js_malloc(ctx, sizeof(double) * size);
|
||||
if (!arr->data) {
|
||||
js_free(ctx, arr);
|
||||
return JS_EXCEPTION;
|
||||
}
|
||||
|
||||
// Fill array data
|
||||
for (int i = 0; i < size; i++) {
|
||||
JSValue elem = JS_GetPropertyUint32(ctx, argv[0], i);
|
||||
double val;
|
||||
if (JS_ToFloat64(ctx, &val, elem)) {
|
||||
js_free(ctx, arr->data);
|
||||
js_free(ctx, arr);
|
||||
JS_FreeValue(ctx, elem);
|
||||
return JS_EXCEPTION;
|
||||
}
|
||||
arr->data[i] = val;
|
||||
JS_FreeValue(ctx, elem);
|
||||
}
|
||||
|
||||
JSValue obj = JS_NewObjectClass(ctx, js_array_class_id);
|
||||
JS_SetOpaque(obj, arr);
|
||||
return obj;
|
||||
}
|
||||
|
||||
static JSValue js_matrix_inverse(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv) {
|
||||
matrix_t *mat = js2matrix(ctx, this_val);
|
||||
if (!mat)
|
||||
return JS_EXCEPTION;
|
||||
|
||||
if (mat->rows != mat->cols)
|
||||
return JS_ThrowTypeError(ctx, "Matrix must be square for inversion");
|
||||
|
||||
int n = mat->rows;
|
||||
|
||||
// Create a copy of the matrix
|
||||
matrix_t *result = js_malloc(ctx, sizeof(matrix_t));
|
||||
if (!result)
|
||||
return JS_EXCEPTION;
|
||||
|
||||
result->rows = n;
|
||||
result->cols = n;
|
||||
result->data = js_malloc(ctx, sizeof(double) * n * n);
|
||||
if (!result->data) {
|
||||
js_free(ctx, result);
|
||||
return JS_EXCEPTION;
|
||||
}
|
||||
|
||||
memcpy(result->data, mat->data, sizeof(double) * n * n);
|
||||
|
||||
// Allocate pivot array
|
||||
int *ipiv = js_malloc(ctx, sizeof(int) * n);
|
||||
if (!ipiv) {
|
||||
js_free(ctx, result->data);
|
||||
js_free(ctx, result);
|
||||
return JS_EXCEPTION;
|
||||
}
|
||||
|
||||
// LU decomposition
|
||||
#ifdef __APPLE__
|
||||
// For macOS with ILP64, use 64-bit integers
|
||||
__LAPACK_int nn = n;
|
||||
__LAPACK_int info = 0;
|
||||
__LAPACK_int *ipiv_lapack = js_malloc(ctx, sizeof(__LAPACK_int) * n);
|
||||
if (!ipiv_lapack) {
|
||||
js_free(ctx, ipiv);
|
||||
js_free(ctx, result->data);
|
||||
js_free(ctx, result);
|
||||
return JS_EXCEPTION;
|
||||
}
|
||||
|
||||
// LAPACK uses column-major, but we'll transpose the result
|
||||
__LAPACK_int lda = n;
|
||||
dgetrf_(&nn, &nn, result->data, &lda, ipiv_lapack, &info);
|
||||
|
||||
if (info != 0) {
|
||||
js_free(ctx, ipiv_lapack);
|
||||
js_free(ctx, ipiv);
|
||||
js_free(ctx, result->data);
|
||||
js_free(ctx, result);
|
||||
return JS_ThrowInternalError(ctx, "Matrix LU decomposition failed");
|
||||
}
|
||||
|
||||
// Compute inverse
|
||||
__LAPACK_int lwork = n * n;
|
||||
double *work = js_malloc(ctx, sizeof(double) * lwork);
|
||||
if (!work) {
|
||||
js_free(ctx, ipiv_lapack);
|
||||
js_free(ctx, ipiv);
|
||||
js_free(ctx, result->data);
|
||||
js_free(ctx, result);
|
||||
return JS_EXCEPTION;
|
||||
}
|
||||
|
||||
dgetri_(&nn, result->data, &lda, ipiv_lapack, work, &lwork, &info);
|
||||
js_free(ctx, work);
|
||||
js_free(ctx, ipiv_lapack);
|
||||
#else
|
||||
int info = LAPACKE_dgetrf(LAPACK_ROW_MAJOR, n, n, result->data, n, ipiv);
|
||||
if (info != 0) {
|
||||
js_free(ctx, ipiv);
|
||||
js_free(ctx, result->data);
|
||||
js_free(ctx, result);
|
||||
return JS_ThrowInternalError(ctx, "Matrix LU decomposition failed");
|
||||
}
|
||||
|
||||
// Compute inverse
|
||||
info = LAPACKE_dgetri(LAPACK_ROW_MAJOR, n, result->data, n, ipiv);
|
||||
#endif
|
||||
js_free(ctx, ipiv);
|
||||
|
||||
if (info != 0) {
|
||||
js_free(ctx, result->data);
|
||||
js_free(ctx, result);
|
||||
return JS_ThrowInternalError(ctx, "Matrix inversion failed");
|
||||
}
|
||||
|
||||
JSValue obj = JS_NewObjectClass(ctx, js_matrix_class_id);
|
||||
JS_SetOpaque(obj, result);
|
||||
return obj;
|
||||
}
|
||||
|
||||
static JSValue js_matrix_multiply(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv) {
|
||||
if (argc < 1)
|
||||
return JS_ThrowTypeError(ctx, "multiply requires an argument");
|
||||
|
||||
matrix_t *A = js2matrix(ctx, this_val);
|
||||
if (!A)
|
||||
return JS_EXCEPTION;
|
||||
|
||||
// Check if multiplying by another matrix
|
||||
matrix_t *B = js2matrix(ctx, argv[0]);
|
||||
if (B) {
|
||||
if (A->cols != B->rows)
|
||||
return JS_ThrowTypeError(ctx, "Matrix dimensions incompatible for multiplication");
|
||||
|
||||
matrix_t *C = js_malloc(ctx, sizeof(matrix_t));
|
||||
if (!C)
|
||||
return JS_EXCEPTION;
|
||||
|
||||
C->rows = A->rows;
|
||||
C->cols = B->cols;
|
||||
C->data = js_malloc(ctx, sizeof(double) * C->rows * C->cols);
|
||||
if (!C->data) {
|
||||
js_free(ctx, C);
|
||||
return JS_EXCEPTION;
|
||||
}
|
||||
|
||||
// C = A * B
|
||||
cblas_dgemm(CblasRowMajor, CblasNoTrans, CblasNoTrans,
|
||||
A->rows, B->cols, A->cols,
|
||||
1.0, A->data, A->cols,
|
||||
B->data, B->cols,
|
||||
0.0, C->data, C->cols);
|
||||
|
||||
JSValue obj = JS_NewObjectClass(ctx, js_matrix_class_id);
|
||||
JS_SetOpaque(obj, C);
|
||||
return obj;
|
||||
}
|
||||
|
||||
// Check if multiplying by an array (matrix-vector multiplication)
|
||||
array_t *x = js2array(ctx, argv[0]);
|
||||
if (x) {
|
||||
if (A->cols != x->size)
|
||||
return JS_ThrowTypeError(ctx, "Matrix columns must match array size");
|
||||
|
||||
array_t *y = js_malloc(ctx, sizeof(array_t));
|
||||
if (!y)
|
||||
return JS_EXCEPTION;
|
||||
|
||||
y->size = A->rows;
|
||||
y->data = js_malloc(ctx, sizeof(double) * y->size);
|
||||
if (!y->data) {
|
||||
js_free(ctx, y);
|
||||
return JS_EXCEPTION;
|
||||
}
|
||||
|
||||
// y = A * x
|
||||
cblas_dgemv(CblasRowMajor, CblasNoTrans,
|
||||
A->rows, A->cols,
|
||||
1.0, A->data, A->cols,
|
||||
x->data, 1,
|
||||
0.0, y->data, 1);
|
||||
|
||||
JSValue obj = JS_NewObjectClass(ctx, js_array_class_id);
|
||||
JS_SetOpaque(obj, y);
|
||||
return obj;
|
||||
}
|
||||
|
||||
return JS_ThrowTypeError(ctx, "multiply requires a Matrix or Array argument");
|
||||
}
|
||||
|
||||
static JSValue js_matrix_to_array(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv) {
|
||||
matrix_t *mat = js2matrix(ctx, this_val);
|
||||
if (!mat)
|
||||
return JS_EXCEPTION;
|
||||
|
||||
JSValue arr = JS_NewArray(ctx);
|
||||
if (JS_IsException(arr))
|
||||
return arr;
|
||||
|
||||
for (int i = 0; i < mat->rows; i++) {
|
||||
JSValue row = JS_NewArray(ctx);
|
||||
if (JS_IsException(row)) {
|
||||
JS_FreeValue(ctx, arr);
|
||||
return row;
|
||||
}
|
||||
for (int j = 0; j < mat->cols; j++) {
|
||||
JS_SetPropertyUint32(ctx, row, j, JS_NewFloat64(ctx, mat->data[i * mat->cols + j]));
|
||||
}
|
||||
JS_SetPropertyUint32(ctx, arr, i, row);
|
||||
}
|
||||
return arr;
|
||||
}
|
||||
|
||||
static JSValue js_array_to_array(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv) {
|
||||
array_t *arr = js2array(ctx, this_val);
|
||||
if (!arr)
|
||||
return JS_EXCEPTION;
|
||||
|
||||
JSValue jsarr = JS_NewArray(ctx);
|
||||
for (int i = 0; i < arr->size; i++) {
|
||||
JS_SetPropertyUint32(ctx, jsarr, i, JS_NewFloat64(ctx, arr->data[i]));
|
||||
}
|
||||
return jsarr;
|
||||
}
|
||||
|
||||
static JSValue js_array_dot(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv) {
|
||||
if (argc < 1)
|
||||
return JS_ThrowTypeError(ctx, "dot requires an argument");
|
||||
|
||||
array_t *a = js2array(ctx, this_val);
|
||||
if (!a)
|
||||
return JS_EXCEPTION;
|
||||
|
||||
array_t *b = js2array(ctx, argv[0]);
|
||||
if (!b)
|
||||
return JS_ThrowTypeError(ctx, "dot requires an Array argument");
|
||||
|
||||
if (a->size != b->size)
|
||||
return JS_ThrowTypeError(ctx, "Arrays must have the same size for dot product");
|
||||
|
||||
double result = cblas_ddot(a->size, a->data, 1, b->data, 1);
|
||||
return JS_NewFloat64(ctx, result);
|
||||
}
|
||||
|
||||
static JSValue js_array_norm(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv) {
|
||||
array_t *arr = js2array(ctx, this_val);
|
||||
if (!arr)
|
||||
return JS_EXCEPTION;
|
||||
|
||||
double norm = cblas_dnrm2(arr->size, arr->data, 1);
|
||||
return JS_NewFloat64(ctx, norm);
|
||||
}
|
||||
|
||||
static JSValue js_array_slice(JSContext *js, JSValueConst self, int argc, JSValueConst *argv)
|
||||
{
|
||||
array_t *arr = js2array(js, self);
|
||||
if (!arr)
|
||||
return JS_EXCEPTION;
|
||||
|
||||
int32_t start = 0;
|
||||
int32_t end = arr->size;
|
||||
|
||||
// Parse start argument
|
||||
if (argc >= 1 && !JS_IsUndefined(argv[0])) {
|
||||
if (JS_ToInt32(js, &start, argv[0]))
|
||||
return JS_EXCEPTION;
|
||||
|
||||
// Handle negative indices
|
||||
if (start < 0) {
|
||||
start = arr->size + start;
|
||||
if (start < 0) start = 0;
|
||||
} else if (start > arr->size) {
|
||||
start = arr->size;
|
||||
}
|
||||
}
|
||||
|
||||
// Parse end argument
|
||||
if (argc >= 2 && !JS_IsUndefined(argv[1])) {
|
||||
if (JS_ToInt32(js, &end, argv[1]))
|
||||
return JS_EXCEPTION;
|
||||
|
||||
// Handle negative indices
|
||||
if (end < 0) {
|
||||
end = arr->size + end;
|
||||
if (end < 0) end = 0;
|
||||
} else if (end > arr->size) {
|
||||
end = arr->size;
|
||||
}
|
||||
}
|
||||
|
||||
// Ensure start <= end
|
||||
if (start > end) {
|
||||
end = start; // Return empty array
|
||||
}
|
||||
|
||||
// Create JavaScript array
|
||||
JSValue jsarr = JS_NewArray(js);
|
||||
if (JS_IsException(jsarr))
|
||||
return jsarr;
|
||||
|
||||
// Fill with sliced values
|
||||
for (int32_t i = start; i < end; i++) {
|
||||
JS_SetPropertyUint32(js, jsarr, i - start, JS_NewFloat64(js, arr->data[i]));
|
||||
}
|
||||
|
||||
return jsarr;
|
||||
}
|
||||
|
||||
static int js_array_get_own_property(JSContext *ctx, JSPropertyDescriptor *desc,
|
||||
JSValueConst obj, JSAtom prop)
|
||||
{
|
||||
array_t *arr = JS_GetOpaque(obj, js_array_class_id);
|
||||
if(!arr) return FALSE;
|
||||
|
||||
if(JS_AtomIsNumericIndex(ctx, prop) > 0) {
|
||||
JSValue key = JS_AtomToValue(ctx, prop);
|
||||
uint32_t idx;
|
||||
JS_ToUint32(ctx, &idx, key);
|
||||
JS_FreeValue(ctx, key);
|
||||
if(idx < arr->size) {
|
||||
if(desc) {
|
||||
desc->flags = JS_PROP_C_W_E; /* configurable, writable, enumerable */
|
||||
desc->value = JS_NewFloat64(ctx, arr->data[idx]);
|
||||
desc->getter = JS_UNDEFINED;
|
||||
desc->setter = JS_UNDEFINED;
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
int ret;
|
||||
|
||||
const char *name = JS_AtomToCString(ctx, prop);
|
||||
if (name && strcmp(name, "length") == 0) {
|
||||
if(desc) {
|
||||
/* length is read-only, non-enumerable */
|
||||
desc->flags = JS_PROP_CONFIGURABLE;
|
||||
desc->value = JS_NewInt32(ctx, arr->size);
|
||||
desc->getter = JS_UNDEFINED;
|
||||
desc->setter = JS_UNDEFINED;
|
||||
}
|
||||
ret = TRUE;
|
||||
}
|
||||
|
||||
ret = FALSE;
|
||||
|
||||
JS_FreeCString(ctx, name);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
#include "quickjs-atom.h"
|
||||
|
||||
static int js_array_define_own_property(JSContext *ctx, JSValueConst this_obj,
|
||||
JSAtom prop, JSValueConst val,
|
||||
JSValueConst getter, JSValueConst setter,
|
||||
int flags)
|
||||
{
|
||||
array_t *arr = JS_GetOpaque(this_obj, js_array_class_id);
|
||||
if(!arr) return FALSE;
|
||||
|
||||
if(JS_AtomIsNumericIndex(ctx, prop) > 0) {
|
||||
JSValue key = JS_AtomToValue(ctx, prop);
|
||||
uint32_t idx;
|
||||
JS_ToUint32(ctx, &idx, key);
|
||||
JS_FreeValue(ctx, key);
|
||||
if(idx < arr->size) {
|
||||
double d;
|
||||
if(JS_ToFloat64(ctx, &d, val) == 0) {
|
||||
arr->data[idx] = d;
|
||||
return TRUE;
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/* reject everything else (including “length”) */
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
|
||||
// Set up the exotic methods structure
|
||||
static const JSClassExoticMethods js_array_exotic = {
|
||||
.get_own_property = js_array_get_own_property,
|
||||
.define_own_property = js_array_define_own_property,
|
||||
// Other methods can be NULL for now
|
||||
};
|
||||
|
||||
static const JSCFunctionListEntry js_matrix_proto_funcs[] = {
|
||||
JS_CFUNC_DEF("inverse", 0, js_matrix_inverse),
|
||||
JS_CFUNC_DEF("multiply", 1, js_matrix_multiply),
|
||||
JS_CFUNC_DEF("toArray", 0, js_matrix_to_array),
|
||||
};
|
||||
|
||||
static const JSCFunctionListEntry js_array_proto_funcs[] = {
|
||||
JS_CFUNC_DEF("dot", 1, js_array_dot),
|
||||
JS_CFUNC_DEF("norm", 0, js_array_norm),
|
||||
JS_CFUNC_DEF("toArray", 0, js_array_to_array),
|
||||
JS_CFUNC_DEF("toJSON", 0, js_array_to_array), /* ← for JSON.stringify */
|
||||
JS_CFUNC_DEF("slice", 2, js_array_slice),
|
||||
};
|
||||
|
||||
|
||||
JSValue js_num_use(JSContext *ctx) {
|
||||
// Register Matrix class
|
||||
JS_NewClassID(&js_matrix_class_id);
|
||||
JS_NewClass(JS_GetRuntime(ctx), js_matrix_class_id, &js_matrix_class);
|
||||
JSValue matrix_proto = JS_NewObject(ctx);
|
||||
JS_SetPropertyFunctionList(ctx, matrix_proto, js_matrix_proto_funcs,
|
||||
sizeof(js_matrix_proto_funcs) / sizeof(JSCFunctionListEntry));
|
||||
JS_SetClassProto(ctx, js_matrix_class_id, matrix_proto);
|
||||
|
||||
// Create Matrix constructor
|
||||
JSValue matrix_ctor = JS_NewCFunction2(ctx, js_matrix_constructor, "Matrix", 1, JS_CFUNC_constructor, 0);
|
||||
JS_SetConstructor(ctx, matrix_ctor, matrix_proto);
|
||||
|
||||
// Register Array class
|
||||
JS_NewClassID(&js_array_class_id);
|
||||
JS_NewClass(JS_GetRuntime(ctx), js_array_class_id, &js_array_class);
|
||||
JSValue array_proto = JS_NewObject(ctx);
|
||||
JS_SetPropertyFunctionList(ctx, array_proto, js_array_proto_funcs,
|
||||
sizeof(js_array_proto_funcs) / sizeof(JSCFunctionListEntry));
|
||||
JS_SetClassProto(ctx, js_array_class_id, array_proto);
|
||||
|
||||
// Create Array constructor
|
||||
JSValue array_ctor = JS_NewCFunction2(ctx, js_array_constructor, "Array", 1, JS_CFUNC_constructor, 0);
|
||||
JS_SetConstructor(ctx, array_ctor, array_proto);
|
||||
|
||||
JSValue export = JS_NewObject(ctx);
|
||||
JS_SetPropertyStr(ctx, export, "Matrix", matrix_ctor);
|
||||
JS_SetPropertyStr(ctx, export, "Array", array_ctor);
|
||||
return export;
|
||||
}
|
||||
|
||||
@@ -300,24 +300,3 @@ JSValue js_qr_use(JSContext *js) {
|
||||
JS_SetPropertyFunctionList(js, exports, js_qr_funcs, sizeof(js_qr_funcs) / sizeof(JSCFunctionListEntry));
|
||||
return exports;
|
||||
}
|
||||
|
||||
// Module init
|
||||
static int js_qr_init(JSContext *js, JSModuleDef *m) {
|
||||
JS_SetModuleExport(js, m, "default", js_qr_use(js));
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef JS_SHARED_LIBRARY
|
||||
#define JS_INIT_MODULE js_init_module
|
||||
#else
|
||||
#define JS_INIT_MODULE js_init_module_qr
|
||||
#endif
|
||||
|
||||
JSModuleDef *JS_INIT_MODULE(JSContext *js, const char *module_name) {
|
||||
JSModuleDef *m = JS_NewCModule(js, module_name, js_qr_init);
|
||||
if (!m)
|
||||
return NULL;
|
||||
|
||||
JS_AddModuleExport(js, m, "default");
|
||||
return m;
|
||||
}
|
||||
|
||||
@@ -217,22 +217,3 @@ JSValue js_soloud_use(JSContext *js)
|
||||
JS_SetPropertyFunctionList(js, export, js_soloud_funcs, sizeof(js_soloud_funcs)/sizeof(JSCFunctionListEntry));
|
||||
return export;
|
||||
}
|
||||
|
||||
static int js_soloud_init(JSContext *js, JSModuleDef *m) {
|
||||
js_soloud_use(js);
|
||||
JS_SetModuleExportList(js, m, js_soloud_funcs, sizeof(js_soloud_funcs)/sizeof(JSCFunctionListEntry));
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef JS_SHARED_LIBRARY
|
||||
#define JS_INIT_MODULE js_init_module
|
||||
#else
|
||||
#define JS_INIT_MODULE js_init_module_soloud
|
||||
#endif
|
||||
|
||||
JSModuleDef *JS_INIT_MODULE(JSContext *ctx, const char *module_name) {
|
||||
JSModuleDef *m = JS_NewCModule(ctx, module_name, js_soloud_init);
|
||||
if (!m) return NULL;
|
||||
JS_AddModuleExportList(ctx, m, js_soloud_funcs, sizeof(js_soloud_funcs)/sizeof(JSCFunctionListEntry));
|
||||
return m;
|
||||
}
|
||||
|
||||
@@ -381,26 +381,6 @@ static const JSCFunctionListEntry js_wota_funcs[] = {
|
||||
JS_CFUNC_DEF("decode", 2, js_wota_decode),
|
||||
};
|
||||
|
||||
static int js_wota_init(JSContext *ctx, JSModuleDef *m)
|
||||
{
|
||||
JS_SetModuleExportList(ctx, m, js_wota_funcs, sizeof(js_wota_funcs)/sizeof(js_wota_funcs[0]));
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef JS_SHARED_LIBRARY
|
||||
#define JS_INIT_MODULE js_init_module
|
||||
#else
|
||||
#define JS_INIT_MODULE js_init_module_wota
|
||||
#endif
|
||||
|
||||
JSModuleDef *JS_INIT_MODULE(JSContext *ctx, const char *module_name)
|
||||
{
|
||||
JSModuleDef *m = JS_NewCModule(ctx, module_name, js_wota_init);
|
||||
if (!m) return NULL;
|
||||
JS_AddModuleExportList(ctx, m, js_wota_funcs, sizeof(js_wota_funcs)/sizeof(js_wota_funcs[0]));
|
||||
return m;
|
||||
}
|
||||
|
||||
JSValue js_wota_use(JSContext *ctx)
|
||||
{
|
||||
JSValue exports = JS_NewObject(ctx);
|
||||
|
||||
23
tests/num_property_test.ce
Normal file
23
tests/num_property_test.ce
Normal file
@@ -0,0 +1,23 @@
|
||||
// Test property access for num.Array
|
||||
var num = use('num');
|
||||
|
||||
// Create an array
|
||||
var arr = new num.Array([10, 20, 30, 40, 50]);
|
||||
|
||||
log.console("Testing property access:");
|
||||
log.console("arr[0] =", arr[0]);
|
||||
log.console("arr[1] =", arr[1]);
|
||||
log.console("arr[2] =", arr[2]);
|
||||
log.console("arr[4] =", arr[4]);
|
||||
|
||||
arr[0] = 15
|
||||
log.console(arr[0])
|
||||
log.console("arr[10] =", arr[10]); // Should be undefined
|
||||
|
||||
log.console("arr.length =", arr.length);
|
||||
|
||||
log.console(arr instanceof num.Array)
|
||||
log.console(json.encode(arr))
|
||||
|
||||
|
||||
$_.stop();
|
||||
32
tests/num_setter_test.ce
Normal file
32
tests/num_setter_test.ce
Normal file
@@ -0,0 +1,32 @@
|
||||
// Test setter functionality for num.Array
|
||||
var num = use('num');
|
||||
|
||||
// Create an array
|
||||
var arr = new num.Array([1, 2, 3, 4, 5]);
|
||||
|
||||
log.console("Original values:");
|
||||
log.console("arr[0] =", arr[0], "arr[1] =", arr[1], "arr[2] =", arr[2]);
|
||||
|
||||
// Test setting values
|
||||
arr[0] = 100;
|
||||
arr[1] = 200;
|
||||
arr[2] = 300.5;
|
||||
|
||||
log.console("After setting:");
|
||||
log.console("arr[0] =", arr[0], "arr[1] =", arr[1], "arr[2] =", arr[2]);
|
||||
|
||||
// Test setting with different types
|
||||
arr[3] = "123.7"; // Should convert string to number
|
||||
arr[4] = true; // Should convert boolean to number
|
||||
|
||||
log.console("After type conversion:");
|
||||
log.console("arr[3] =", arr[3], "(from string)");
|
||||
log.console("arr[4] =", arr[4], "(from boolean)");
|
||||
|
||||
// Test bounds checking - this should fail silently
|
||||
arr[10] = 999;
|
||||
log.console("arr[10] after out-of-bounds set =", arr[10]);
|
||||
|
||||
log.console("Final array:", arr.toArray());
|
||||
|
||||
$_.stop();
|
||||
54
tests/num_test.ce
Normal file
54
tests/num_test.ce
Normal file
@@ -0,0 +1,54 @@
|
||||
// Test the num module
|
||||
var num = use('num');
|
||||
|
||||
// Test matrix creation and operations
|
||||
var A = new num.Matrix([
|
||||
[1, 2, 3],
|
||||
[4, 5, 6],
|
||||
[7, 8, 10]
|
||||
]);
|
||||
|
||||
log.console("Matrix A:");
|
||||
log.console(A.toArray());
|
||||
|
||||
// Test matrix inversion
|
||||
var A_inv = A.inverse();
|
||||
log.console("\nMatrix A inverse:");
|
||||
log.console(A_inv.toArray());
|
||||
|
||||
// Verify A * A_inv = I (approximately)
|
||||
var I = A.multiply(A_inv);
|
||||
log.console("\nA * A_inv (should be identity):");
|
||||
log.console(I.toArray());
|
||||
|
||||
// Test array creation
|
||||
var v = new num.Array([1, 2, 3]);
|
||||
log.console("\nVector v:");
|
||||
log.console(v.toArray());
|
||||
|
||||
// Test matrix-vector multiplication
|
||||
var result = A.multiply(v);
|
||||
log.console("\nA * v:");
|
||||
log.console(result.toArray());
|
||||
|
||||
// Test dot product
|
||||
var u = new num.Array([4, 5, 6]);
|
||||
var dot_product = v.dot(u);
|
||||
log.console("\nv · u =", dot_product);
|
||||
|
||||
// Test norm
|
||||
var v_norm = v.norm();
|
||||
log.console("||v|| =", v_norm);
|
||||
|
||||
// Test matrix-matrix multiplication
|
||||
var B = new num.Matrix([
|
||||
[1, 0, 0],
|
||||
[0, 2, 0],
|
||||
[0, 0, 3]
|
||||
]);
|
||||
|
||||
var C = A.multiply(B);
|
||||
log.console("\nA * B:");
|
||||
log.console(C.toArray());
|
||||
|
||||
$_.stop()
|
||||
Reference in New Issue
Block a user