2021-11-25 15:31:10 +00:00
/* Imports */
import Head from 'next/head'
import React , { useState } from "react" ;
2021-12-08 11:35:18 +00:00
import { DrawGraph , removeOldSvg } from './labeledGraph' ;
2021-11-25 15:49:58 +00:00
import { SubmitSliderButton } from "./slider" ;
2021-11-25 15:31:10 +00:00
import { DisplayElement } from './displayElement'
import { DisplayAsMarkdown } from './displayAsMarkdown'
2021-12-08 10:34:28 +00:00
import { CreateTable , buildRows } from './findPaths'
2021-12-08 11:59:17 +00:00
import { DataSetChanger } from "./datasetChanger"
2021-11-25 15:31:10 +00:00
import { pushToMongo } from "./pushToMongo.js"
2021-12-08 11:59:17 +00:00
import { increasingList , maxMergeSortSteps , expectedNumMergeSortSteps } from "./utils.js"
2021-11-25 15:31:10 +00:00
2021-12-07 19:40:43 +00:00
/* DEFINTIONS */
2021-12-08 11:59:17 +00:00
const DEFAULT _COMPARE = ( ) => 1 // 1/Math.random() - 1
2021-12-07 19:40:43 +00:00
2021-12-08 11:59:17 +00:00
/* Misc. helpers */
2021-11-25 15:31:10 +00:00
let buildLinks = quantitativeComparisons => quantitativeComparisons . map ( ( [ element1 , element2 , distance , reasoning ] ) => ( { source : element1 , target : element2 , distance : distance , reasoning : reasoning } ) )
Array . prototype . containsArray = function ( val ) {
var hash = { } ;
for ( var i = 0 ; i < this . length ; i ++ ) {
hash [ this [ i ] ] = i ;
}
return hash . hasOwnProperty ( val ) ;
}
let checkIfListIsOrdered = ( arr , binaryComparisons ) => {
let l = arr . length
let isOrdered = true
for ( let i = 0 ; i < l - 1 ; i ++ ) {
isOrdered = isOrdered && binaryComparisons . containsArray ( [ arr [ i ] , arr [ i + 1 ] ] )
}
return isOrdered
}
let nicelyFormatLinks = ( quantitativeComparisons , listOfElements ) => quantitativeComparisons . map ( ( [ element1 , element2 , distance , reasoning ] ) => ( { source : listOfElements [ element1 ] . name , target : listOfElements [ element2 ] . name , distance : distance , reasoning : reasoning } ) )
// Main react component
export default function ComparisonView ( { listOfElementsForView } ) {
2021-12-08 11:59:17 +00:00
/* State */
// initial state values
2021-11-25 15:31:10 +00:00
let initialListOfElements = listOfElementsForView . map ( ( element , i ) => ( { ... element , id : i } ) )
let initialPosList = increasingList ( listOfElementsForView . length ) // [0,1,2,3,4]
let initialComparePair = [ initialPosList [ initialPosList . length - 2 ] , initialPosList [ initialPosList . length - 1 ] ]
let initialSliderValue = 1
let initialReasoning = ''
let initialBinaryComparisons = [ ]
let initialQuantitativeComparisons = [ ]
let initialIsListOdered = false
let initialOrderedList = [ ]
let initialShowAdvancedOptions = false
let initialShowComparisons = false
let initialShowChangeDataSet = false
2021-11-28 15:33:19 +00:00
let initialNumSteps = 0 ;
2021-11-28 15:46:54 +00:00
let initialMaxSteps = maxMergeSortSteps ( listOfElementsForView . length )
let initialExpectedSteps = expectedNumMergeSortSteps ( listOfElementsForView . length )
2021-12-08 10:34:28 +00:00
let initialTableRows = [ ]
2021-12-08 15:46:17 +00:00
let initialDontPushSubmitButtonAnyMore = false
2021-11-25 15:31:10 +00:00
2021-12-08 11:59:17 +00:00
// state variables and functions
2021-11-25 15:31:10 +00:00
const [ listOfElements , setListOfElements ] = useState ( initialListOfElements )
const [ posList , setPosList ] = useState ( initialPosList )
const [ toComparePair , setToComparePair ] = useState ( initialComparePair )
const [ sliderValue , setSliderValue ] = useState ( initialSliderValue )
const [ reasoning , setReasoning ] = useState ( initialReasoning )
const [ binaryComparisons , setBinaryComparisons ] = useState ( initialBinaryComparisons )
const [ quantitativeComparisons , setQuantitativeComparisons ] = useState ( initialQuantitativeComparisons ) // More expressive, but more laborious to search through. For the ordering step, I only manipulate the binaryComparisons.
const [ isListOrdered , setIsListOrdered ] = useState ( initialIsListOdered )
const [ orderedList , setOrderedList ] = useState ( initialOrderedList )
2021-12-08 15:46:17 +00:00
const [ dontPushSubmitButtonAnyMore , setDontPushSubmitButtonAnyMore ] = useState ( initialDontPushSubmitButtonAnyMore )
2021-11-25 15:31:10 +00:00
let [ showAdvancedOptions , changeShowAdvanceOptions ] = useState ( initialShowAdvancedOptions ) ;
let [ showComparisons , changeShowComparisons ] = useState ( initialShowComparisons ) ;
let [ showChangeDataSet , changeshowChangeDataSet ] = useState ( initialShowChangeDataSet ) ;
2021-12-07 19:40:43 +00:00
let [ numSteps , changeNumSteps ] = useState ( initialNumSteps ) ;
2021-11-28 15:46:54 +00:00
let [ maxSteps , changeMaxSteps ] = useState ( initialMaxSteps )
let [ expectedSteps , changeExpectedSteps ] = useState ( initialExpectedSteps )
2021-12-08 10:34:28 +00:00
let [ tableRows , setTableRows ] = useState ( initialTableRows )
2021-11-25 15:31:10 +00:00
2021-12-08 11:59:17 +00:00
/* Convenience utils: restart + changeDataSet */
2021-11-25 15:31:10 +00:00
let restart = ( posList ) => {
setToComparePair ( [ posList [ posList . length - 2 ] , posList [ posList . length - 1 ] ] )
setSliderValue ( initialSliderValue )
setBinaryComparisons ( initialBinaryComparisons )
setQuantitativeComparisons ( initialQuantitativeComparisons )
setIsListOrdered ( initialIsListOdered )
setOrderedList ( initialOrderedList )
2021-12-08 10:56:20 +00:00
changeNumSteps ( initialNumSteps )
2021-11-25 15:31:10 +00:00
removeOldSvg ( )
2021-12-08 10:56:20 +00:00
setTableRows ( initialTableRows )
2021-12-08 15:46:17 +00:00
setDontPushSubmitButtonAnyMore ( initialDontPushSubmitButtonAnyMore )
2021-11-25 15:31:10 +00:00
}
let changeDataSet = ( listOfElementsNew ) => {
listOfElementsNew =
listOfElementsNew . map ( ( element , i ) => ( { ... element , id : i } ) )
let newPosList = increasingList ( listOfElementsNew . length )
2021-11-28 15:46:54 +00:00
let newListLength = listOfElementsNew . length
2021-11-25 15:31:10 +00:00
setListOfElements ( listOfElementsNew )
setPosList ( increasingList ( listOfElementsNew . length ) )
setToComparePair ( [ newPosList [ newPosList . length - 2 ] , newPosList [ newPosList . length - 1 ] ] )
2021-11-28 15:46:54 +00:00
changeExpectedSteps ( expectedNumMergeSortSteps ( newListLength ) )
changeMaxSteps ( maxMergeSortSteps ( newListLength ) )
2021-11-25 15:31:10 +00:00
restart ( newPosList )
}
// Manipulations
let compareTwoElements = ( newBinaryComparisons , element1 , element2 ) => {
let element1Greater = newBinaryComparisons . containsArray ( [ element1 , element2 ] )
let element2Greater = newBinaryComparisons . containsArray ( [ element2 , element1 ] )
if ( element1Greater || element2Greater ) {
return element1Greater && ! element2Greater
} else {
setToComparePair ( [ element1 , element2 ] )
//console.log(`No comparison found between ${element1} and ${element2}`)
//console.log(`Comparisons:`)
//console.log(JSON.stringify(newBinaryComparisons, null, 4));
return "No comparison found"
}
}
function merge ( newBinaryComparisons , left , right ) {
let sortedArr = [ ] ; // the sorted elements will go here
while ( left . length && right . length ) {
// insert the biggest element to the sortedArr
let comparison = compareTwoElements ( newBinaryComparisons , left [ 0 ] , right [ 0 ] )
if ( comparison == "No comparison found" ) {
return "No comparison found; unable to proceed"
}
else if ( comparison ) { // left[0] > right[0]
sortedArr . push ( left . shift ( ) ) ;
} else {
sortedArr . push ( right . shift ( ) ) ;
}
}
// use spread operator and create a new array, combining the three arrays
return [ ... sortedArr , ... left , ... right ] ; // if they don't have the same size, the remaining ones will be greater than the ones before
}
function mergeSort ( { array , comparisons } ) {
if ( array == "No comparison found; unable to proceed" ) {
return "No comparison found; unable to proceed"
}
const half = array . length / 2 ;
// the base case is array length <=1
if ( array . length <= 1 ) {
return array ;
}
const left = array . slice ( 0 , half ) ; // the first half of the array
const right = array . slice ( half , array . length ) // Note that splice is destructive.
let orderedFirstHalf = mergeSort ( { array : left , comparisons } )
let orderedSecondHalf = mergeSort ( { array : right , comparisons } )
if ( orderedFirstHalf != "No comparison found; unable to proceed" && orderedSecondHalf != "No comparison found; unable to proceed" ) {
let result = merge ( comparisons , orderedFirstHalf , orderedSecondHalf ) ;
return result
} else {
return "No comparison found; unable to proceed"
}
}
let nextStepSimple = ( posList , binaryComparisons , element1 , element2 ) => {
//console.log("Binary comparisons: ")
//console.log(JSON.stringify(binaryComparisons, null, 4));
let newComparison = [ element1 , element2 ] // [element1, element2]
let newBinaryComparisons = [ ... binaryComparisons , newComparison ]
//console.log("New binaryComparisons: ")
//console.log(JSON.stringify(newBinaryComparisons, null, 4));
setBinaryComparisons ( newBinaryComparisons )
let result = mergeSort ( { array : posList , comparisons : newBinaryComparisons } )
//console.log(result)
if ( result != "No comparison found; unable to proceed" && checkIfListIsOrdered ( result , newBinaryComparisons ) ) {
// console.log(`isListOrdered: ${isListOrdered}`)
console . log ( "poslist@nextStepSimple" )
console . log ( posList )
console . log ( "result@nextStepSimple" )
console . log ( result )
2021-12-07 17:40:17 +00:00
return [ true , result ]
2021-11-25 15:31:10 +00:00
} else {
2021-12-07 17:40:17 +00:00
return [ false , result ]
2021-11-25 15:31:10 +00:00
}
}
2021-12-08 15:46:17 +00:00
let nextStepSlider = async ( { posList , binaryComparisons , sliderValue , reasoning , element1 , element2 } ) => {
if ( ! dontPushSubmitButtonAnyMore ) {
if ( sliderValue < 1 && sliderValue > 0 ) {
sliderValue = 1 / sliderValue ;
[ element1 , element2 ] = [ element2 , element1 ]
}
console . log ( ` posList@nextStepSlider: ` )
console . log ( posList )
let [ successStatus , result ] = nextStepSimple ( posList , binaryComparisons , element1 , element2 )
let newQuantitativeComparison = [ element1 , element2 , sliderValue , reasoning ]
let newQuantitativeComparisons = [ ... quantitativeComparisons , newQuantitativeComparison ]
setQuantitativeComparisons ( newQuantitativeComparisons )
setSliderValue ( DEFAULT _COMPARE ( ) )
setReasoning ( '' )
changeNumSteps ( numSteps + 1 )
if ( successStatus ) {
setDontPushSubmitButtonAnyMore ( true )
let jsObject = nicelyFormatLinks ( quantitativeComparisons , listOfElements )
await pushToMongo ( jsObject )
console . log ( jsObject )
alert ( "Comparisons completed. Background work might take a while, or straight-out fail" )
2021-12-07 17:40:17 +00:00
setIsListOrdered ( true )
setOrderedList ( result )
2021-12-08 11:59:17 +00:00
await buildRows ( {
isListOrdered : true ,
orderedList : result ,
listOfElements ,
links : buildLinks ( newQuantitativeComparisons ) ,
rows : tableRows ,
setTableRows
} )
2021-12-08 15:46:17 +00:00
/ *
setTimeout ( async ( ) => {
// Make sure to do this after the
2021-12-08 11:59:17 +00:00
2021-12-08 15:46:17 +00:00
} , 100 ) ;
* /
}
2021-11-25 15:31:10 +00:00
}
2021-12-08 15:46:17 +00:00
2021-11-25 15:31:10 +00:00
}
// Html
return (
< div className = "flex flex-col items-center justify-center min-h-screen py-2" >
2021-12-08 15:46:17 +00:00
2021-12-08 11:59:17 +00:00
{ /* Webpage name & favicon */ }
2021-11-25 15:31:10 +00:00
< div className = "mt-20" >
< Head >
< title > Utility Function Extractor < / t i t l e >
< link rel = "icon" href = "/favicon.ico" / >
< / H e a d >
< / d i v >
< main className = "flex flex-col items-center w-full flex-1 px-20 text-center" >
2021-12-08 15:46:17 +00:00
2021-12-08 11:59:17 +00:00
{ /* Heading */ }
2021-11-25 15:31:10 +00:00
< h1 className = "text-6xl font-bold" >
Utility Function Extractor
< / h 1 >
2021-12-08 11:59:17 +00:00
{ /* Approximate progress indicator */ }
2021-12-08 15:46:17 +00:00
2021-11-28 15:33:19 +00:00
< p > { ` ${ numSteps } out of ~ ${ expectedSteps } (max ${ maxSteps } ) comparisons ` } < / p >
2021-11-25 15:31:10 +00:00
2021-12-08 11:59:17 +00:00
{ /* Comparison section */ }
2021-12-08 12:49:29 +00:00
< div className = { isListOrdered ? "hidden" : "" } >
2021-11-25 15:31:10 +00:00
< div className = "flex flex-wrap items-center max-w-4xl sm:w-full mt-10" >
2021-12-08 15:46:17 +00:00
2021-12-08 11:59:17 +00:00
{ /* Element 1 */ }
< div className = "flex m-auto border-gray-300 border-4 h-72 w-72 p-5" >
2021-11-25 15:31:10 +00:00
< div className = "block m-auto text-center" >
< DisplayElement element = { listOfElements [ toComparePair [ 0 ] ] } >
< / D i s p l a y E l e m e n t >
< / d i v >
< / d i v >
2021-12-08 11:59:17 +00:00
{ /* Comparison actuator (text, previously slider) */ }
2021-11-25 15:31:10 +00:00
< div className = "flex m-auto w-72" >
< div className = "block m-auto text-center" >
< br / >
< label >
{ ` ... is ` }
< br / >
2021-11-28 15:33:19 +00:00
< input
type = "number"
className = "text-center text-blueGray-600 bg-white rounded text-lg border-0 shadow outline-none focus:outline-none focus:ring w-8/12 h-10 m-2"
value = { sliderValue }
onChange = { ( event ) => {
//console.log(event)
//console.log(event.target.value)
setSliderValue ( event . target . value )
} }
/ >
2021-11-25 15:31:10 +00:00
< br / >
{ ` times as valuable as ... ` }
< / l a b e l >
< br / >
< SubmitSliderButton
posList = { posList }
binaryComparisons = { binaryComparisons }
sliderValue = { sliderValue }
reasoning = { reasoning }
toComparePair = { toComparePair }
nextStepSlider = { nextStepSlider }
2021-12-08 15:46:17 +00:00
dontPushSubmitButtonAnyMore = { dontPushSubmitButtonAnyMore }
2021-11-25 15:31:10 +00:00
/ >
< / d i v >
< / d i v >
2021-12-08 15:46:17 +00:00
2021-12-08 11:59:17 +00:00
{ /* Element 2 */ }
< div className = "flex m-auto border-gray-300 border-4 h-72 w-72 p-5" >
2021-11-25 15:31:10 +00:00
< div className = "block m-auto text-center" >
< DisplayElement element = { listOfElements [ toComparePair [ 1 ] ] } >
< / D i s p l a y E l e m e n t >
< / d i v >
< / d i v >
< / d i v >
< br / >
2021-11-25 15:56:48 +00:00
2021-11-25 15:31:10 +00:00
< label className = "" >
2021-11-25 15:56:48 +00:00
Reasoning ( optional ) :
< textarea className = "mt-2 px-3 py-4 placeholder-blueGray-300 text-blueGray-600 relative bg-white bg-white rounded text-base border-0 shadow outline-none focus:outline-none focus:ring w-full"
2021-11-28 15:33:19 +00:00
value = { reasoning }
onChange = { ( event ) => setReasoning ( event . target . value ) }
2021-11-25 15:31:10 +00:00
/ >
< / l a b e l >
< br / >
< div >
< / d i v >
2021-12-08 11:59:17 +00:00
{ / * O l d s l i d e r e l e m e n t ( p r e v i o u s a c t u a t o r )
2021-11-25 15:31:10 +00:00
< div className = { ` flex row-start-3 row-end-3 col-start-1 col-end-4 md:col-start-3 md:col-end-3 md:row-start-1 md:row-end-1 lg:col-start-3 lg:col-end-3 lg:row-start-1 lg:row-end-1 items-center justify-center mb-4 mt-10 ${ isListOrdered ? "hidden" : "" } ` } >
< SliderElement
className = "flex items-center justify-center"
onChange = { ( event ) => ( setSliderValue ( event [ 0 ] ) ) }
value = { sliderValue }
displayFunction = { displayFunctionSlider }
domain = { domain }
/ >
< / d i v >
2021-12-08 11:59:17 +00:00
* / }
2021-11-25 15:31:10 +00:00
< / d i v >
2021-12-08 11:59:17 +00:00
{ /* Results section */ }
< div >
{ /* Graph */ }
< DrawGraph
2021-11-25 15:31:10 +00:00
isListOrdered = { isListOrdered }
orderedList = { orderedList }
listOfElements = { listOfElements }
2021-12-08 11:59:17 +00:00
links = { buildLinks ( quantitativeComparisons ) } >
< / D r a w G r a p h >
{ /* Comparison table */ }
2021-12-08 12:49:29 +00:00
< div className = { isListOrdered ? "inline items-center text-center mt-10" : "hidden" } >
2021-12-08 11:59:17 +00:00
< CreateTable
isListOrdered = { isListOrdered }
orderedList = { orderedList }
listOfElements = { listOfElements }
links = { buildLinks ( quantitativeComparisons ) }
tableRows = { tableRows }
setTableRows = { setTableRows }
>
< / C r e a t e T a b l e >
< / d i v >
2021-11-25 15:31:10 +00:00
< / d i v >
2021-12-08 15:46:17 +00:00
2021-12-08 11:59:17 +00:00
{ /* Convenience functions */ }
2021-11-25 15:31:10 +00:00
< div className = "w-2/12 flex justify-center mt-10" >
< button
className = "text-gray-500 text-sm"
onClick = { ( ) => changeShowAdvanceOptions ( ! showAdvancedOptions ) }
>
Advanced options ▼
< / b u t t o n >
< / d i v >
2021-12-08 15:46:17 +00:00
< div className = { showAdvancedOptions ? "flex flex-wrap -mx-4 overflow-hidden" : "hidden" } >
2021-12-08 11:59:17 +00:00
{ /* Button: Restart */ }
2021-11-25 15:31:10 +00:00
< div className = "my-4 px-4 w-1/3 overflow-hidden" >
< button
className = "bg-transparent hover:bg-blue-500 text-blue-700 font-semibold hover:text-white py-2 px-4 border border-blue-500 hover:border-transparent rounded mt-5"
onClick = { ( ) => restart ( posList ) } >
Restart
< / b u t t o n >
< / d i v >
2021-12-08 11:59:17 +00:00
{ /* Button: Show comparisons */ }
2021-11-25 15:31:10 +00:00
< div className = "my-4 px-4 w-1/3 overflow-hidden" >
< button
className = "bg-transparent hover:bg-blue-500 text-blue-700 font-semibold hover:text-white py-2 px-4 border border-blue-500 hover:border-transparent rounded mt-5"
onClick = { ( ) => changeShowComparisons ( ! showComparisons ) } >
Show comparisons
< / b u t t o n >
< / d i v >
2021-12-08 11:59:17 +00:00
{ /* Button: Change dataset */ }
2021-11-25 15:31:10 +00:00
< div className = "my-4 px-4 w-1/3 overflow-hidden" >
< button
className = "bg-transparent hover:bg-blue-500 text-blue-700 font-semibold hover:text-white py-2 px-4 border border-blue-500 hover:border-transparent rounded mt-5"
onClick = { ( ) => changeshowChangeDataSet ( ! showChangeDataSet ) } >
Use your own data
< / b u t t o n >
< / d i v >
< / d i v >
2021-12-08 11:59:17 +00:00
{ /* Change dataset section */ }
2021-12-08 12:49:29 +00:00
< div className = { showChangeDataSet ? "inline mt-5" : "hidden" } >
2021-12-08 11:59:17 +00:00
< DataSetChanger handleSubmit = { changeDataSet } / >
2021-11-25 15:31:10 +00:00
< / d i v >
2021-12-08 11:59:17 +00:00
{ /* Show comparisons section */ }
2021-12-08 12:49:29 +00:00
< div className = { showComparisons ? "inline mt-5" : "hidden" } >
2021-11-25 15:31:10 +00:00
< h2 > Comparisons < / h 2 >
< div className = "text-left" >
< DisplayAsMarkdown
markdowntext = { JSON . stringify ( nicelyFormatLinks ( quantitativeComparisons , listOfElements ) , null , 4 ) }
className = { "" } >
< / D i s p l a y A s M a r k d o w n >
< / d i v >
< / d i v >
< / m a i n >
< / d i v >
)
}