IIHE Nobel Prize website

When I started working for the ULB at the IIHE I wanted to get involved with some outreach activities. One of my first activities was the creation of a website celebrating the Nobel Prize in Phsics 2013, which was awarded to François Englert and Peter Higgs for the prediction of the scalar (Higgs) boson. This involved collaborating and development using HTML, CSS, and PHP, with multiple language support, search engine optimisation, YouTube integration, and video recording sessions. The results was a webpage where the users of the lab got to show off what they did to the wider world and to show how the rich physics program at the lab was relevant to the prize. It was a fun project to work on, it was my own creation and was excellent practice for working on similar projects in the future.

Live page

Spectrum

This project was made around the same time as the Reflections project, but I cannot remember which was written first.

Overview

This project takes a parametric equation provided by the user and then makes pretty curves, where the colours reflect the values of the parameters. This is performed in much the same way as in the Reflections project, except this time the user has complete freedom of the form of the expressions. This is achieved by writing the expressions directly as a Javascript macro embedded in the SVG. This way the expensive and potentially dangerous eval function is avoided, as it is in the Inverter project.

The $$(x,y)$$ values are determined using functions of a variable, $$t$$, and a parameter, $$q$$:

$$x = f(t;q)$$, $$y = g(t;q)$$, where the different colours correspond to different values of $$q$$.

This project was used to make the icon for the LGBT CERN Twitter and Facebook accounts.

Challenges

Challenge: This project needed to have SVG and Javascript working together.
Solution: This is not easy! When embedding Javascript inside SVG is must be properly wrapped, and cannot use any external files. As a result that SVG file is often cumbersome and any libraries have to be written to the SVG file, making excessive use of Javascript unreasonable. (Although this didn’t stop me from trying to write an SVG based platform game.) (Resolved)
Challenge: This project required a smooth colour gradient for best effect.
Solution: This was one of the first times I needed to make a smooth colour gradient, and it is not as trivial as I might like. I found a way to quickly create an arbitrary colour gradient, which would help with many projects in the future, especially those that involve fractals. (Resolved)

Screenshot

Here is the output for $$x = 3\sin(q+2t\pi)$$, $$y=t^2$$, $$-1\lt t \lt 1$$, $$0\lt q \lt 1$$

Reflections

A “reflection” is a term in particle physics for when a decay of one particle looks like the decay of another. It’s a topic which has been studied for about fifty years, and seemed like an excellent chance to combine physics and coding.

Overview

To make a reflection it’s necessary to take one particle that decays to two daughters, and then assign the wrong mass hypothesis to at least one of the daughters. The way this is done is by using PHP to generate an SVG image with Javascript embedded within it. The user then makes a POST request to create a new image.

The definition of the helicity angle is the angle between one of the daughter particles, and the relativistic boost into the lab frame, in the frame of the mother particle. The angle is determined by explicitly boosting into this frame and taking the dot product.

Challenges

Challenge: This was one of the first projects where I had to map onto a coordinate system on a canvas.
Solution: This is not as easy as it sounds when axes are involved! By appropriate use of the log function and creating margins it was possible to make a two dimensional histogram, which is a model I’ve used ever since. (Resolved)
Challenge: This project needs some insight into how the underlying physics works.
Solution: It took some thought about how to make this work effectively, as it’s essentially an exercise in four vectors, which are always more subtle than I’d like. (Resolved)
Challenge: The plot needs to be updated to match the user’s input.
Solution: At the time this was written I was already used to using both PHP and SVG, which were the state of the art at the time. It’s a bit clunky to send a POST request every time the plot needs to be updated, and now we have the canvas. At some point I should update this code to use Javascript and the canvas. (Resolved, to be revisited)

Screenshot

Here is the output for the decay $$\Lambda \to p \pi$$ ($$m(\Lambda)=1150 ~\mathrm{MeV}$$) creating a reflection for the decay $$K_S^0 \to \pi\pi$$ ($$m(K_S^0)=497~\mathrm{MeV}$$), which has been studied for decades.

Iterated path

In 2014 the CMS experiment at CERN released their 2010 data to the public for analysis. As a quick exercise I decided to analyse the dimuon mass spectrum to show that this could be done in a reasonable amount of time.

Overview

The input file is the 2010 Run B comma separated values file. The python script then produces mass spectra for same sign and opposite sign mass spectra and zooms in on the interesting regions.

Challenges

Challenge: The main challenge was that this project was made as quickly as possible.
Solution: This project uses python and existing ROOT libraries for the maximal development speed. The other data format available was using CMSSW and a virtual machine. In principle using CMSSW should be straightforward, but I decided against using this because the software was already four years old and support would be minimal or non-existant, even to current CMS physicsts. (Resolved)

91 map

I played the game 91 and wanted to create a map of the world. What started out as a simple map to help keep track of the regions soon turned into a larger project which resulted in an A0 sized poster being produced. The creator of the game got in touch and at the time of writing the first poster has been printed and more will follow. The creator is going to publish and sell these posters online.

Overview

The game 91 is a tile based canvas game which is written entirely in Javascript (served up by PHP via AJAX requests). The game itself has a “fog” which means that only regions in a line of sight are visible. Given the way the game is organised means that in principle it should be easy to create a map. I adapted the code to make maps of each region, and then created a patchwork of all these small maps to create a larger map.

Challenges

Challenge: The code had to be reverse engineered to obtain all the maps.
Solution: One of the most fun parts of this project was reverse engineering how the AJAX requests were handled and how the maps were parsed. Fortunately it was relatively straightforward to do. (Resolved.)
Challenge: Small maps had to be combined with different drawing styles.
Solution: To make the large map I had to combined many smaller maps. There’s no “cheap” way to do this, so I created an object that contained the smaller maps in a larger two dimensional array where each cell has its own drawing style flag, allowing non-trivial overlap of different drawing styles. (Resolved.)
Challenge: A poster of very large dimensions needed to be created.
Solution: There’s no easy way to manage an image of the size needed to make a large poster. I have made large posters before (for example, in the Marble Hornets project) so I was prepared for many of the problems. The sides of the map had to have relative sizes of $$\sqrt{2}$$, which fortunately was relatively easy (especially when compared to the Citadel project. In the end I opted to have a large .png file at $$\sim 500 ~\mathrm{dpi}$$ giving very fine print quality. The resulting file is just under 20 MB in size, which is rather impressive given its physical size. (Resolved.)

Sample outputs

The final version of the poster, as approved by the game’s creator, can be seen here: Poster version 2.

Ever since I was a child and I played Citadel on the BBC Micro computer I’ve wanted a poster showing a map of the entire game. Inspired by a recent project (91) I decided to create a poster for Citadel. The results are necessarily “blocky”, but not altogether displeasing. Ideally I’d prefer to have a very wide poster, but I’m not sure such an aspect ratio is supported by most print services, so I settled for a standard A series size instead.

Getting this printed and put up on my wall will make me very happy!

Full size poster

Tetris

This was my first serious Javascript project which was written some time in 2009. I chose to write a Tetris clone because it was a well defined project that would teach me how to use Javascript. In addition to Tetris I also made Tritris and Pentris to see how well balanced the three games are compared to each other. It turns out that Tetris is about right, with Tritris being too easy and Pentris being too hard.

Overview

The user plays the game with the arrow keys, and the game gets slowly faster as their score increases. There is a MySQL and PHP backend to save scores on the server. As much as possible, the three games have been harmonised so that they use the same page, the same Javascript library, and the same PHP page for interaction with the server. This is outlined in a previous post where I discussed how the code was refactored. This is one of my favourite projects, as it’s one of the few “complete” projects that touches on almost all of Javascript, with some HTML, CSS, PHP, MySQL, httpxml, and cookies also thrown in there. This project taught me so much about Javascript and was an excellent start with the language.

Challenges

Challenge: This project required learning how to use Javascript.
Solution: What a challenge! Having worked previously with C++, I found that Javascript was rather easy to learn, and quickly came across its peculiarities and limitations. (Resolved)
Challenge: The project required detailed manipulation of the DOM.
Solution: It was with this project that I learned how to use the DOM, which helped me to better understand the heirarchical structure of XML in general. I also wanted the HTML to be semantically pure, so while I used the DOM to store some information about the state of the game, I also ensured that it was semantically consistent. (Resolved)
Challenge: This game required careful control of Javascript events and synchronisation.
Solution: This was probably the most difficult and instructive part of the project. I had to learn how to register event handlers in a manner which worked across browsers. I still use the same style of event handling today that I developed when I wrote this project. It took a while to get used to the issues of synchronisation using the window.setTimeout method, which I still use frequently today. (Resolved)
Challenge: I had to store some data on the server.
Solution: I had had plenty of experience with PHP and MySQL before this project, including sanitising input to the database, so the PHP side of this challenge was easy to implement. However making the httpxml requests was not so easy and took some practice. After a few iterations I got a working model, although this is something I should improve further, as httpxml requests tend to be rather messy. (Resolved)
Challenge: One of the users wanted a feature that required cookies.
Solution: One user spent so long playing the game that he wanted to be able to “block” himself. As a result I had to implement a feature hat sets a cookie that prevents the user from playing. This was the first time I had set and read cookies using Javascript, and not something I have had much use for since. (Resolved)
Challenge: The game has a soundtrack.
Solution: Having used so many feature of Javascript, I wanted to add some music. This is far from trivial in the world of Javascript, and not so easy in the days befre embedded YouTube videos. Although support is a little shaky, the music was added and an interface included. (Resolved)
Challenge: The game had to have cross browser support.
Solution: This game was initially developed using Firefox, but one of the users wanted it to work with Chrome. This was the first time I met the frustration of cross browser event handling, which has been something of a pain ever since, but it was not too hard to overcome. (Resolved)

Screenshot

This is a collection of YouTube videos as made as an outreach project in December 2012. Each day of Advent gives a different interesting fact about particle physics.

Overview

This is mostly an exercise in using the YouTube Javascript API. It was this project that made me realise that the old Javascript API is now deprecated, which means I will have to update the Marble Hornets project at some point.

Challenges

Challenge: The YouTube Javascript API changed.
Solution: Having already become accustomed to the old YouTube API, it was somewhat frustrating to get used to a new one, but such is life and working with bleeding edge technologies! (Resolved.)

Code update: Tetris

The first major Javascript project I took on was a clone of Tetris. Every time I look back at it I’m actually surprised an impressed at how many features it has. For a first project it has DOM manipulation, httpxml requests, timeout functions, cookies, audio, and a MySQL backend. After making the Tetris clone I then tried to make Pentris, which is the same game but with five blocks per piece. In going from Tetris to Pentris I made some interesting optimisations.

For example, here is the function tryRotate:

function tryRotate(){
switch(color){
case "red":
switch(orientation){
case 0:
case 2:
rotatedC[0] = C[0] + 1 ; rotatedR[0] = R[0] + 0 ;
rotatedC[1] = C[1] + 0 ; rotatedR[1] = R[1] + 1 ;
rotatedC[2] = C[2] - 1 ; rotatedR[2] = R[2] + 0 ;
rotatedC[3] = C[3] - 2 ; rotatedR[3] = R[3] + 1 ;
break ;
case 1:
case 3:
rotatedC[0] = C[0] - 1 ; rotatedR[0] = R[0] + 0 ;
rotatedC[1] = C[1] + 0 ; rotatedR[1] = R[1] - 1 ;
rotatedC[2] = C[2] + 1 ; rotatedR[2] = R[2] + 0 ;
rotatedC[3] = C[3] + 2 ; rotatedR[3] = R[3] - 1 ;
break ;
}
break ;

case "green":
switch(orientation){
case 0:
rotatedC[0] = C[0] + 1 ; rotatedR[0] = R[0] + 1 ;
rotatedC[1] = C[1] + 1 ; rotatedR[1] = R[1] - 1 ;
rotatedC[2] = C[2] + 0 ; rotatedR[2] = R[2] + 0 ;
rotatedC[3] = C[3] - 1 ; rotatedR[3] = R[3] + 1 ;
break ;
case 1:
rotatedC[0] = C[0] - 1 ; rotatedR[0] = R[0] + 1 ;
rotatedC[1] = C[1] + 1 ; rotatedR[1] = R[1] + 1 ;
rotatedC[2] = C[2] + 0 ; rotatedR[2] = R[2] + 0 ;
rotatedC[3] = C[3] - 1 ; rotatedR[3] = R[3] - 1 ;
break ;
case 2:
rotatedC[0] = C[0] - 1 ; rotatedR[0] = R[0] - 1 ;
rotatedC[1] = C[1] - 1 ; rotatedR[1] = R[1] + 1 ;
rotatedC[2] = C[2] + 0 ; rotatedR[2] = R[2] + 0 ;
rotatedC[3] = C[3] + 1 ; rotatedR[3] = R[3] - 1 ;
break ;
case 3:
rotatedC[0] = C[0] + 1 ; rotatedR[0] = R[0] - 1 ;
rotatedC[1] = C[1] - 1 ; rotatedR[1] = R[1] - 1 ;
rotatedC[2] = C[2] + 0 ; rotatedR[2] = R[2] + 0 ;
rotatedC[3] = C[3] + 1 ; rotatedR[3] = R[3] + 1 ;
break ;
}
break ;

case "blue":
switch(orientation){
case 0:
rotatedC[0] = C[0] + 1 ; rotatedR[0] = R[0] + 0 ;
rotatedC[1] = C[1] + 0 ; rotatedR[1] = R[1] + 1 ;
rotatedC[2] = C[2] - 1 ; rotatedR[2] = R[2] + 2 ;
rotatedC[3] = C[3] - 2 ; rotatedR[3] = R[3] + 1 ;
break ;
case 1:
rotatedC[0] = C[0] + 1 ; rotatedR[0] = R[0] + 1 ;
rotatedC[1] = C[1] + 0 ; rotatedR[1] = R[1] + 0 ;
rotatedC[2] = C[2] - 1 ; rotatedR[2] = R[2] - 1 ;
rotatedC[3] = C[3] + 0 ; rotatedR[3] = R[3] - 2 ;
break ;
case 2:
rotatedC[0] = C[0] - 1 ; rotatedR[0] = R[0] + 1 ;
rotatedC[1] = C[1] + 0 ; rotatedR[1] = R[1] + 0 ;
rotatedC[2] = C[2] + 1 ; rotatedR[2] = R[2] - 1 ;
rotatedC[3] = C[3] + 2 ; rotatedR[3] = R[3] + 0 ;
break ;
case 3:
rotatedC[0] = C[0] - 1 ; rotatedR[0] = R[0] - 2 ;
rotatedC[1] = C[1] + 0 ; rotatedR[1] = R[1] - 1 ;
rotatedC[2] = C[2] + 1 ; rotatedR[2] = R[2] + 0 ;
rotatedC[3] = C[3] + 0 ; rotatedR[3] = R[3] + 1 ;
break ;
}
break ;

case "cyan":
switch(orientation){
case 0:
rotatedC[0] = C[0] - 1 ; rotatedR[0] = R[0] + 2 ;
rotatedC[1] = C[1] + 0 ; rotatedR[1] = R[1] + 1 ;
rotatedC[2] = C[2] + 1 ; rotatedR[2] = R[2] + 0 ;
rotatedC[3] = C[3] + 0 ; rotatedR[3] = R[3] - 1 ;
break ;
case 1:
rotatedC[0] = C[0] - 1 ; rotatedR[0] = R[0] - 1 ;
rotatedC[1] = C[1] + 0 ; rotatedR[1] = R[1] + 0 ;
rotatedC[2] = C[2] + 1 ; rotatedR[2] = R[2] + 1 ;
rotatedC[3] = C[3] + 2 ; rotatedR[3] = R[3] + 0 ;
break ;
case 2:
rotatedC[0] = C[0] + 1 ; rotatedR[0] = R[0] - 1 ;
rotatedC[1] = C[1] + 0 ; rotatedR[1] = R[1] + 0 ;
rotatedC[2] = C[2] - 1 ; rotatedR[2] = R[2] + 1 ;
rotatedC[3] = C[3] + 0 ; rotatedR[3] = R[3] + 2 ;
break ;
case 3:
rotatedC[0] = C[0] + 1 ; rotatedR[0] = R[0] + 0 ;
rotatedC[1] = C[1] + 0 ; rotatedR[1] = R[1] - 1 ;
rotatedC[2] = C[2] - 1 ; rotatedR[2] = R[2] - 2 ;
rotatedC[3] = C[3] - 2 ; rotatedR[3] = R[3] - 1 ;
break ;
}
break ;

case "magenta":
switch(orientation){
case 0:
case 2:
rotatedC[0] = C[0] - 1 ; rotatedR[0] = R[0] + 0 ;
rotatedC[1] = C[1] + 0 ; rotatedR[1] = R[1] + 1 ;
rotatedC[2] = C[2] + 1 ; rotatedR[2] = R[2] + 0 ;
rotatedC[3] = C[3] + 2 ; rotatedR[3] = R[3] + 1 ;
break ;
case 1:
case 3:
rotatedC[0] = C[0] + 1 ; rotatedR[0] = R[0] + 0 ;
rotatedC[1] = C[1] + 0 ; rotatedR[1] = R[1] - 1 ;
rotatedC[2] = C[2] - 1 ; rotatedR[2] = R[2] + 0 ;
rotatedC[3] = C[3] - 2 ; rotatedR[3] = R[3] - 1 ;
break ;
}
break ;

case "yellow":
switch(orientation){
case 0:
case 1:
case 2:
case 3:
rotatedC[0] = C[0] + 0 ; rotatedR[0] = R[0] + 0 ;
rotatedC[1] = C[1] + 0 ; rotatedR[1] = R[1] + 0 ;
rotatedC[2] = C[2] + 0 ; rotatedR[2] = R[2] + 0 ;
rotatedC[3] = C[3] + 0 ; rotatedR[3] = R[3] + 0 ;
break ;
}
break ;

case "white":
switch(orientation){
case 0:
case 2:
rotatedC[0] = C[0] + 1 ; rotatedR[0] = R[0] - 1 ;
rotatedC[1] = C[1] + 0 ; rotatedR[1] = R[1] + 0 ;
rotatedC[2] = C[2] - 1 ; rotatedR[2] = R[2] + 1 ;
rotatedC[3] = C[3] - 2 ; rotatedR[3] = R[3] + 2 ;
break ;
case 1:
case 3:
rotatedC[0] = C[0] - 1 ; rotatedR[0] = R[0] + 1 ;
rotatedC[1] = C[1] + 0 ; rotatedR[1] = R[1] + 0 ;
rotatedC[2] = C[2] + 1 ; rotatedR[2] = R[2] - 1 ;
rotatedC[3] = C[3] + 2 ; rotatedR[3] = R[3] - 2 ;
break ;
}
break ;
}
for(var i=0 ; i<4 ; i++){
if(rotatedR[i]>nRows) return false ;
if(rotatedR[i]<1)     return false ;
if(rotatedC[i]>nCols) return false ;
if(rotatedC[i]<1)     return false ;
var skip = 0 ;
for(var j=0 ; j<4 ; j++){ if(rotatedR[i]==R[j] && rotatedC[i]==C[j]) skip = 1 ; }
if(skip==0){
var className = getClass(rotatedR[i],rotatedC[i]) ;
if(className!="empty") return false ;
}
}
for(var i=0 ; i<4 ; i++){
set(R[i],C[i], "empty") ;
R[i] = rotatedR[i] ;
C[i] = rotatedC[i] ;
}
for(var i=0 ; i<4 ; i++){
set(R[i],C[i], color) ;
}
orientation++ ;
if(orientation==4) orientation = 0 ;
}


Compare that to the streamlined version I had for Pentris:

function tryRotate(){
rotatePiece() ;
for(var i=0 ; i<n ; i++){
if(rotatedR[i]>nRows) return false ;
if(rotatedR[i]<1)     return false ;
if(rotatedC[i]>nCols) return false ;
if(rotatedC[i]<1)     return false ;
var skip = 0 ;
for(var j=0 ; j<n ; j++){ if(rotatedR[i]==R[j] && rotatedC[i]==C[j]) skip = 1 ; }
if(skip==0){
var className = getClass(rotatedR[i],rotatedC[i]) ;
if(className!="empty") return false ;
}
}
for(var i=0 ; i<n ; i++){
set(R[i],C[i], "empty") ;
R[i] = rotatedR[i] ;
C[i] = rotatedC[i] ;
}
for(var i=0 ; i<n ; i++){ set(R[i],C[i], color) ;}
}
function rotatePiece(){
for(var i=0 ; i<n ; i++){
rotatedR[i] =  (Y) + C[i] - (X) ;
rotatedC[i] =  (X) - R[i] + (Y) ;
}
}


The optimisation here is quite impressive, and very much needed when you consider that the move from Tetris to Pentris means moving from five pieces of four blocks each, to eighteeen pieces of five blocks each, with the space needed for ratotions scaling from sixteen to twenty five.

In the first function the different scenarios are all checked explicitly, whereas in the second function the temporary piece is rotated and then collisions checked, before updating the main piece. The amount of code when from 82 to 27 lines, and in doing so became safer and more easily extendible.

In a similar vein, here's the updatePreview function, first in Tetris:

function updatePreview(){
for(var i=1 ; i<3 ; i++){ for(var j=1 ; j<5 ; j++){ setPreview(i, j, "empty") ; } }
switch(nextPiece){
case 1:
setPreview(1, 2, "red") ;
setPreview(1, 3, "red") ;
setPreview(2, 3, "red") ;
setPreview(2, 4, "red") ;
break ;
case 2:
setPreview(1, 3, "green") ;
setPreview(2, 2, "green") ;
setPreview(2, 3, "green") ;
setPreview(2, 4, "green") ;
break ;
case 3:
setPreview(1, 1, "blue") ;
setPreview(1, 2, "blue") ;
setPreview(1, 3, "blue") ;
setPreview(2, 3, "blue") ;
break ;
case 4:
setPreview(1, 3, "cyan") ;
setPreview(1, 2, "cyan") ;
setPreview(1, 1, "cyan") ;
setPreview(2, 1, "cyan") ;
break ;
case 5:
setPreview(1, 3, "magenta") ;
setPreview(1, 2, "magenta") ;
setPreview(2, 2, "magenta") ;
setPreview(2, 1, "magenta") ;
break ;
case 6:
setPreview(1, 2, "yellow") ;
setPreview(1, 3, "yellow") ;
setPreview(2, 3, "yellow") ;
setPreview(2, 2, "yellow") ;
break ;
case 7:
setPreview(2, 1, "white") ;
setPreview(2, 2, "white") ;
setPreview(2, 3, "white") ;
setPreview(2, 4, "white") ;
break ;
}
}


Second in Pentris:

function updatePreview(){
for(var i=1 ; i<4 ; i++){ for(var j=1 ; j<6 ; j++){ setPreview(i, j, "empty") ; } }
var x = new Array(n) ;
var y = new Array(n) ;
var piece = getPiece(nextPiece) ;
var counter = 0 ;
for(var i=0 ; i<n ; i++){
for(var j=0 ; j<n ; j++){
if(piece[i][j]==1){
x[counter] = i   ;
y[counter] = j+1 ;
counter++ ;
}
}
}
for(var i=0 ; i<n ; i++){setPreview(x[i],y[i],colors[nextPiece-1]);}
}


Such an economy of code! One of my favourite parts of code is refactoring existing blocks of code into something more elegant and effiienct. The original code, as a first attempt, and a first adventure in serious Javascript was necessarily messy and in need of a good clean, so it's no surprise that so many improvements could be had.

I've been putting off resolving these differences for a very long time, partly because the games work as they are and there's no need to fix something that's not broken, and partly because I wanted to keep some legacy code around to remind myself of how it all started. I've archived the original code and now I'm bringing the Tetris, Pentris, and Tritris into line with each other to make all three games share a single code base.

Complex polynomial

On the Numberphile YouTube channel I saw a video that gave a proof that all complex polynomials have at least one solution. The method involved moving one point about the complex plane while another is mapped around a path elsewhere in the plane. I decided to map this out using Javascript!

Overview

The proof states that for a reduced polynomial $$f$$ of order $$n$$, for values of $$u = re^{i\theta} \in \mathcal{C}, |r| \gg 1$$, the function $$z = f(u)$$, $$z\in\mathcal{C}$$ will take values of the order $$|z| \sim r^n$$. The value of $$r$$ is then slowly and smoothly reduced, as $$\theta$$ is allowed to increase at a constant rate. After many iterations the value of $$z$$ will tend towards a constant value, which is the constant term in the polynomial. As a result the path traced out by $$z$$ will pass through the origin, giving at least one solution to the polynomial.

This is handled in Javascript by making finite steps in $$r$$ and $$\theta$$, with small steps in time. Both paths are animated, to show the behaviour of $$z$$ as a function of $$r$$ and $$\theta$$. The results can be quite beautiful!

Challenges

Challenge: This project requires timed Javascript and complex number manipulationn.
Solution: Both these problems have been overcome many times before in other projects, so they weren’t a problem here! (Resolved)