Get size of volume on Windows - winapi

I'm writing a library to extract information about physical disks, partitions, and volumes on a Windows system (XP or later).
I'm trying to get the capacity of a volume. Here are the approaches I know about and the reason each fails:
GetDiskFreeSpaceEx -- Affected by user quota.
IOCTL_DISK_GET_DRIVE_GEOMETRY_EX -- Gets size of entire physical disk, even when invoked using a volume handle.
IOCTL_VOLUME_GET_VOLUME_DISK_EXTENTS -- Doesn't account for RAID overhead.
IOCTL_DISK_GET_LENGTH_INFO -- Fails with access denied. (Actually, it requires GENERIC_READ access, unlike all other queries, and GENERIC_READ requires administrator access.)
IOCTL_STORAGE_READ_CAPACITY -- Not available on XP, also shares the drawbacks of IOCTL_DISK_GET_LENGTH_INFO and IOCTL_DISK_GET_DRIVE_GEOMETRY_EX
FSCTL_GET_VOLUME_BITMAP + GetFreeDiskSpace for cluster size -- Requires GENERIC_READ (admin access) and gives the size of the data area of the filesystem, not the entire volume.
IOCTL_DISK_GET_PARTITION_INFO -- Requires GENERIC_READ (admin access) and also failed on a USB-attached disk (possibly using superfloppy partitioning)
Oddly, the number of clusters from FSCTL_GET_VOLUME_BITMAP and WMI's CIM_LogicalDisk.Size property agree, and both are 4096 bytes smaller than the value from IOCTL_DISK_GET_LENGTH_INFO.
What is the correct way to get volume capacity? Since all the other queries work without administrator access, I'm looking for a least-privilege solution for this too.

What exactly do you want to get?
1) Physical Disk capacity
OR
2) capacity of the Partition on the Disk
OR
3) capacity of the File System on the Partition
There is PDO for Physical Disk, for it disk.sys creates and attaches FDO (\Device\Harddisk<I>\DR0 - name or \Device\Harddisk<I>\Partition0 - symbolick link, where I disk number in 0,1,2..)
for every Partition on Physical Disk disk.sys creates PDO (\Device\Harddisk<I>\Partition<J> - (J in {1,2,3..}) - symlink to some \Device\HarddiskVolume<X> )
1) there are several ways to get Physical Disk capacity:
a)
open any of \Device\Harddisk<I>\Partition<J> devices (J in {0,1,..} - so disk FDO or any partition PDO)
with (FILE_READ_ACCESS | FILE_WRITE_ACCESS) and send IOCTL_SCSI_PASS_THROUGH_DIRECT with SCSIOP_READ_CAPACITY and/or SCSIOP_READ_CAPACITY16 - and we got SCSIOP_READ_CAPACITY or SCSIOP_READ_CAPACITY16 struct.
READ_CAPACITY_DATA_EX rcd;
SCSI_PASS_THROUGH_DIRECT sptd = {
sizeof(sptd), 0, 0, 0, 0, CDB12GENERIC_LENGTH, 0, SCSI_IOCTL_DATA_IN,
sizeof(rcd), 1, &rcd, 0, {SCSIOP_READ_CAPACITY16}
};
if (0 <= NtDeviceIoControlFile(hFile, 0, 0, 0, &iosb, IOCTL_SCSI_PASS_THROUGH_DIRECT,
&sptd, sizeof(sptd), &sptd, sizeof(sptd)))
{
DbgPrint("---- SCSIOP_READ_CAPACITY16 ----\n");
rcd.BytesPerBlock = _byteswap_ulong(rcd.BytesPerBlock);
rcd.LogicalBlockAddress.QuadPart = _byteswap_uint64(rcd.LogicalBlockAddress.QuadPart) + 1;
DbgPrint("%I64x %x\n", rcd.LogicalBlockAddress, rcd.BytesPerBlock);
rcd.LogicalBlockAddress.QuadPart *= rcd.BytesPerBlock;
DbgPrint("%I64x %I64u\n", rcd.LogicalBlockAddress.QuadPart, rcd.LogicalBlockAddress.QuadPart);
}
or
READ_CAPACITY_DATA rcd;
SCSI_PASS_THROUGH_DIRECT sptd = {
sizeof(sptd), 0, 0, 0, 0, CDB10GENERIC_LENGTH, 0, SCSI_IOCTL_DATA_IN,
sizeof(rcd), 1, &rcd, 0, {SCSIOP_READ_CAPACITY}
};
if (0 <= NtDeviceIoControlFile(hFile, 0, 0, 0, &iosb, IOCTL_SCSI_PASS_THROUGH_DIRECT,
&sptd, sizeof(sptd), &sptd, sizeof(sptd)))
{
DbgPrint("---- SCSIOP_READ_CAPACITY ----\n");
rcd.BytesPerBlock = _byteswap_ulong(rcd.BytesPerBlock);
rcd.LogicalBlockAddress = _byteswap_ulong(rcd.LogicalBlockAddress) + 1;
DbgPrint("%x %x\n", rcd.LogicalBlockAddress, rcd.BytesPerBlock);
ULARGE_INTEGER u = {rcd.LogicalBlockAddress};
u.QuadPart *= rcd.BytesPerBlock;
DbgPrint("%I64x %I64u\n", u.QuadPart, u.QuadPart);
}
b)
open any of \Device\Harddisk<I>\Partition<J> devices with FILE_READ_ACCESS and send IOCTL_STORAGE_READ_CAPACITY - must be the same result as a) - this request handle ClassReadDriveCapacity in classpnp.sys wich internal send SCSI request (SCSIOP_READ_CAPACITY) to disk PDO. this way not worked on XP.
STORAGE_READ_CAPACITY sc;
if (0 <= NtDeviceIoControlFile(hFile, 0, 0, 0, &iosb, IOCTL_STORAGE_READ_CAPACITY, 0, 0, &sc, sizeof(sc)))
{
DbgPrint("---- IOCTL_STORAGE_READ_CAPACITY ----\n");
DbgPrint("%I64x %I64x %x \n", sc.DiskLength.QuadPart, sc.NumberOfBlocks.QuadPart, sc.BlockLength);
sc.NumberOfBlocks.QuadPart *= sc.BlockLength;
DbgPrint("%I64x %I64u\n", sc.NumberOfBlocks.QuadPart, sc.NumberOfBlocks.QuadPart);
}
c)
open any of \Device\Harddisk<I>\Partition<J> with any access and send IOCTL_DISK_GET_DRIVE_GEOMETRY_EX and use DISK_GEOMETRY_EX.DiskSize. this think the best way. not need any rights and work on XP
DISK_GEOMETRY_EX GeometryEx;
if (0 <= NtDeviceIoControlFile(hFile, 0, 0, 0, &iosb, IOCTL_DISK_GET_DRIVE_GEOMETRY_EX, 0, 0, &GeometryEx, sizeof(GeometryEx)))
{
DbgPrint("---- IOCTL_DISK_GET_DRIVE_GEOMETRY ----\n");
ULONG BytesPerCylinder = GeometryEx.Geometry.TracksPerCylinder * GeometryEx.Geometry.SectorsPerTrack * GeometryEx.Geometry.BytesPerSector;
DbgPrint("%I64x == %I64x\n", GeometryEx.Geometry.Cylinders.QuadPart, GeometryEx.DiskSize.QuadPart / BytesPerCylinder);
DbgPrint("%I64x <= %I64x\n", GeometryEx.Geometry.Cylinders.QuadPart * BytesPerCylinder, GeometryEx.DiskSize.QuadPart);
}
d)
open \Device\Harddisk<I>\Partition0 or \Device\Harddisk<I>\Dr0 with FILE_READ_ACCESS and use IOCTL_DISK_GET_LENGTH_INFO
2)
to get capacity of the Partition on the Disk - open \Device\Harddisk<I>\Partition<J> (where J in {1,2..} ) or if X letter assigned to partition - \GLOBAL??\X: and use IOCTL_DISK_GET_LENGTH_INFO. again need FILE_READ_ACCESS
GET_LENGTH_INFORMATION gli;
if (0 <= NtDeviceIoControlFile(hFile, 0, 0, 0, &iosb, IOCTL_DISK_GET_LENGTH_INFO, 0, 0, &gli, sizeof(gli)))
{
DbgPrint("---- IOCTL_DISK_GET_LENGTH_INFO ----\n");
DbgPrint("%I64x %I64u\n", gli.Length.QuadPart, gli.Length.QuadPart);
}
3)
to get capacity of the File System on the Partition - open any file (\GLOBAL??\X:\ for example) and use NtQueryVolumeInformationFile(FileFsSizeInformation)
FILE_FS_SIZE_INFORMATION fsi;
if (0 <= NtOpenFile(&hFile, SYNCHRONIZE, &oa, &iosb, FILE_SHARE_VALID_FLAGS, FILE_OPEN_FOR_FREE_SPACE_QUERY|FILE_SYNCHRONOUS_IO_NONALERT))
{
if (0 <= NtQueryVolumeInformationFile(hFile, &iosb, &fsi, sizeof(fsi), FileFsSizeInformation))
{
DbgPrint("%I64x %x %x\n", fsi.TotalAllocationUnits.QuadPart, fsi.SectorsPerAllocationUnit, fsi.BytesPerSector);
fsi.TotalAllocationUnits.QuadPart *= fsi.SectorsPerAllocationUnit * fsi.BytesPerSector;
DbgPrint("%I64x %I64u\n", fsi.TotalAllocationUnits.QuadPart, fsi.TotalAllocationUnits.QuadPart);
}
NtClose(hFile);
}
or use GetDiskFreeSpaceEx - internally it also calls NtQueryVolumeInformationFile( FileFsSizeInformation) but uses flag FILE_DIRECTORY_FILE, so as input parameter you can use only directories

Related

Why do WM_APPCOMMAND LPARAM have to be multiplied by 65536

I am trying to control the master volume. I am able to succesfully do that with this:
HWND mainhwnd = CreateWindow(szWindowClass, _T("window-noit-ext-profilist"), 0, 0, 0, 0, 0, HWND_MESSAGE, NULL, wcex.hInstance, NULL);
if (!mainhwnd) {
MessageBox(NULL, _T("Profilist: Call to CreateWindow failed!"), _T("window-noit-ext-profilist"), NULL);
return 1;
}
SendMessage(mainhwnd, WM_APPCOMMAND, (WPARAM)mainhwnd, (LPARAM)(APPCOMMAND_VOLUME_MUTE * 65536)); // mute
SendMessage(mainhwnd, WM_APPCOMMAND, (WPARAM)mainhwnd, (LPARAM)(APPCOMMAND_VOLUME_DOWN * 65536)); // vol down
SendMessage(mainhwnd, WM_APPCOMMAND, (WPARAM)mainhwnd, (LPARAM)(APPCOMMAND_VOLUME_UP * 65536)); // vol up
Why do I have to multiply by 65,536? The docs do not state this. IF I don't multiply, then it doesn't work.
For WM_APPCOMMAND, the lParam parameter packs three values in a single integer.
The lower 16bit word, dwKeys, indicates whether various virtual keys are down.
The higher 16bit word packs two fields: the highest 4 bits, uDevice, specifies the input device that is generating the input event. The lower 12 bits, cmd, contains the application command.
Multiplying by 65536 is same as bit shifting by 16 bits to the left (because 65536 = 0x10000 in hexadecimal). So, when you send the message with APPCOMMAND_VOLUME_UP * 65536, you are specifying the cmd is APPCOMMAND_VOLUME_UP, and the uDevice and dwKeys are both zero.

ioctl prototype in solaris libc

I had a program issue with the following stack.
6600: ora_d006_LOOKUP
ffffffff7addbbd0 __systemcall6 (3, ffffffff7d300440, 0, ffffffff7adc1268, d, fff7) + 24
ffffffff7adcba74 pthread_sigmask (2000, 0, 0, 0, ffffffff7d300200, d) + 1c4
00000001068ff3bc sslssalck (ffffffff7fffb138, 2, ffffffff7fffb070, 0, 3e8, 10c24d7e0) + 7c
00000001069358e8 sltmarm (a00029810, 29810, 10c3f3ab0, 3f9, a00000000, 29810) + 88
00000001069aa734 ltmdvp (8006689e, 3f9, 0, 10c55ba38, 10c3f8160, 10c3f34d0) + 154
00000001068ff2a4 sslsstehdlr (e, 0, ffffffff7fffb570, 7fffff84, 10c3ed0d8, 10c24d7e0) + 224
ffffffff7add7498 __sighndlr (e, 0, ffffffff7fffb570, 1068fcba0, 0, d) + c
ffffffff7adcb02c call_user_handler (ffffffff7d300200, ffffffff7d300200, ffffffff7fffb570, c, 0, 0) + 3e0
ffffffff7adcb238 sigacthandler (0, 0, ffffffff7fffb570, ffffffff7d300200, 0, ffffffff7af3e000) + 68
--- called from signal handler with signal 0 (SIGEXIT) ---
ffffffff7addad48 ioctl (10c3f80c0, bb8, 400, 10c426810, 10c6aae90, 2001420c) + c
0000000109e47668 nteveque (10c40c940, bb8, ffffffff7fffca98, 1afbfb85a4, 1c, 98) + 28
0000000109e3f0c0 ntevque (7, bb8, 10c2cbfd0, 10c40c940, ffffffff7fffca98, 10c2cbfd0) + 80
0000000109d8e738 nsevwait (0, 0, 10c25cc00, 0, 10c25cc04, 10c3f7a60) + 1b8
000000010092e7b4 ksnwait (10c25cc00, 6, 10c403fb0, 10c25c000, 10c25c, 10c000) + 54
000000010072060c ksliwat (0, ffffffff7fffd8e8, 1770, 10c25b, 10c000, 0) + 140c
0000000100704b28 kslwait (1770, ffffffff7fffd8e8, ffffffff7fffd8e8, ffffffff7fffd8e8, 0, 0) + e8
00000001065707a0 kmdmai (1b1bfffe00, 10c2628e8, 1b02faf258, 10c26c190, 10c25b, 38000d000) + e40
00000001063b0400 opirip (10a726000, 0, 380002, 380000, 38002a000, 38002a) + a80
00000001035c59cc opidrv (32, 4, ffffffff7ffff590, 1ebb90, ffffffff7af45050, ffffffff7ffff9a0) + 30c
000000010474117c sou2o (ffffffff7ffff568, 32, 4, ffffffff7ffff590, 10c000, 10b800) + 5c
0000000100604f64 opimai_real (3, ffffffff7ffff838, ffffffff7ffffb60, ffffffff7ffffbb5, 0, 0) + 204
0000000104757380 ssthrdmain (10c000, 3, 44dc00, 100604d60, 10c27c000, 10c27c) + 140
0000000100604c74 main (3, ffffffff7ffff948, 0, ffffffff7ffff840, ffffffff7ffff950, ffffffff7d300200) + 134
0000000100604b1c _start (0, 0, 0, 0, 0, 0) + 17c
this process is used to dispatch request from client. During the issue, no more request can be sent in and this process consumed many SYS cpu.
man ioctl, I will get the prototype of ioctl in system call. but I don't think it is same as the ioctl. The ioctl in the output of pstack should be a function in userland.
In the pstack:
--- called from signal handler with signal 0 (SIGEXIT) ---
ffffffff7addad48 ioctl (10c3f80c0, bb8, 400, 10c426810, 10c6aae90, 2001420c) + c
I wrote a small dtrace script.
pid$target::ioctl:entry
{
printf("%s", probemod)
}
I get
3 82218 ioctl:entry libc.so.1
so I think this ioctl came from libc.so.
But I can't get the manual for ioctl from libc.so.
1 where can I get the manual for ioctl in libc of solaris?
2 it is said that SIGEXIT is a pseudo signal. how to set up signal handle for this? how to sent SIGEXIT signal for a process? and at the last, we will have the following stack?
... my_handle_signal ....
--- called from signal handler with signal 0 (SIGEXIT) ---
... xxxx
Your ioctl on /devices/pseudo/poll#0:poll device (or /dev/pool) seems to be handled by kernel function from common/io/devpoll.c file (online copy - http://fxr.watson.org/fxr/source/common/io/devpoll.c?v=OPENSOLARIS)
More exact, by the dpioctl function:
692 dpioctl(dev_t dev, int cmd, intptr_t arg, int mode, cred_t *credp, int *rvalp)
zhihuifan, after checking your stacktrace I see that you program had executed:
main() -> ... nteveque() -> ioctl()
Then the signal hanlder was called.. I see no sending of signals from dpioctl, so I think the signal was send by some external function (or program or by user):
--- called from signal handler with signal 0 (SIGEXIT) ---
Then the user-space signal handler was called:
sigacthandler -> call_user_handler -> __sighndlr
-> sslsstehdlr
The sslsstehdlr did many actions, and according to my knowledge and POSIX standards ("2.4 Signal Concepts" from The Open Group Base Specifications Issue 6; IEEE Std 1003.1, 2004 Edition), the signal handler may only call (directly or indirectly) functions listed in table
The following table defines a set of functions that shall be either reentrant or non-interruptible by signals and shall be async-signal-safe. Therefore applications may invoke them, without restriction, from signal-catching functions:
... huge list but there is no ptherad_sigmask here...
All functions not in the above table are considered to be unsafe with respect to signals. .... when a signal interrupts an unsafe function and the signal-catching function calls an unsafe function, the behavior is undefined.

Using USBASP programmer for SPI communication

I'm trying to send some data from PC to ATmega328P chip through USBASP programmer.
It is able to transmit up to 4 bytes over SPI. These 4 bytes сan be set in USB Setup Packet (2 bytes for wValue and 2 bytes for wIndex). To enable SPI in ATmega328P I've connected USBASP Reset pin to SS. At PC side I'm using libusb to send USB Setup Packets.
ATmega328P code:
int main()
{
char spiData = 0;
// Enable SPI
SPCR |= 1 << SPE;
DDRB |= 1 << 4;
// Main cycle
while(1)
{
while(!(SPSR & (1 << SPIF))); // Wait for transmission end
spiData = SPDR; // Read SPI Data Register
// Do something with first byte
while(!(SPSR & (1 << SPIF)));
spiData = SPDR;
// Do something with second byte
while(!(SPSR & (1 << SPIF)));
spiData = SPDR;
// Do something with third byte
while(!(SPSR & (1 << SPIF)));
spiData = SPDR;
// Do something with fourth byte
}
return 0;
}
PC code (C#):
static void Main(string[] args)
{
// Find USBASP
var device = UsbDevice.OpenUsbDevice(new UsbDeviceFinder(0x16C0, 0x05DC));
// Set Clock and RESET pin to enable SPI
int bytesTrasferred;
var usbSetupPacket = new UsbSetupPacket(0xC0, 1, 0, 0, 0);
device.ControlTransfer(ref usbSetupPacket, null, 0, out bytesTrasferred);
// Send Setup Packets
while (Console.ReadKey(true).Key == ConsoleKey.Enter)
{
byte[] buffer = new byte[4];
usbSetupPacket = new UsbSetupPacket(0xC0, 3, 200, 200, 0);
device.ControlTransfer(ref usbSetupPacket, buffer, 4, out bytesTrasferred);
Console.WriteLine("Done. Return result: [{0}, {1}, {2}, {3}]", buffer[0], buffer[1], buffer[2], buffer[3]);
}
// Disable SPI
usbSetupPacket = new UsbSetupPacket(0xC0, 2, 0, 0, 0);
device.ControlTransfer(ref usbSetupPacket, null, 0, out bytesTrasferred);
// Free resources
device.Close();
UsbDevice.Exit();
}
USBASP -> ATmega328P SPI communication works well, but it seems that data in wValue and wIndex fields of Setup Packet comes corrupted to USBASP, because I'm getting this output (while it should be constant - [0, 200, 0, 200]):
[0, 153, 0, 128]
[0, 136, 0, 128]
[1, 209, 1, 217]
[1, 128, 0, 145]
[1, 153, 0, 128]
[0, 145, 1, 209]
[1, 217, 1, 136]
[0, 209, 1, 209]
[1, 217, 1, 136]
so on...
Also I see these numbers on LED digit display connected to ATmega328P.
Can anyone explain that?
P.S. For programming purposes this USBASP works well.
The problem was in SPI though. My ATmega328P was set by default to 8MHz internal clock with 1/8 divider, so it had 1MHz frequency which is too small for proper SPI communication. I fixed that by setting ATmega328P to external 16mHz crystal.
You can also set the data transfer rate to 750kb in libusb or if that program does not support changing transfer rates, use a program such as avrdude which can do that.

Cuda, why I cannot use more than one streaming processor?

I implemented a RNS Montgomery exponentiation in Cuda.
Everything nice everything fine. It runs on just one SM.
BUT, so far I focus on parallelization of just a single exp. What I want to do now is test with several exp on fly. That is, I want that the i-th next exp is assign to a free SM.
I tried, and the final time was always growing linearly, that is all the exp were assign to the same SM.
Then I switched to streams, but nothing changed.
However I have never used them, so maybe I am doing something wrong..
This is the code:
void __smeWrapper() {
cudaEvent_t start, stop;
cudaStream_t stream0, stream1, stream2;
float time;
unsigned int j, i, tmp;
cudaEventCreate(&start);
cudaEventCreate(&stop);
dim3 threadsPerBlock(SET_SIZE, (SET_SIZE+1)/2);
setCudaDevice();
s_transferDataToGPU();
if(cudaDeviceSetCacheConfig(cudaFuncCachePreferL1) != cudaSuccess)
printf("cudaDeviceSetCacheConfig ERROR!");
cudaEventRecord( start, 0 );
//for(i=0; i<EXPONENTIATION_NUMBER; i++) {
i=0;
__me<<< 1, threadsPerBlock, 0, stream0 >>>(&__s_x[i*(2*SET_SIZE + 1)], __B2modN, __bases, __mmi_NinB, __mmi_Bimodbi, __Bi_inAUar, __dbg, __NinAUar,
__mmi_BinAUar, __mmi_Ajmodaj, __Ajmodar, __mmi_Armodar, __AjinB, __minusAinB, &__z[i*(2*SET_SIZE + 1)], __e);
i=1;
__me<<< 1, threadsPerBlock, 0, stream1 >>>(&__s_x[i*(2*SET_SIZE + 1)], __B2modN, __bases, __mmi_NinB, __mmi_Bimodbi, __Bi_inAUar, __dbg, __NinAUar,
__mmi_BinAUar, __mmi_Ajmodaj, __Ajmodar, __mmi_Armodar, __AjinB, __minusAinB, &__z[i*(2*SET_SIZE + 1)], __e);
i=2;
__me<<< 1, threadsPerBlock, 0, stream2 >>>(&__s_x[i*(2*SET_SIZE + 1)], __B2modN, __bases, __mmi_NinB, __mmi_Bimodbi, __Bi_inAUar, __dbg, __NinAUar, __mmi_BinAUar,
__mmi_Ajmodaj, __Ajmodar, __mmi_Armodar, __AjinB, __minusAinB, &__z[i*(2*SET_SIZE + 1)], __e);
//printf("\n%s\n\n", cudaGetErrorString(cudaGetLastError()));
//}
cudaEventRecord( stop, 0 );
cudaEventSynchronize( stop );
cudaEventElapsedTime( &time, start, stop );
printf("GPU %f µs : %f ms\n", time*1000, time);
cudaEventDestroy( start );
cudaEventDestroy( stop );
Ubuntu 11.04 64b, Cuda 5 RC, 560 Ti (8 SM)
All threads from a block always run on a same SM. You need to start more then one block to use other SMs.
There seems to be something wrong with your streams - do you call cudaStreamCreate for every stream? On my system it crashes with SEGFAULT if I don't use one though.

What is the fastest way to check for duplicate digits of a number?

Let's say I want to check if a number n = 123 has duplicate digits. I tried:
#include <iostream>
using namespace std;
int main() {
int n = 123;
int d1 = n % 10;
int d2 = ( n / 10 ) % 10;
int d3 = ( n / 100 ) % 10;
if( d1 != d2 && d1 != d3 && d2 != d3 ) {
cout << n << " does not have duplicate digits.\n";
}
}
Is there any faster solution to this problem?
Update
Sorry for being unclear. The code above was written in C++ only for description purpose. I have to solve this problem in TI-89, with a number of 9 digits. And since the limitation of memory and speed, I'm looking for a fastest way possible.
TI-89 only has several condition keyword:
If
If ... Then
when(
For ... EndFor
While ... EndWhile
Loop ... EndLoop
Custom ... EndCustom
Thanks,
Chan
Not necessarily faster but you should measure anyway, just in case - my optimisation mantra is "measure, don't guess".
But I believe it's clearer in intent (and simple enough to be translated to a simpler calculator language. It's also able to handle arbitrarily sized integers.
int hasDupes (unsigned int n) {
// Flag to indicate digit has been used, all zero to start.
int used[10] = {0};
// More than 10 digits must have duplicates, return true quickly.
if (n > 9999999999) return 1;
// Process each digit in number.
while (n != 0) {
// If duplicate, return true as soon as found.
if (used[n%10]) return 1;
// Otherwise, mark used, go to next digit.
used[n%10] = 1;
n /= 10;
}
// No duplicates after checking all digits, return false.
return 0;
}
If you have a limited range of possibilities, you can use the time-honoured approach of sacrificing space for time. For example, let's say you're talking about numbers between 0 and 999 inclusive (the : : markers simply indicate data I've removed to keep the size of the answer manageable):
const int *hasDupes = {
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0 - 9
0, 1, 0, 0, 0, 0, 0, 0, 0, 0, // 10 - 19
0, 0, 1, 0, 0, 0, 0, 0, 0, 0, // 20 - 29
: :
0, 0, 1, 0, 0, 1, 0, 0, 0, 0, // 520 - 529
: :
0, 1, 0, 0, 0, 0, 0, 0, 1, 0, // 810 - 819
: :
0, 0, 0, 0, 0, 0, 0, 1, 0, 1, // 970 - 979
0, 0, 0, 0, 0, 0, 0, 0, 1, 1, // 980 - 989
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 990 - 999
};
and just do a table lookup of hasDupes[n]. The table itself could be generated (once) programmatically and then just inserted into your code for usage.
However, based on your edit where you state you need to handle nine-digit numbers, a billion-element array is probably not going to be possible on your calculator. I would therefore opt for the first solution.
template<class T, int radix = 10>
bool has_duplicate_digits(T n) {
int digits_mask = 0;
while (digits_mask |= (1 << (n % radix)), n /= radix)
if (digits_mask & (1 << (n % radix)))
return true;
return false;
}
Something like that should work as long as n is nonnegative and int has at least radix bits.
digits_mask is a bitset (bit 0 represents the occurrence of a 0 digit, bit 1 represents the occurrence of a 1 digit, etc.).
The bitmap is populated with the least significant digit of n, and the rest of the digits are shifted down. If there are more digits, and the new least significant digit is marked as having occurred previously, return true, otherwise repeat.
When there are no more digits, return false.
1 << x returns 1, 2, 4, 8, etc.: masks to use to test/set bits in the bitset.
a |= z is shorthand for a = a | z, which sets bits by the union of a from z.
a & z is the intersection of the bits in a and z, and is zero (false) if none are set and non-zero (true) if any are set.
I did a crash course in TI-89 basic to answer :)
Let's see if this works (I haven't an emulator, so can't check).
Test()
Prgm
{0,0,0,0,0,0,0,0,0,0}->A
Title "Request"
Request "Enter a number",B
EndDlog
Expr(B)->B
While B > 1
MOD(10,B)->C
if A[C+1] = 1 goto K
1->A[C+1]
B-C->B
EndWhile
Title "Done"
Text "Numbers non repeating"
Enddlog
goto J
Lbl K
Title "Done"
Text "Numbers repeating"
Enddlog
Lbl J
EndPrgm

Resources