Linker Errors

This is a discussion on Linker Errors within the Game Programming forums, part of the General Programming Boards category; Hi, i am on lesson 1 on NeHe, and i am having some problems. There are 15 errors and i ...

  1. #1
    Sanity is for the weak! beene's Avatar
    Join Date
    Jul 2006
    Posts
    321

    Linker Errors

    Hi, i am on lesson 1 on NeHe, and i am having some problems.
    There are 15 errors and i have no idea how to correct them.
    Can anyone help me?
    Here is the code:

    Code:
    #include <windows.h>       /*Header file for Windows*/
    #include <gl\gl.h>         /*Header file for OpenGL32 Library*/
    #include <gl\glu.h>        /*Header file for GLu32 Library*/
    #include <gl\glaux.h>      /*Header file for GLaux Library*/
    
    HGLRC hRC = NULL;          /*Permanent Rendering Context*/  
    HDC hDC = NULL;            /*Private GDI Device Context*/
    HWND hWnd = NULL;          /*Holds your Window handle*/
    HINSTANCE hInstance;     /*Holds the Instance of the App*/
    
    bool keys[256];            /*Array used for the Keyboard Routine*/ 
    bool active = TRUE;        /*Window active flag set to true*/
    bool fullscreen = TRUE;    /*Fullscreen flag set to true*/
    
    LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM); /*Declaration of 
    													  the Windows Procedure*/
    
    GLvoid ReSizeGLScene(GLsizei width, GLsizei height) /*Resize and Initialize the GL window*/
    {
    	if(height==0)      /*Prevent a divide by Zero by...*/
    	{
    		height = 1;    /*...Making height equal to 1*/
    	}
    
    	glViewport(0, 0, width, height);  /*Reset the current viewport*/
    	
    	glMatrixMode(GL_PROJECTION);      /*Select the Projection Matrix*/
    	glLoadIdentity();                 /*Reset the Projection Matrix*/
        
    	/*Calculate the aspect ration of the Window*/
    	gluPerspective(45.0f,(GLfloat)width/(GLfloat)height,0.1f,100.0f);
    
    	glMatrixMode(GL_MODELVIEW);       /*Select the Model View Matrix*/
    	glLoadIdentity();                 /*Reset the Model View Matrix*/
    }
    
    int InitGL(GLvoid)                    /*The setup for OpenGL goes here*/
    {
    	glShadeModel(GL_SMOOTH);          /*Enable the Smooth Shading*/
    	glClearColor(0.0f, 0.0f, 0.0f, 0.0f); /*Black Background*/
    	glClearDepth(1.0f);               /*Depth Buffer Setup*/
    	glEnable(GL_DEPTH_TEST);          /*Enables Depth Testing*/
    	glDepthFunc(GL_LEQUAL);           /*The Type of Depth Test to do*/
    	/*Really Nice Perspective Calculations*/
    	glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST);
    
    	return TRUE;   /*Initialization went ok*/
    }
    
    int DrawGLScene(GLvoid)     /*Here's where we do all of the drawing*/
    {   
    	/*Clear the Screen and Depth Buffer*/
    	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    	glLoadIdentity();       /*Reset the Current Model View Matrix*/
    	return TRUE;            /*Everything went ok*/
    }
    
    GLvoid KillGLWindow(GLvoid)   /*Properly Kill the Window*/
    {
    	if (fullscreen)         /*Are we in fullscreen mode?*/
    	{
    		ChangeDisplaySettings(NULL, 0);   /*If so, Switch back to the normal Desktop*/
    		ShowCursor(TRUE);   /*Show the Cursor*/
    	}
    
    	if (hRC)                /*Do we have a rendering Context?*/
    	{
    		if (!wglMakeCurrent(NULL, NULL)); /*Are we able to release the DC and RC Contexts*/
    		{
    			/*If not, this Message Should Appear*/
                MessageBox(NULL,"Release Of DC And RC Failed.","SHUTDOWN ERROR",MB_OK | MB_ICONINFORMATION);
    		}
    		if (!wglDeleteContext(hRC));    /*Are we able to Delete the RC?*/
    		{
    		    /*If not, this Message Should Appear*/
                MessageBox(NULL,"Release Rendering Context Failed.","SHUTDOWN ERROR",MB_OK | MB_ICONINFORMATION);
    		}
    		hRC = NULL;         /*Set RC to NULL*/
    	}
    
    	if (hWnd && !DestroyWindow(hWnd))   /*Are we able to destroy the window?*/
    	{
    		/*If not, this Message should Appear*/
            MessageBox(NULL,"Could Not Release hWnd.","SHUTDOWN ERROR",MB_OK | MB_ICONINFORMATION);
    		hWnd = NULL;        /*Set hWnd to NULL*/
    	}
    
    	if (!UnregisterClass("OpenGL", hInstance)) /*Are we able to Unregister the Class?*/
    	{
    		/*If not, this Message Should Appear*/
            MessageBox(NULL,"Could Not Unregister Class.","SHUTDOWN ERROR",MB_OK | MB_ICONINFORMATION);
    		hInstance = NULL;   /*Set hInstance to NULL*/
    	}
    }
    
    BOOL CreateGLWindow(char* title, int width, int height, int bits, bool fullscreenflag)
    {
    	GLuint PixelFormat;    /*Holds the Results after Searching for a Match*/
    	WNDCLASS wc;           /*Class Window Structure*/
    
    	DWORD dwExStyle;       /*Window Extended Style*/
    	DWORD dwStyle;         /*Window Style*/
    
    	RECT WindowRect;       /*Grabs Rectangle Upper Left/Lower Right Values*/
    	WindowRect.left=(long)0;        /*Set Value to Zero*/
    	WindowRect.right=(long)width;   /*Set right Value to Requested width*/
    	WindowRect.top=(long)0;         /*Set Top Value to Zero*/
    	WindowRect.bottom=(long)height;  /*Set the Bottom Value to the requested height*/
    
    	fullscreen=fullscreenflag;      /*Set the Global fullscreen flag*/
    
    	hInstance = GetModuleHandle(NULL);             /*Grab an Instance for our Window*/
    	wc.style = CS_HREDRAW | CS_HREDRAW | CS_OWNDC; /*Redraw on move, and own DC for Window*/
    	wc.lpfnWndProc = (WNDPROC) WndProc;            /*Wnd Proc Handles Messages*/
    	wc.cbClsExtra = 0;                             /*No Extra Window Data*/
    	wc.cbWndExtra = 0;                             /*No Extra Window Data*/
    	wc.hInstance = hInstance;                      /*Set the Instance*/
    	wc.hIcon = LoadIcon(NULL, IDI_WINLOGO);        /*Load the Default Icon*/
    	wc.hCursor = LoadCursor(NULL, IDC_ARROW);      /*Load the Default Cursor*/
    	wc.hbrBackground = NULL;                       /*No Background Required for GL*/
    	wc.lpszMenuName = NULL;                        /*We don't want a Menu*/
    	wc.lpszClassName = "OpenGL";                   /*Set the Class Name*/
    
    	if (!RegisterClass(&wc))                       /*Attempt to Register the Window Class*/
    	{
    		/*If it fails, this Message should Appear*/
            MessageBox(NULL,"Failed To Register The Window Class.","ERROR",MB_OK|MB_ICONEXCLAMATION);
    		return FALSE;                              /*Exit and Return False*/
    	}
    
    	if (fullscreen)                   /*Attempt Fullscreen Mode?*/
    	{
    		DEVMODE dmScreenSettings;     /*Device Mode*/                                
    		memset(&dmScreenSettings, 0, sizeof(dmScreenSettings));/*Makes sure that the Memory is cleared*/
    		dmScreenSettings.dmSize = sizeof(dmScreenSettings); /*Size of the DevMode structure*/
    		dmScreenSettings.dmPaperWidth = width;  /*Selected Screen Width*/
    		dmScreenSettings.dmPelsHeight = height; /*Selected Screen Height*/
    		dmScreenSettings.dmBitsPerPel = bits;   /*Selected Bits Per Pixel*/
    		dmScreenSettings.dmFields = DM_BITSPERPEL |DM_PELSWIDTH | DM_PELSHEIGHT;
    
    		/*Try to Set Selected Mode and get Results. NOTE... CDS_FULLSCREEN gets rid of the Status Bar*/
            if (ChangeDisplaySettings(&dmScreenSettings,CDS_FULLSCREEN)!=DISP_CHANGE_SUCCESSFUL)
    		{
    			/*If the Mode Fails, Offer two Options. Quit or rune in a Window*/
                if (MessageBox(NULL,"The Requested Fullscreen Mode Is Not Supported By\nYour Video Card. Use Windowed Mode Instead?","Fullscreen",MB_YESNO|MB_ICONEXCLAMATION)==IDYES)
    			{
    				fullscreen = FALSE;  /*Select Windowed Mode*/
    			}
    
    			else
    			{
    				/*Popup a Message box to let th user know that the program is closing*/
                    MessageBox(NULL,"Program Will Now Close.","ERROR",MB_OK|MB_ICONSTOP);
    				return FALSE;  /*Exitand return FALSE*/
    			}
    		}
    	}
     
    	if (fullscreen)                      /*Are we in fullscreen mode?*/
    	{
    		dwExStyle = WS_EX_APPWINDOW | WS_EX_WINDOWEDGE;     /*Windows Extended Style*/
    		dwStyle = WS_OVERLAPPEDWINDOW;   /*Windows Style*/
    	}
    
    	AdjustWindowRectEx(&WindowRect, dwStyle, FALSE, dwExStyle); /*Adjust Window to True Requested size*/
    
    	if (!(hWnd = CreateWindowEx(dwExStyle,
    		                        "OpenGL",     /*Class Name*/
    								title,        /*Title Name*/
    								WS_CLIPSIBLINGS |  /*Required Window Style*/
    								WS_CLIPCHILDREN |  /*Required Window Style*/
    								dwStyle,      /*Selected Window Style*/
    								0, 0,         /*Window Position*/
    								WindowRect.right-WindowRect.left, /*Calculate Adjusted Window Width*/
    								WindowRect.bottom-WindowRect.top, /*Calculate Adjusted Window Height*/
    								NULL,         /*No Parent Window*/
    								NULL,         /*No Menu*/
    								hInstance,    /*hInstance*/
    								NULL)))       /*Don't Pass anything to WM_CREATE*/
    
    	{
    		KillGLWindow();     /*Reset the Display*/
            MessageBox(NULL,"Window Creation Error.","ERROR",MB_OK|MB_ICONEXCLAMATION);
    		return FALSE;       /*Return FALSE*/
    	}
    
    	static PIXELFORMATDESCRIPTOR pfd=    /*pfd tells Windows how we want things to be*/
    	{
    		sizeof(PIXELFORMATDESCRIPTOR),   /*Size of this Pixel Format Descriptor*/
    		1,                               /*Version Number*/
    		PFD_DRAW_TO_WINDOW |             /*Format must support Window*/
    		PFD_SUPPORT_OPENGL |             /*Format must support OpenGL*/
    		PFD_DOUBLEBUFFER,                /*Must support Double Buffering*/
    		PFD_TYPE_RGBA,                   /*Request an RGBA Format*/
    		bits,                            /*Select our Colour depth*/
    		0, 0, 0, 0, 0, 0,                /*Color Bits ignored*/
    		0,                               /*No Alpha Buffer*/
    		0,                               /*Shift Bit Ignored*/
    		0,                               /*No Accumulation Buffer*/
    		0, 0, 0, 0,                      /*Accumulation Bits Ignored*/
    		16,                              /*16Bit Z-Buffer (Depth Buffer)*/
    		0,                               /*No Stencil Buffer*/
    		0,                               /*No Auxiliary Buffer*/
    		PFD_MAIN_PLANE,                  /*Main Drawing Layer*/
    		0,                               /*Reserved*/
    		0, 0, 0                          /*Layer Masks Ignored*/
    	};
    
    	if (!(PixelFormat=ChoosePixelFormat(hDC,&pfd))) /*Did Windows find a matching Pixel Format?*/
    	{
    		KillGLWindow();     /*Reset the Display*/
            MessageBox(NULL,"Can't Find A Suitable PixelFormat.","ERROR",MB_OK|MB_ICONEXCLAMATION);
    		return FALSE;       /*Return FALSE*/
    	}
    
    	if (!SetPixelFormat(hDC,PixelFormat,&pfd))      /*Are we able to set the Pixel Format?*/
    	{
    		KillGLWindow();     /*Reset the Display*/
            MessageBox(NULL,"Can't Set The PixelFormat.","ERROR",MB_OK|MB_ICONEXCLAMATION);
    		return FALSE;       /*Return FALSE*/
    	}
    
    	if (!(hRC=wglCreateContext(hDC)))     /*Are we able to get a Rendering Context?*/
    	{
    		KillGLWindow();     /*Reset the Display*/
            MessageBox(NULL,"Can't Create A GL Rendering Context.","ERROR",MB_OK|MB_ICONEXCLAMATION);
    		return FALSE;       /*Return FALSE*/
    	}
    
    	if (!wglMakeCurrent(hDC,hRC))     /*Try to Activate the Rendering Context*/
    	{
    		KillGLWindow();     /*Reset the Display*/
            MessageBox(NULL,"Can't Activate The GL Rendering Context.","ERROR",MB_OK|MB_ICONEXCLAMATION);
    		return FALSE;       /*Return FALSE*/
    	}
    
    	ShowWindow(hWnd, SW_SHOW);     /*Show the Window*/
    	SetForegroundWindow(hWnd);     /*Slightlyhigher Priority*/
    	SetFocus(hWnd);                /*Sets Keyboard Focus to the Window*/
    	ReSizeGLScene(width, height);  /*Set up our Perspective GL Screen*/
    
    	if (!InitGL())          /*Initialize our newly created GL Window*/
    	{
    		KillGLWindow();     /*Reset the Display*/
            MessageBox(NULL,"Initialization Failed.","ERROR",MB_OK|MB_ICONEXCLAMATION);
    		return FALSE;        /*Return FALSE*/
    	}
    	return TRUE;            /*Success*/
    }
    
    LRESULT CALLBACK WndProc(HWND hWnd,      /*Handle for this Window*/
    						 UINT msg,       /*Message for this Window*/
    						 WPARAM wParam,  /*Additional Message info*/
    						 LPARAM lParam)  /*Additional Message info*/
    {
    	switch(msg)               /*Check for Windows Messages*/
    	{
    	    case WM_ACTIVATE:     /*Watch for Window Activate Message*/
    			{
    				if (!HIWORD(wParam)) /*Check Minimization State*/
    				{
    					active = TRUE;   /*Program is Active*/
    				}
    				else
    				{
    					active = FALSE;   /*Program is inactive*/
    				}
    
    				return 0;     /*Return to the Message Loop*/
    			}
    		case WM_SYSCOMMAND:   /*Intercept System Commands*/
    			{
    				switch (wParam)       /*Check System Calls*/
    				{
    				    case SC_SCREENSAVE:   /*Screen Saver trying to start?*/
    				    case SC_MONITORPOWER: /*Monitor trying to enter Power Saving Mode?*/
    				    return 0;     /*Prevent this from happening*/
    				}
    				break;
    			}
    		case WM_CLOSE:     /*Did we Receive a Closing Message?*/
    			{
    				PostQuitMessage(0);       /*Send a Quit Message*/
    				return 0;  /*Jump back*/
    			}
    		case WM_KEYDOWN:   /*Is a key being held down?*/
    			{
    				keys[wParam] = TRUE;     /*If so, mark it as TRUE*/
    				return 0;  /*Jump back*/
    			}
    		case WM_KEYUP:     /*Is a key being released?*/
    			{
    				keys[wParam] = FALSE;    /*If so, mark it as FALSE*/
    				return 0;  /*Jump back*/
    			}
    		case WM_SIZE:                    /*Resize the OpenGL Window*/
    			{
                    ReSizeGLScene(LOWORD(lParam),HIWORD(lParam)); /*LOWORD = width, HIWORD = height*/
    				return 0;  /*Jump back*/
    			}
    	}
    
    	/*Pass all unhandled Messages to DefWndProc*/
        return DefWindowProc(hWnd,msg,wParam,lParam);
    }
    
    int WINAPI WinMain(HINSTANCE hInstance,     /*Instance*/
    				   HINSTANCE hPrevInstance, /*Previous Instance*/
    				   LPSTR lpCmdLine,         /*Command line Parameters*/
    				   int nCmdShow)            /*Window Show State*/
    {
    	MSG msg;          /*Windows Message Structure*/
    	BOOL done=FALSE;  /*Bool Variable to Exit Loop*/
    
    	/*Ask the user which screen mode they would like to use*/
        if (MessageBox(NULL,"Would You Like To Run In Fullscreen Mode?", "Start FullScreen?",MB_YESNO|MB_ICONQUESTION)==IDNO)
    	{
    		fullscreen = FALSE;     /*Windowed mode*/
    	}
    
    	/*Create our OpenGL Window*/
        if (!CreateGLWindow("OpenGL Framework",640,480,16,fullscreen))
    	{
    		return 0;     /*Quit if Windows was not created*/
    	}
    	
    	while (!done)     /*Loop that runs until done = TRUE*/
    	{
    		if (PeekMessage(&msg,NULL,0,0,PM_REMOVE))  /*Is there a Message waiting*/
    		{
    			if (msg.message==WM_QUIT)     /*Have we recieved a quit Message?*/
    			{
    				done = TRUE;     /*If so, done = TRUE*/
    			}
    			else     /*If not, deal with Windows Messages*/
    			{
    				TranslateMessage(&msg);   /*Translate the Message*/
    				DispatchMessage(&msg);    /*Dispatch the Message*/
    			}
    		}
    		else     /*If there are no Messages*/
    		{
    			/*Draw the Scene. Watch for ESC Key and Quit Messages from DrawGLScene()*/
    			if (active)                  /*Program active?*/
    			{
    				if (keys[VK_ESCAPE])     /*Was the ESC Key pressed?*/
    				{
    					done = TRUE;         /*ESC Signaled a Quit*/
    				}
    				else                     /*Not time to Quit, Draw the Scene*/
    				{
    					DrawGLScene();       /*Draw the Scene*/
    					SwapBuffers(hDC);    /*Swap Buffers(Double Buffering*/
    				}
    			}
    
    			if (keys[VK_F1])             /*If so, make Key FALSE*/
    			{
    				KillGLWindow();          /*Kill our current Window*/
    				fullscreen = !fullscreen; /*Toggle fullscreen / Windowed Modes*/
    				/*Recreate our OpenGL Window*/
                    if (!CreateGLWindow("OpenGL Framework",640,480,16,fullscreen))
    				{
    					return 0;            /*Quit if Window was not Created*/
    				}
    			}
    		}
    	}
    
    	/*Shutdown*/
    	KillGLWindow();       /*Kill the Window*/
    	return (msg.wParam);  /*Exit the Program*/
    }
    And here are the Errors...

    Code:
    Main.obj : error LNK2019: unresolved external symbol _gluPerspective@32 referenced in function "void __cdecl ReSizeGLScene(int,int)" (?ReSizeGLScene@@YAXHH@Z)
    Code:
    1>Main.obj : error LNK2019: unresolved external symbol __imp__glLoadIdentity@0 referenced in function "void __cdecl ReSizeGLScene(int,int)" (?ReSizeGLScene@@YAXHH@Z)
    Code:
    1>Main.obj : error LNK2019: unresolved external symbol __imp__glMatrixMode@4 referenced in function "void __cdecl ReSizeGLScene(int,int)" (?ReSizeGLScene@@YAXHH@Z)
    Code:
    1>Main.obj : error LNK2019: unresolved external symbol __imp__glViewport@16 referenced in function "void __cdecl ReSizeGLScene(int,int)" (?ReSizeGLScene@@YAXHH@Z)
    Code:
    1>Main.obj : error LNK2019: unresolved external symbol __imp__glHint@8 referenced in function "int __cdecl InitGL(void)" (?InitGL@@YAHXZ)
    Code:
    1>Main.obj : error LNK2019: unresolved external symbol __imp__glDepthFunc@4 referenced in function "int __cdecl InitGL(void)" (?InitGL@@YAHXZ)
    Code:
    1>Main.obj : error LNK2019: unresolved external symbol __imp__glEnable@4 referenced in function "int __cdecl InitGL(void)" (?InitGL@@YAHXZ)
    Code:
    1>Main.obj : error LNK2019: unresolved external symbol __imp__glClearDepth@8 referenced in function "int __cdecl InitGL(void)" (?InitGL@@YAHXZ)
    Code:
    1>Main.obj : error LNK2019: unresolved external symbol __imp__glClearColor@16 referenced in function "int __cdecl InitGL(void)" (?InitGL@@YAHXZ)
    Code:
    1>Main.obj : error LNK2019: unresolved external symbol __imp__glShadeModel@4 referenced in function "int __cdecl InitGL(void)" (?InitGL@@YAHXZ)
    Code:
    1>Main.obj : error LNK2019: unresolved external symbol __imp__glClear@4 referenced in function "int __cdecl DrawGLScene(void)" (?DrawGLScene@@YAHXZ)
    Code:
    1>Main.obj : error LNK2019: unresolved external symbol __imp__wglDeleteContext@4 referenced in function "void __cdecl KillGLWindow(void)" (?KillGLWindow@@YAXXZ)
    Code:
    1>Main.obj : error LNK2019: unresolved external symbol __imp__wglMakeCurrent@8 referenced in function "void __cdecl KillGLWindow(void)" (?KillGLWindow@@YAXXZ)
    Code:
    1>Main.obj : error LNK2019: unresolved external symbol __imp__wglCreateContext@4 referenced in function "int __cdecl CreateGLWindow(char *,int,int,int,bool)" (?CreateGLWindow@@YAHPADHHH_N@Z)
    Code:
    1>C:\Documents and Settings\Ross\My Documents\C++\OpenGl\Starting OpenGL\Debug\Starting OpenGL.exe : fatal error LNK1120: 14 unresolved externals
    Sorry if my post was to long.

  2. #2
    CSharpener vart's Avatar
    Join Date
    Oct 2006
    Location
    Rishon LeZion, Israel
    Posts
    6,484
    Windows NT/2000: Requires Windows NT 3.5 or later.
    Windows 95/98: Requires Windows 95 or later. Available as a redistributable for Windows 95.
    Header: Declared in Glu.h.
    Library: Use Glu32.lib.
    It seems that you forgot to add the glu32.lib to your project settings
    The first 90% of a project takes 90% of the time,
    the last 10% takes the other 90% of the time.

  3. #3
    Sanity is for the weak! beene's Avatar
    Join Date
    Jul 2006
    Posts
    321
    And how would i do this? I can't find a way to "add" glu32.lib to my project settings.

  4. #4
    CSharpener vart's Avatar
    Join Date
    Oct 2006
    Location
    Rishon LeZion, Israel
    Posts
    6,484
    What IDE you are using?
    The first 90% of a project takes 90% of the time,
    the last 10% takes the other 90% of the time.

  5. #5
    Sanity is for the weak! beene's Avatar
    Join Date
    Jul 2006
    Posts
    321
    MSCV++ 2005 Express Edition

  6. #6
    Sanity is for the weak! beene's Avatar
    Join Date
    Jul 2006
    Posts
    321
    Can anyone tell me?

  7. #7
    The Right Honourable psychopath's Avatar
    Join Date
    Mar 2004
    Location
    Where circles begin.
    Posts
    1,070
    Project->Properties->Linker->Input->Additional Dependencies

    add "glu32.lib"

    EDIT:
    Actually, you're missing everything necessary to build and OpenGL program.

    Add kernel32.lib, user32.lib, gdi32.lib, opengl32.lib, and glu32.lib.
    Last edited by psychopath; 11-18-2006 at 12:11 PM.
    Memorial University of Newfoundland
    Computer Science

    Mac and OpenGL evangelist.

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. Linker errors
    By jw232 in forum C++ Programming
    Replies: 3
    Last Post: 06-12-2009, 01:56 PM
  2. Linker errors in VC++ 2005
    By C+/- in forum C++ Programming
    Replies: 0
    Last Post: 05-18-2007, 08:42 AM
  3. Sneaky little linker errors...
    By Tozar in forum C++ Programming
    Replies: 8
    Last Post: 10-25-2006, 06:40 AM
  4. Linker errors when compiling
    By The Wazaa in forum C++ Programming
    Replies: 4
    Last Post: 10-07-2006, 01:55 PM
  5. Linker errors with Visual C++
    By codegirl in forum C++ Programming
    Replies: 4
    Last Post: 09-11-2003, 10:20 AM

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21